diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
new file mode 100644
index 0000000..c9b89af
--- /dev/null
+++ b/.github/workflows/lint.yaml
@@ -0,0 +1,34 @@
+name: Lint
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - "**/*.go"
+ - "go.mod"
+ - "go.sum"
+ - "**/go.mod"
+ - "**/go.sum"
+ pull_request:
+ paths:
+ - "**/*.go"
+ - "go.mod"
+ - "go.sum"
+ - "**/go.mod"
+ - "**/go.sum"
+ merge_group:
+permissions:
+ contents: read
+jobs:
+ golangci:
+ name: golangci-lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-go@v5
+ with:
+ go-version: "1.21"
+ check-latest: true
+ - name: run linting
+ run: |
+ make lint
\ No newline at end of file
diff --git a/.golangci.yaml b/.golangci.yaml
new file mode 100644
index 0000000..20af873
--- /dev/null
+++ b/.golangci.yaml
@@ -0,0 +1,65 @@
+run:
+ timeout: 10m
+ tests: true
+
+linters:
+ disable-all: true
+ enable:
+ - asciicheck
+ - bidichk
+ - bodyclose
+ - decorder
+ - dupl
+ - dupword
+ - errcheck
+ - errchkjson
+ - errname
+ - exhaustive
+ - exportloopref
+ - forbidigo
+ - gci
+ - goconst
+ - gocritic
+ - gofmt
+ - gosec
+ - gosimple
+ - gosmopolitan
+ - govet
+ - grouper
+ - ineffassign
+ - loggercheck
+ - misspell
+ - nilerr
+ - nilnil
+ - noctx
+ - stylecheck
+ - testifylint
+ - thelper
+ - tparallel
+ - typecheck
+ - unconvert
+ - unparam
+ - unused
+ - usestdlibvars
+ - wastedassign
+ - whitespace
+
+linters-settings:
+ gci:
+ custom-order: true
+ sections:
+ - standard # Standard section: captures all standard packages.
+ - default # Default section: contains all imports that could not be matched to another section type.
+ - blank # blank imports
+ - dot # dot imports
+ - prefix(github.com/cometbft/cometbft)
+ - prefix(github.com/cosmos)
+ - prefix(github.com/cosmos/cosmos-sdk)
+ - prefix(cosmossdk.io)
+ - prefix(github.com/strangelove-ventures/noble-cctp-relayer)
+ gosec:
+ excludes:
+ - G404
+
+issues:
+ max-issues-per-linter: 0
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 371392e..28b78d6 100644
--- a/Makefile
+++ b/Makefile
@@ -17,20 +17,20 @@ GOBIN := $(GOPATH)/bin
###############################################################################
### Formatting & Linting ###
###############################################################################
-.PHONY: format lint
+.PHONY: lint lint-fix
-gofumpt_cmd=mvdan.cc/gofumpt
-golangci_lint_cmd=github.com/golangci/golangci-lint/cmd/golangci-lint
-
-format:
- @echo "🤖 Running formatter..."
- @go run $(gofumpt_cmd) -l -w .
- @echo "✅ Completed formatting!"
+golangci_lint_cmd=golangci-lint
+golangci_version=v1.57.2
lint:
- @echo "🤖 Running linter..."
- @go run $(golangci_lint_cmd) run --timeout=10m
- @echo "✅ Completed linting!"
+ @echo "--> Running linter"
+ @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
+ @$(golangci_lint_cmd) run ./... --timeout 15m
+
+lint-fix:
+ @echo "--> Running linter and fixing issues"
+ @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
+ @$(golangci_lint_cmd) run ./... --fix --timeout 15m
###############################################################################
diff --git a/README.md b/README.md
index 915fe7d..d4a1f7d 100644
--- a/README.md
+++ b/README.md
@@ -20,28 +20,55 @@ Sample configs can be found in [config](config).
### Flush Interval
-Using the `--flush-interval` flag will run a flush on all paths every `duration`; ex `--flush-interval 5m`
+Using the `--flush-interval` flag will run a flush on all chains every `duration`; ex `--flush-interval 5m`
-The relayer will keep track of the latest flushed block. The first time the flush is run, the flush will start at the chains latest height - lookback period and flush up until height of the chain when the flush started. It will then store the height the flush ended on.
+The first time the flush is run per chain, the flush will start at the chains `latest height - (2 * lookback period)`. The flush will always finish at the `latest chain height - lookback period`. This allows the flush to lag behind the chain so that the flush does not compete for transactions that are actively being processed. For subsequent flushes, each chain will reference its last flushed block, start from there and flush to the `latest chain height - lookback period` again. The flushing process will continue as long as the relayer is running.
-After that, it will flush from the last stored height - lookback period up until the latest height of the chain.
+For best results and coverage, the lookback period in blocks should correspond to the flush interval. If a chain produces 1 block a second and the flush interval is set to 30 minutes (1800 seconds), the lookback period should be at least 1800 blocks. When in doubt, round up and add a small buffer.
+
+#### Examples
+
+Consider a 30 minute flush interval (1800 seconds)
+- Ethereum: 12 second blocks = (1800 / 12) = `150 blocks`
+- Polygon: 2 second blocks = (1800 / 2) = `900 blocks`
+- Arbitrum: 0.26 second blocks = (1800 / 0.26) = `~6950 blocks`
+
+### Flush Only Mode
+
+This relayer also supports a `--flush-only-mode`. This mode will only flush the chain and not actively listen for new events as they occur. This is useful for running a secondary relayer which "lags" behind the primary relayer. It is only responsible for retrying failed transactions.
+
+When the relayer is in flush only mode, the flush mechanism will start at `latest height - (4 * lookback period)` and finish at `latest height - (3 * lookback period)`. For all subsequent flushes, the relayer will start at the last flushed block and finish at `latest height - (3 * lookback period)`. Please see the notes above for configuring the flush interval and lookback period.
+
+> Note: It is highly recommended to use the same configuration for both the primary and secondary relayer. This ensures that there is zero overlap between the relayers.
### Prometheus Metrics
By default, metrics are exported at on port :2112/metrics (`http://localhost:2112/metrics`). You can customize the port using the `--metrics-port` flag.
-| **Exported Metric** | **Description** | **Type** |
-|-------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|----------|
-| cctp_relayer_wallet_balance | Current balance of a relayer wallet in Wei.
Noble balances are not currently exported b/c `MsgReceiveMessage` is free to submit on Noble. | Gauge |
-| cctp_relayer_chain_latest_height | Current height of the chain. | Gauge |
-| cctp_relayer_broadcast_errors_total | The total number of failed broadcasts. Note: this is AFTER it retries `broadcast-retries` (config setting) number of times. | Counter |
+| **Exported Metric** | **Description** | **Type** |
+| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
+| cctp_relayer_wallet_balance | Current balance of a relayer wallet in Wei.
Noble balances are not currently exported b/c `MsgReceiveMessage` is free to submit on Noble. | Gauge |
+| cctp_relayer_chain_latest_height | Current height of the chain. | Gauge |
+| cctp_relayer_broadcast_errors_total | The total number of failed broadcasts. Note: this is AFTER it retries `broadcast-retries` (config setting) number of times. | Counter |
-### Noble Key
+### Minter Private Keys
+Minter private keys are required on a per chain basis to broadcast transactions to the target chain. These private keys can either be set in the `config.yaml` or via environment variables.
-The noble private key you input into the config must be hex encoded. The easiest way to get this is via a chain binary:
+#### Config Private Keys
-`nobled keys export --unarmored-hex --unsafe`
+Please see `./config/sample-config.yaml` for setting minter private keys in configuration. Please note that this method is insecure as the private keys are stored in plain text.
+#### Env Vars Private Keys
+
+To pass in a private key via an environment variable, first identify the chain's name. A chain's name corresponds to the key under the `chains` section in the `config.yaml`. The sample config lists these chain names for example: `noble`, `ethereum`, `optimism`, etc. Now, take the chain name in all caps and append `_PRIV_KEY`.
+
+An environment variable for `noble` would look like: `NOBLE_PRIV_KEY=`
+
+#### Noble Private Key Format
+
+The noble private key you input into the config or via enviroment variables must be hex encoded. The easiest way to get this is via a chain binary:
+
+`nobled keys export --unarmored-hex --unsafe`
### API
Simple API to query message state cache
@@ -54,14 +81,14 @@ localhost:8000/tx/?domain=0
### State
-| IrisLookupId | Status | SourceDomain | DestDomain | SourceTxHash | DestTxHash | MsgSentBytes | Created | Updated |
-|:-------------|:---------|:-------------|:-----------|:--------------|:-----------|:-------------|:--------|:--------|
-| 0x123 | Created | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
-| 0x123 | Pending | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
-| 0x123 | Attested | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
-| 0x123 | Complete | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
-| 0x123 | Failed | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
-| 0x123 | Filtered | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
+| IrisLookupId | Status | SourceDomain | DestDomain | SourceTxHash | DestTxHash | MsgSentBytes | Created | Updated |
+| :----------- | :------- | :----------- | :--------- | :----------- | :--------- | :----------- | :------ | :------ |
+| 0x123 | Created | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
+| 0x123 | Pending | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
+| 0x123 | Attested | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
+| 0x123 | Complete | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
+| 0x123 | Failed | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
+| 0x123 | Filtered | 0 | 4 | 0x123 | ABC123 | bytes... | date | date |
### Generating Go ABI bindings
diff --git a/circle/attestation.go b/circle/attestation.go
index ca069fc..b866076 100644
--- a/circle/attestation.go
+++ b/circle/attestation.go
@@ -1,6 +1,7 @@
package circle
import (
+ "context"
"encoding/json"
"fmt"
"io"
@@ -8,24 +9,46 @@ import (
"time"
"cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
// CheckAttestation checks the iris api for attestation status and returns true if attestation is complete
-func CheckAttestation(attestationURL string, logger log.Logger, irisLookupId string, txHash string, sourceDomain, destDomain types.Domain) *types.AttestationResponse {
- logger.Debug(fmt.Sprintf("Checking attestation for %s%s%s for source tx %s from %d to %d", attestationURL, "0x", irisLookupId, txHash, sourceDomain, destDomain))
+func CheckAttestation(attestationURL string, logger log.Logger, irisLookupID string, txHash string, sourceDomain, destDomain types.Domain) *types.AttestationResponse {
+ // append ending / if not present
+ if attestationURL[len(attestationURL)-1:] != "/" {
+ attestationURL += "/"
+ }
+
+ // add 0x prefix if not present
+ if len(irisLookupID) > 2 && irisLookupID[:2] != "0x" {
+ irisLookupID = "0x" + irisLookupID
+ }
+
+ logger.Debug(fmt.Sprintf("Checking attestation for %s%s for source tx %s from %d to %d", attestationURL, irisLookupID, txHash, sourceDomain, destDomain))
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
- client := http.Client{Timeout: 2 * time.Second}
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, attestationURL+irisLookupID, nil)
+ if err != nil {
+ logger.Debug("error creating request: " + err.Error())
+ return nil
+ }
- rawResponse, err := client.Get(attestationURL + "0x" + irisLookupId)
+ client := http.Client{}
+ rawResponse, err := client.Do(req)
if err != nil {
logger.Debug("error during request: " + err.Error())
return nil
}
+
+ defer rawResponse.Body.Close()
if rawResponse.StatusCode != http.StatusOK {
logger.Debug("non 200 response received from Circles attestation API")
return nil
}
+
body, err := io.ReadAll(rawResponse.Body)
if err != nil {
logger.Debug("unable to parse message body")
@@ -38,7 +61,8 @@ func CheckAttestation(attestationURL string, logger log.Logger, irisLookupId str
logger.Debug("unable to unmarshal response")
return nil
}
- logger.Info(fmt.Sprintf("Attestation found for %s%s%s", attestationURL, "0x", irisLookupId))
+
+ logger.Info(fmt.Sprintf("Attestation found for %s%s", attestationURL, irisLookupID))
return &response
}
diff --git a/circle/attestation_test.go b/circle/attestation_test.go
index a2fa252..8f19199 100644
--- a/circle/attestation_test.go
+++ b/circle/attestation_test.go
@@ -4,28 +4,47 @@ import (
"os"
"testing"
- "cosmossdk.io/log"
"github.com/rs/zerolog"
+ "github.com/stretchr/testify/require"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/circle"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
var cfg types.Config
var logger log.Logger
func init() {
- cfg.Circle.AttestationBaseUrl = "https://iris-api-sandbox.circle.com/attestations/"
+ cfg.Circle.AttestationBaseURL = "https://iris-api-sandbox.circle.com/attestations/"
logger = log.NewLogger(os.Stdout, log.LevelOption(zerolog.ErrorLevel))
}
func TestAttestationIsReady(t *testing.T) {
- resp := circle.CheckAttestation(cfg.Circle.AttestationBaseUrl, logger, "85bbf7e65a5992e6317a61f005e06d9972a033d71b514be183b179e1b47723fe", "", 0, 4)
+ resp := circle.CheckAttestation(cfg.Circle.AttestationBaseURL, logger, "85bbf7e65a5992e6317a61f005e06d9972a033d71b514be183b179e1b47723fe", "", 0, 4)
require.NotNil(t, resp)
require.Equal(t, "complete", resp.Status)
}
func TestAttestationNotFound(t *testing.T) {
- resp := circle.CheckAttestation(cfg.Circle.AttestationBaseUrl, logger, "not an attestation", "", 0, 4)
+ resp := circle.CheckAttestation(cfg.Circle.AttestationBaseURL, logger, "not an attestation", "", 0, 4)
require.Nil(t, resp)
}
+
+func TestAttestationWithoutEndingSlash(t *testing.T) {
+ startURL := cfg.Circle.AttestationBaseURL
+ cfg.Circle.AttestationBaseURL = startURL[:len(startURL)-1]
+
+ resp := circle.CheckAttestation(cfg.Circle.AttestationBaseURL, logger, "85bbf7e65a5992e6317a61f005e06d9972a033d71b514be183b179e1b47723fe", "", 0, 4)
+ require.NotNil(t, resp)
+ require.Equal(t, "complete", resp.Status)
+
+ cfg.Circle.AttestationBaseURL = startURL
+}
+
+func TestAttestationWithLeading0x(t *testing.T) {
+ resp := circle.CheckAttestation(cfg.Circle.AttestationBaseURL, logger, "0x85bbf7e65a5992e6317a61f005e06d9972a033d71b514be183b179e1b47723fe", "", 0, 4)
+ require.NotNil(t, resp)
+ require.Equal(t, "complete", resp.Status)
+}
diff --git a/cmd/appstate.go b/cmd/appstate.go
index b41f079..4e290bf 100644
--- a/cmd/appstate.go
+++ b/cmd/appstate.go
@@ -1,13 +1,20 @@
package cmd
import (
+ "fmt"
"os"
- "cosmossdk.io/log"
"github.com/rs/zerolog"
+
+ "cosmossdk.io/log"
+
+ "github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
+ "github.com/strangelove-ventures/noble-cctp-relayer/noble"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
+const nobleChainName = "noble"
+
// appState is the modifiable state of the application.
type AppState struct {
Config *types.Config
@@ -68,18 +75,131 @@ func (a *AppState) loadConfigFile() {
}
a.Logger.Info("Successfully parsed config file", "location", a.ConfigPath)
a.Config = config
- a.validateConfig()
+
+ err = a.validateConfig()
+ if err != nil {
+ a.Logger.Error("Invalid config", "err", err)
+ os.Exit(1)
+ }
}
// validateConfig checks the AppState Config for any invalid settings.
-func (a *AppState) validateConfig() {
- if a.Config.Circle.AttestationBaseUrl == "" {
- a.Logger.Error("AttestationBaseUrl is required in the config")
- os.Exit(1)
+func (a *AppState) validateConfig() error {
+ // validate chains
+ for name, cfg := range a.Config.Chains {
+ // check if chain is noble
+ if name == nobleChainName {
+ // validate noble chain
+ cc := cfg.(*noble.ChainConfig)
+ err := a.validateChain(
+ name,
+ cc.ChainID,
+ "",
+ cc.RPC,
+ "",
+ cc.BroadcastRetries,
+ cc.BroadcastRetryInterval,
+ cc.MinMintAmount,
+ )
+ if err != nil {
+ return err
+ }
+ } else {
+ // validate eth based chains
+ cc := cfg.(*ethereum.ChainConfig)
+ err := a.validateChain(
+ name,
+ fmt.Sprintf("%d", cc.ChainID),
+ fmt.Sprintf("%d", cc.Domain),
+ cc.RPC,
+ cc.WS,
+ cc.BroadcastRetries,
+ cc.BroadcastRetryInterval,
+ cc.MinMintAmount,
+ )
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ // ensure at least 1 enabled route
+ if len(a.Config.EnabledRoutes) == 0 {
+ return fmt.Errorf("at least one route must be enabled in the config")
+ }
+
+ // validate circle api config
+ err := a.validateCircleConfig()
+ if err != nil {
+ return err
+ }
+
+ // validate processor worker count
+ if a.Config.ProcessorWorkerCount == 0 {
+ return fmt.Errorf("ProcessorWorkerCount must be greater than zero in the config")
+ }
+
+ return nil
+}
+
+// validateChain ensures the chain is configured correctly
+func (a *AppState) validateChain(
+ name string,
+ chainID string,
+ domain string,
+ rpcURL string,
+ wsURL string,
+ broadcastRetries int,
+ broadcastRetryInterval int,
+ minMintAmount uint64,
+) error {
+ if name == "" {
+ return fmt.Errorf("chain name must be set in the config")
+ }
+
+ if chainID == "" {
+ return fmt.Errorf("chainID must be set in the config (chain: %s) (chainID: %s)", name, chainID)
+ }
+
+ // domain is hardcoded to 4 for noble chain
+ if domain == "" && name != nobleChainName {
+ return fmt.Errorf("domain must be set in the config (chain: %s) (domain: %s)", name, domain)
+ }
+
+ if rpcURL == "" {
+ return fmt.Errorf("rpcURL must be set in the config (chain: %s) (rpcURL: %s)", name, rpcURL)
+ }
+
+ // we do not use a websocket for noble
+ if wsURL == "" && name != nobleChainName {
+ return fmt.Errorf("wsURL must be set in the config (chain: %s) (wsURL: %s)", name, wsURL)
+ }
+
+ if broadcastRetries <= 0 {
+ return fmt.Errorf("broadcastRetries must be greater than zero in the config (chain: %s) (broadcastRetries: %d)", name, broadcastRetries)
+ }
+
+ if broadcastRetryInterval <= 0 {
+ return fmt.Errorf("broadcastRetryInterval must be greater than zero in the config (chain: %s) (broadcastRetryInterval: %d)", name, broadcastRetryInterval)
+ }
+
+ // noble has free minting
+ if minMintAmount == 0 && name != nobleChainName {
+ return fmt.Errorf("ETH-based chains must have a minMintAmount greater than zero in the config (chain: %s) (minMintAmount: %d)", name, minMintAmount)
+ }
+
+ return nil
+}
+
+// validateCircleConfig ensures the circle api is configured correctly
+func (a *AppState) validateCircleConfig() error {
+ if a.Config.Circle.AttestationBaseURL == "" {
+ return fmt.Errorf("AttestationBaseUrl is required in the config")
}
if a.Config.Circle.FetchRetryInterval == 0 {
- a.Logger.Error("FetchRetryInterval must be greater than zero in the config")
- os.Exit(1)
+ return fmt.Errorf("FetchRetryInterval must be greater than zero in the config")
}
+
+ return nil
}
diff --git a/cmd/config.go b/cmd/config.go
index 3f878ee..e65df88 100644
--- a/cmd/config.go
+++ b/cmd/config.go
@@ -7,10 +7,11 @@ import (
"strings"
"github.com/spf13/cobra"
+ "gopkg.in/yaml.v2"
+
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "gopkg.in/yaml.v2"
)
// Command for printing current configuration
@@ -26,7 +27,6 @@ func configShowCmd(a *AppState) *cobra.Command {
$ %s show-config --config %s
$ %s sc`, appName, defaultConfigPath, appName)),
RunE: func(cmd *cobra.Command, args []string) error {
-
jsn, err := cmd.Flags().GetBool(flagJSON)
if err != nil {
return err
@@ -50,8 +50,7 @@ $ %s sc`, appName, defaultConfigPath, appName)),
}
},
}
- addJsonFlag(cmd)
- return cmd
+ return addJSONFlag(cmd)
}
// ParseConfig parses the app config file
@@ -70,7 +69,7 @@ func ParseConfig(file string) (*types.Config, error) {
EnabledRoutes: cfg.EnabledRoutes,
Circle: cfg.Circle,
ProcessorWorkerCount: cfg.ProcessorWorkerCount,
- Api: cfg.Api,
+ API: cfg.API,
Chains: make(map[string]types.ChainConfig),
}
diff --git a/cmd/config_test.go b/cmd/config_test.go
index ab714e2..5cea3b6 100644
--- a/cmd/config_test.go
+++ b/cmd/config_test.go
@@ -3,10 +3,11 @@ package cmd_test
import (
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cmd"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
- "github.com/stretchr/testify/require"
)
func TestConfig(t *testing.T) {
diff --git a/cmd/flags.go b/cmd/flags.go
index 5bc3cbc..7cac21f 100644
--- a/cmd/flags.go
+++ b/cmd/flags.go
@@ -13,6 +13,7 @@ const (
flagJSON = "json"
flagMetricsPort = "metrics-port"
flagFlushInterval = "flush-interval"
+ flagFlushOnlyMode = "flush-only-mode"
)
func addAppPersistantFlags(cmd *cobra.Command, a *AppState) *cobra.Command {
@@ -21,11 +22,11 @@ func addAppPersistantFlags(cmd *cobra.Command, a *AppState) *cobra.Command {
cmd.PersistentFlags().StringVar(&a.LogLevel, flagLogLevel, "info", "log level (debug, info, warn, error)")
cmd.PersistentFlags().Int16P(flagMetricsPort, "p", 2112, "customize Prometheus metrics port")
cmd.PersistentFlags().DurationP(flagFlushInterval, "i", 0, "how frequently should a flush routine be run")
+ cmd.PersistentFlags().BoolP(flagFlushOnlyMode, "f", false, "only run the background flush routine (acts as a redundant relayer)")
return cmd
-
}
-func addJsonFlag(cmd *cobra.Command) *cobra.Command {
+func addJSONFlag(cmd *cobra.Command) *cobra.Command {
cmd.Flags().Bool(flagJSON, false, "return in json format")
return cmd
}
diff --git a/cmd/process.go b/cmd/process.go
index ae7a3f9..b9665d0 100644
--- a/cmd/process.go
+++ b/cmd/process.go
@@ -8,11 +8,13 @@ import (
"strconv"
"time"
- "cosmossdk.io/log"
- "cosmossdk.io/math"
cctptypes "github.com/circlefin/noble-cctp/x/cctp/types"
"github.com/gin-gonic/gin"
"github.com/spf13/cobra"
+
+ "cosmossdk.io/log"
+ "cosmossdk.io/math"
+
"github.com/strangelove-ventures/noble-cctp-relayer/circle"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
@@ -36,12 +38,30 @@ func Start(a *AppState) *cobra.Command {
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
a.InitAppState()
},
- Run: func(cmd *cobra.Command, args []string) {
-
+ RunE: func(cmd *cobra.Command, args []string) error {
logger := a.Logger
cfg := a.Config
- go startApi(a)
+ flushInterval, err := cmd.Flags().GetDuration(flagFlushInterval)
+ if err != nil {
+ logger.Error("Invalid flush interval", "error", err)
+ }
+
+ flushOnly, err := cmd.Flags().GetBool(flagFlushOnlyMode)
+ if err != nil {
+ return fmt.Errorf("invalid flush only flag error=%w", err)
+ }
+
+ if flushInterval == 0 {
+ if flushOnly {
+ return fmt.Errorf("flush only mode requires a flush interval")
+ } else {
+ logger.Error("Flush interval not set. Use the --flush-interval flag to set a reoccurring flush")
+ }
+ }
+
+ // start API on normal relayer only
+ go startAPI(a)
// messageState processing queue
var processingQueue = make(chan *types.TxState, 10000)
@@ -50,16 +70,7 @@ func Start(a *AppState) *cobra.Command {
port, err := cmd.Flags().GetInt16(flagMetricsPort)
if err != nil {
- logger.Error("Invalid port", "error", err)
- os.Exit(1)
- }
-
- flushInterval, err := cmd.Flags().GetDuration(flagFlushInterval)
- if err != nil {
- logger.Error("Invalid flush interval", "error", err)
- }
- if flushInterval == 0 {
- logger.Info("Flush interval not set. Use the --flush-interval flag to set a reoccurring flush")
+ return fmt.Errorf("invalid port error=%w", err)
}
metrics := relayer.InitPromMetrics(port)
@@ -67,15 +78,13 @@ func Start(a *AppState) *cobra.Command {
for name, cfg := range cfg.Chains {
c, err := cfg.Chain(name)
if err != nil {
- logger.Error("Error creating chain", "err: ", err)
- os.Exit(1)
+ return fmt.Errorf("error creating chain error=%w", err)
}
logger = logger.With("name", c.Name(), "domain", c.Domain())
if err := c.InitializeClients(cmd.Context(), logger); err != nil {
- logger.Error("Error initializing client", "err", err)
- os.Exit(1)
+ return fmt.Errorf("error initializing client error=%w", err)
}
go c.TrackLatestBlockHeight(cmd.Context(), logger, metrics)
@@ -89,22 +98,20 @@ func Start(a *AppState) *cobra.Command {
break
}
if i == maxRetries-1 {
- logger.Error("Unable to get height")
- os.Exit(1)
+ return fmt.Errorf("unable to get height")
}
}
if err := c.InitializeBroadcaster(cmd.Context(), logger, sequenceMap); err != nil {
- logger.Error("Error initializing broadcaster", "error", err)
- os.Exit(1)
+ return fmt.Errorf("error initializing broadcaster error=%w", err)
}
- go c.StartListener(cmd.Context(), logger, processingQueue, flushInterval)
+ go c.StartListener(cmd.Context(), logger, processingQueue, flushOnly, flushInterval)
+
go c.WalletBalanceMetric(cmd.Context(), a.Logger, metrics)
if _, ok := registeredDomains[c.Domain()]; ok {
- logger.Error("Duplicate domain found", "domain", c.Domain(), "name:", c.Name())
- os.Exit(1)
+ return fmt.Errorf("duplicate domain found domain=%d name=%s", c.Domain(), c.Name())
}
registeredDomains[c.Domain()] = c
@@ -115,14 +122,19 @@ func Start(a *AppState) *cobra.Command {
go StartProcessor(cmd.Context(), a, registeredDomains, processingQueue, sequenceMap, metrics)
}
- defer func() {
- for _, c := range registeredDomains {
- fmt.Printf("\n%s: latest-block: %d last-flushed-block: %d", c.Name(), c.LatestBlock(), c.LastFlushedBlock())
- c.CloseClients()
+ // wait for context to be done
+ <-cmd.Context().Done()
+
+ // close clients & output latest block heights
+ for _, c := range registeredDomains {
+ logger.Info(fmt.Sprintf("%s: latest-block: %d last-flushed-block: %d", c.Name(), c.LatestBlock(), c.LastFlushedBlock()))
+ err := c.CloseClients()
+ if err != nil {
+ logger.Error("Error closing clients", "error", err)
}
- }()
+ }
- <-cmd.Context().Done()
+ return nil
},
}
@@ -157,7 +169,6 @@ func StartProcessor(
var broadcastMsgs = make(map[types.Domain][]*types.MessageState)
var requeue bool
for _, msg := range tx.Msgs {
-
// if a filter's condition is met, mark as filtered
if FilterDisabledCCTPRoutes(cfg, logger, msg) ||
filterInvalidDestinationCallers(registeredDomains, logger, msg) ||
@@ -169,32 +180,35 @@ func StartProcessor(
// if the message is burned or pending, check for an attestation
if msg.Status == types.Created || msg.Status == types.Pending {
- response := circle.CheckAttestation(cfg.Circle.AttestationBaseUrl, logger, msg.IrisLookupId, msg.SourceTxHash, msg.SourceDomain, msg.DestDomain)
+ response := circle.CheckAttestation(cfg.Circle.AttestationBaseURL, logger, msg.IrisLookupID, msg.SourceTxHash, msg.SourceDomain, msg.DestDomain)
- if response == nil {
- logger.Debug("Attestation is still processing for 0x" + msg.IrisLookupId + ". Retrying...")
+ switch {
+ case response == nil:
+ logger.Debug("Attestation is still processing for 0x" + msg.IrisLookupID + ". Retrying...")
requeue = true
continue
- } else if msg.Status == types.Created && response.Status == "pending_confirmations" {
- logger.Debug("Attestation is created but still pending confirmations for 0x" + msg.IrisLookupId + ". Retrying...")
+ case msg.Status == types.Created && response.Status == "pending_confirmations":
+ logger.Debug("Attestation is created but still pending confirmations for 0x" + msg.IrisLookupID + ". Retrying...")
State.Mu.Lock()
msg.Status = types.Pending
msg.Updated = time.Now()
State.Mu.Unlock()
requeue = true
continue
- } else if response.Status == "pending_confirmations" {
- logger.Debug("Attestation is still pending for 0x" + msg.IrisLookupId + ". Retrying...")
+ case response.Status == "pending_confirmations":
+ logger.Debug("Attestation is still pending for 0x" + msg.IrisLookupID + ". Retrying...")
requeue = true
continue
- } else if response.Status == "complete" {
- logger.Debug("Attestation is complete for 0x" + msg.IrisLookupId + ".")
+ case response.Status == "complete":
+ logger.Debug("Attestation is complete for 0x" + msg.IrisLookupID + ".")
State.Mu.Lock()
msg.Status = types.Attested
msg.Attestation = response.Attestation
msg.Updated = time.Now()
broadcastMsgs[msg.DestDomain] = append(broadcastMsgs[msg.DestDomain], msg)
State.Mu.Unlock()
+ default:
+ logger.Error("Attestation failed for unknown reason for 0x" + msg.IrisLookupID + ". Status: " + response.Status)
}
}
}
@@ -250,7 +264,6 @@ func FilterDisabledCCTPRoutes(cfg *types.Config, logger log.Logger, msg *types.M
logger.Info(fmt.Sprintf("Filtered tx %s because relaying from %d to %d is not enabled",
msg.SourceTxHash, msg.SourceDomain, msg.DestDomain))
return true
-
}
// filterInvalidDestinationCallers returns true if the minter is not the destination caller for the specified domain
@@ -317,20 +330,24 @@ func filterLowTransfers(cfg *types.Config, logger log.Logger, msg *types.Message
return false
}
-func startApi(a *AppState) {
+func startAPI(a *AppState) {
logger := a.Logger
cfg := a.Config
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
- err := router.SetTrustedProxies(cfg.Api.TrustedProxies) // vpn.primary.strange.love
+ err := router.SetTrustedProxies(cfg.API.TrustedProxies) // vpn.primary.strange.love
if err != nil {
logger.Error("Unable to set trusted proxies on API server: " + err.Error())
os.Exit(1)
}
router.GET("/tx/:txHash", getTxByHash)
- router.Run("localhost:8000")
+ err = router.Run("localhost:8000")
+ if err != nil {
+ logger.Error("Unable to start API server: " + err.Error())
+ os.Exit(1)
+ }
}
func getTxByHash(c *gin.Context) {
diff --git a/cmd/process_test.go b/cmd/process_test.go
index 307d16f..743a3f5 100644
--- a/cmd/process_test.go
+++ b/cmd/process_test.go
@@ -6,12 +6,14 @@ import (
"testing"
"time"
- "cosmossdk.io/log"
"github.com/rs/zerolog"
+ "github.com/stretchr/testify/require"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cmd"
testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
// var a *cmd.AppState
@@ -64,10 +66,10 @@ func TestProcessDisabledCctpRoute(t *testing.T) {
Msgs: []*types.MessageState{
{
SourceTxHash: "123",
- IrisLookupId: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
+ IrisLookupID: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
Status: types.Created,
SourceDomain: 0,
- DestDomain: 5, //not configured
+ DestDomain: 5, // not configured
DestinationCaller: emptyBz,
},
},
@@ -99,7 +101,7 @@ func TestProcessInvalidDestinationCaller(t *testing.T) {
Msgs: []*types.MessageState{
{
SourceTxHash: "123",
- IrisLookupId: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
+ IrisLookupID: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
Status: types.Created,
SourceDomain: 0,
DestDomain: 4,
@@ -119,7 +121,6 @@ func TestProcessInvalidDestinationCaller(t *testing.T) {
// we want to filter out the transaction if the route is not enabled
func TestFilterDisabledCCTPRoutes(t *testing.T) {
-
logger := log.NewLogger(os.Stdout, log.LevelOption(zerolog.DebugLevel))
var msgState types.MessageState
@@ -153,5 +154,4 @@ func TestFilterDisabledCCTPRoutes(t *testing.T) {
}
filterTx = cmd.FilterDisabledCCTPRoutes(&cfg, logger, &msgState)
require.True(t, filterTx)
-
}
diff --git a/cmd/version.go b/cmd/version.go
index 2dbc4fd..5269cb5 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -34,7 +34,6 @@ $ %s v`,
appName, appName,
)),
RunE: func(cmd *cobra.Command, args []string) error {
-
jsn, err := cmd.Flags().GetBool(flagJSON)
if err != nil {
return err
@@ -62,6 +61,5 @@ $ %s v`,
return err
},
}
- addJsonFlag(versionCmd)
- return versionCmd
+ return addJSONFlag(versionCmd)
}
diff --git a/cosmos/codec.go b/cosmos/codec.go
index 6ac6f0d..6f92cf3 100644
--- a/cosmos/codec.go
+++ b/cosmos/codec.go
@@ -2,6 +2,7 @@ package cosmos
import (
"github.com/circlefin/noble-cctp/x/cctp"
+
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
@@ -9,8 +10,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
- // authz "github.com/cosmos/cosmos-sdk/x/authz/module"
- //"github.com/cosmos/cosmos-sdk/x/bank"
)
var ModuleBasics = []module.AppModuleBasic{
diff --git a/cosmos/cosmosprovider.go b/cosmos/cosmosprovider.go
index 39495a7..9693926 100644
--- a/cosmos/cosmosprovider.go
+++ b/cosmos/cosmosprovider.go
@@ -3,13 +3,14 @@ package cosmos
import (
"time"
+ "google.golang.org/grpc/encoding"
+ "google.golang.org/grpc/encoding/proto"
+
rpcclient "github.com/cometbft/cometbft/rpc/client"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
libclient "github.com/cometbft/cometbft/rpc/jsonrpc/client"
- gogogrpc "github.com/cosmos/gogoproto/grpc"
- "google.golang.org/grpc/encoding"
- "google.golang.org/grpc/encoding/proto"
+ gogogrpc "github.com/cosmos/gogoproto/grpc"
)
var _ gogogrpc.ClientConn = &CosmosProvider{}
diff --git a/cosmos/grpc_shim.go b/cosmos/grpc_shim.go
index 008985a..d4563f9 100644
--- a/cosmos/grpc_shim.go
+++ b/cosmos/grpc_shim.go
@@ -6,14 +6,16 @@ import (
"reflect"
"strconv"
- abci "github.com/cometbft/cometbft/abci/types"
- "github.com/cosmos/cosmos-sdk/codec/types"
- sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
- grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
+
+ abci "github.com/cometbft/cometbft/abci/types"
+
+ "github.com/cosmos/cosmos-sdk/codec/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
)
// Invoke implements the grpc ClientConn.Invoke method
@@ -86,7 +88,6 @@ func (cc *CosmosProvider) runGRPCQuery(ctx context.Context, method string, req i
return abci.ResponseQuery{}, nil, sdkerrors.ErrInvalidRequest.Wrapf(
"client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader)
}
-
}
height, err := heightFromMetadata(md)
diff --git a/cosmos/query.go b/cosmos/query.go
index 448d728..7d25c20 100644
--- a/cosmos/query.go
+++ b/cosmos/query.go
@@ -5,9 +5,11 @@ import (
"fmt"
cctptypes "github.com/circlefin/noble-cctp/x/cctp/types"
+
abci "github.com/cometbft/cometbft/abci/types"
rpcclient "github.com/cometbft/cometbft/rpc/client"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
+
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
diff --git a/cosmos/query_test.go b/cosmos/query_test.go
index 0ecd20f..3d89629 100644
--- a/cosmos/query_test.go
+++ b/cosmos/query_test.go
@@ -4,8 +4,9 @@ import (
"context"
"testing"
- "github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
"github.com/stretchr/testify/require"
+
+ "github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
)
func TestUsedNonce(t *testing.T) {
diff --git a/ethereum/broadcast.go b/ethereum/broadcast.go
index 052ab66..91372b4 100644
--- a/ethereum/broadcast.go
+++ b/ethereum/broadcast.go
@@ -10,10 +10,12 @@ import (
"strconv"
"time"
- "cosmossdk.io/log"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum/contracts"
"github.com/strangelove-ventures/noble-cctp-relayer/relayer"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
@@ -40,7 +42,6 @@ func (e *Ethereum) Broadcast(
sequenceMap *types.SequenceMap,
m *relayer.PromMetrics,
) error {
-
logger = logger.With("chain", e.name, "chain_id", e.chainID, "domain", e.domain)
backend := NewContractBackendWrapper(e.rpcClient)
@@ -144,14 +145,12 @@ func (e *Ethereum) attemptBroadcast(
response, nonceErr := messageTransmitter.UsedNonces(co, [32]byte(crypto.Keccak256(key)))
if nonceErr != nil {
logger.Debug("Error querying whether nonce was used. Continuing...", "error:", nonceErr)
- } else {
- if response.Uint64() == uint64(1) {
- // nonce has already been used, mark as complete
- logger.Debug(fmt.Sprintf("This source domain/nonce has already been used: %d %d",
- msg.SourceDomain, msg.Nonce), "src-tx", msg.SourceTxHash, "reviever")
- msg.Status = types.Complete
- return nil
- }
+ } else if response.Uint64() == uint64(1) {
+ // nonce has already been used, mark as complete
+ logger.Debug(fmt.Sprintf("This source domain/nonce has already been used: %d %d",
+ msg.SourceDomain, msg.Nonce), "src-tx", msg.SourceTxHash, "reviever")
+ msg.Status = types.Complete
+ return nil
}
// broadcast txn
@@ -171,7 +170,7 @@ func (e *Ethereum) attemptBroadcast(
}
logger.Error(fmt.Sprintf("error during broadcast: %s", err.Error()))
- if parsedErr, ok := err.(JsonError); ok {
+ if parsedErr, ok := err.(JSONError); ok {
if parsedErr.ErrorCode() == 3 && parsedErr.Error() == "execution reverted: Nonce already used" {
msg.Status = types.Complete
logger.Error(fmt.Sprintf("This account nonce has already been used: %d", nonce))
diff --git a/ethereum/broadcast_test.go b/ethereum/broadcast_test.go
index f39e596..ff09e7c 100644
--- a/ethereum/broadcast_test.go
+++ b/ethereum/broadcast_test.go
@@ -11,9 +11,10 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/joho/godotenv"
+ "github.com/stretchr/testify/require"
+
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum/contracts"
testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
- "github.com/stretchr/testify/require"
)
func TestEthUsedNonce(t *testing.T) {
diff --git a/ethereum/chain.go b/ethereum/chain.go
index 1effa87..8b7ddfd 100644
--- a/ethereum/chain.go
+++ b/ethereum/chain.go
@@ -10,9 +10,10 @@ import (
"strings"
"sync"
+ "github.com/ethereum/go-ethereum/ethclient"
+
"cosmossdk.io/log"
- "github.com/ethereum/go-ethereum/ethclient"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -145,11 +146,12 @@ func (e *Ethereum) InitializeClients(ctx context.Context, logger log.Logger) err
return nil
}
-func (e *Ethereum) CloseClients() {
+func (e *Ethereum) CloseClients() error {
if e.wsClient != nil {
e.wsClient.Close()
}
if e.rpcClient != nil {
e.rpcClient.Close()
}
+ return nil
}
diff --git a/ethereum/config.go b/ethereum/config.go
index 39fdcf4..d999bdc 100644
--- a/ethereum/config.go
+++ b/ethereum/config.go
@@ -1,6 +1,10 @@
package ethereum
import (
+ "fmt"
+ "os"
+ "strings"
+
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -24,11 +28,21 @@ type ChainConfig struct {
MetricsDenom string `yaml:"metrics-denom"`
MetricsExponent int `yaml:"metrics-exponent"`
- // TODO move to keyring
MinterPrivateKey string `yaml:"minter-private-key"`
}
func (c *ChainConfig) Chain(name string) (types.Chain, error) {
+ envKey := strings.ToUpper(name) + "_PRIV_KEY"
+ privKey := os.Getenv(envKey)
+
+ if len(c.MinterPrivateKey) == 0 || len(privKey) != 0 {
+ if len(privKey) == 0 {
+ return nil, fmt.Errorf("env variable %s is empty, priv key not found for chain %s", envKey, name)
+ } else {
+ c.MinterPrivateKey = privKey
+ }
+ }
+
return NewChain(
name,
c.Domain,
diff --git a/ethereum/listener.go b/ethereum/listener.go
index 0999d3a..4183f0c 100644
--- a/ethereum/listener.go
+++ b/ethereum/listener.go
@@ -8,13 +8,14 @@ import (
"os"
"time"
- "cosmossdk.io/log"
ethereum "github.com/ethereum/go-ethereum"
- ethtypes "github.com/ethereum/go-ethereum/core/types"
-
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
+ ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/pascaldekloe/etherstream"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/relayer"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -32,6 +33,7 @@ func (e *Ethereum) StartListener(
ctx context.Context,
logger log.Logger,
processingQueue chan *types.TxState,
+ flushOnlyMode bool,
flushInterval time.Duration,
) {
logger = logger.With("chain", e.name, "chain_id", e.chainID, "domain", e.domain)
@@ -54,43 +56,48 @@ func (e *Ethereum) StartListener(
Ready: make(chan struct{}),
}
- // start main stream (does not account for lookback period or specific start block)
- stream, sub, history := e.startMainStream(ctx, logger, messageSent, messageTransmitterAddress)
+ // FlushOnlyMode is used for the secondary, flush only relayer. When enabled, the main stream is not started.
+ if flushOnlyMode {
+ go e.flushMechanism(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, flushOnlyMode, flushInterval, sig)
+ } else {
+ // start main stream (does not account for lookback period or specific start block)
+ stream, sub, history := e.startMainStream(ctx, logger, messageSent, messageTransmitterAddress)
- go e.consumeStream(ctx, logger, processingQueue, messageSent, messageTransmitterABI, stream, sig)
- consumeHistory(logger, history, processingQueue, messageSent, messageTransmitterABI)
+ go e.consumeStream(ctx, logger, processingQueue, messageSent, messageTransmitterABI, stream, sig)
+ consumeHistory(logger, history, processingQueue, messageSent, messageTransmitterABI)
- // get history from (start block - lookback) up until latest block
- latestBlock := e.LatestBlock()
- start := latestBlock
- if e.startBlock != 0 {
- start = e.startBlock
- }
- startLookback := start - e.lookbackPeriod
- logger.Info(fmt.Sprintf("Getting history from %d: starting at: %d looking back %d blocks", startLookback, start, e.lookbackPeriod))
- e.getAndConsumeHistory(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, startLookback, latestBlock)
+ // get history from (start block - lookback) up until latest block
+ latestBlock := e.LatestBlock()
+ start := latestBlock
+ if e.startBlock != 0 {
+ start = e.startBlock
+ }
+ startLookback := start - e.lookbackPeriod
- logger.Info("Finished getting history")
+ logger.Info(fmt.Sprintf("Getting history from %d: starting at: %d looking back %d blocks", startLookback, start, e.lookbackPeriod))
+ e.getAndConsumeHistory(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, startLookback, latestBlock)
+ logger.Info("Finished getting history")
- if flushInterval > 0 {
- go e.flushMechanism(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, flushInterval, sig)
- }
+ if flushInterval > 0 {
+ go e.flushMechanism(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, flushOnlyMode, flushInterval, sig)
+ }
- // listen for errors in the main websocket stream
- // if error occurs, trigger sig.Ready
- // This will cancel `consumeStream` and `flushMechanism` routines
- select {
- case <-ctx.Done():
- return
- case err := <-sub.Err():
- logger.Error("Websocket disconnected. Reconnecting...", "err", err)
- close(sig.Ready)
-
- // restart
- e.startBlock = e.lastFlushedBlock
- time.Sleep(10 * time.Millisecond)
- e.StartListener(ctx, logger, processingQueue, flushInterval)
- return
+ // listen for errors in the main websocket stream
+ // if error occurs, trigger sig.Ready
+ // This will cancel `consumeStream` and `flushMechanism` routines
+ select {
+ case <-ctx.Done():
+ return
+ case err := <-sub.Err():
+ logger.Error("Websocket disconnected. Reconnecting...", "err", err)
+ close(sig.Ready)
+
+ // restart
+ e.startBlock = e.lastFlushedBlock
+ time.Sleep(10 * time.Millisecond)
+ e.StartListener(ctx, logger, processingQueue, flushOnlyMode, flushInterval)
+ return
+ }
}
}
@@ -99,9 +106,7 @@ func (e *Ethereum) startMainStream(
logger log.Logger,
messageSent abi.Event,
messageTransmitterAddress common.Address,
-
) (stream <-chan ethtypes.Log, sub ethereum.Subscription, history []ethtypes.Log) {
-
var err error
etherReader := etherstream.Reader{Backend: e.wsClient}
@@ -142,7 +147,6 @@ func (e *Ethereum) getAndConsumeHistory(
messageTransmitterAddress common.Address,
messageTransmitterABI abi.ABI,
start, end uint64) {
-
var toUnSub ethereum.Subscription
var history []ethtypes.Log
var err error
@@ -203,7 +207,8 @@ func consumeHistory(
messageSent abi.Event,
messageTransmitterABI abi.ABI,
) {
- for _, historicalLog := range history {
+ for i := range history {
+ historicalLog := history[i]
parsedMsg, err := types.EvmLogToMessageState(messageTransmitterABI, messageSent, &historicalLog)
if err != nil {
logger.Error("Unable to parse history log into MessageState, skipping", "tx hash", historicalLog.TxHash.Hex(), "err", err)
@@ -243,14 +248,15 @@ func (e *Ethereum) consumeStream(
continue
}
logger.Info(fmt.Sprintf("New stream msg from %d with tx hash %s", parsedMsg.SourceDomain, parsedMsg.SourceTxHash))
- if txState == nil {
+
+ switch {
+ case txState == nil:
txState = &types.TxState{TxHash: parsedMsg.SourceTxHash, Msgs: []*types.MessageState{parsedMsg}}
- } else if parsedMsg.SourceTxHash != txState.TxHash {
+ case parsedMsg.SourceTxHash != txState.TxHash:
processingQueue <- txState
txState = &types.TxState{TxHash: parsedMsg.SourceTxHash, Msgs: []*types.MessageState{parsedMsg}}
- } else {
+ default:
txState.Msgs = append(txState.Msgs, parsedMsg)
-
}
default:
if txState != nil {
@@ -261,6 +267,16 @@ func (e *Ethereum) consumeStream(
}
}
+// flushMechanism looks back over the chain history every specified flushInterval.
+//
+// Each chain is configured with a lookback period which signifies how many blocks to look back
+// at each interval. The flush mechanism will start from the last flushed block and will rescan
+// the lookback period and consume all messages in that range. The flush mechanism will not flush
+// all the way to the chain's latest block to avoid consuming messages that are still in the queue.
+// There will be a minimum gap of the lookback period between the last flushed block and the latest block.
+//
+// Note: The first time the flush mechanism is run, it will set the lastFlushedBlock to the latest block
+// minus twice the lookback period.
func (e *Ethereum) flushMechanism(
ctx context.Context,
logger log.Logger,
@@ -268,11 +284,19 @@ func (e *Ethereum) flushMechanism(
messageSent abi.Event,
messageTransmitterAddress common.Address,
messageTransmitterABI abi.ABI,
+ flushOnlyMode bool,
flushInterval time.Duration,
sig *errSignal,
) {
logger.Info(fmt.Sprintf("Starting flush mechanism. Will flush every %v", flushInterval))
+ // extraFlushBlocks is used to add an extra space between latest height and last flushed block
+ // this setting should only be used for the secondary, flush only relayer
+ extraFlushBlocks := uint64(0)
+ if flushOnlyMode {
+ extraFlushBlocks = 2 * e.lookbackPeriod
+ }
+
for {
timer := time.NewTimer(flushInterval)
select {
@@ -281,19 +305,31 @@ func (e *Ethereum) flushMechanism(
// initialize first lastFlushedBlock if not set
if e.lastFlushedBlock == 0 {
- e.lastFlushedBlock = latestBlock - e.lookbackPeriod
+ e.lastFlushedBlock = latestBlock - (2*e.lookbackPeriod + extraFlushBlocks)
+
+ if latestBlock < e.lookbackPeriod {
+ e.lastFlushedBlock = 0
+ }
}
- // start from lastFlushedBlock
- start := e.lastFlushedBlock
+ // start from the last block it flushed
+ startBlock := e.lastFlushedBlock
+
+ // set finish block to be latestBlock - lookbackPeriod
+ finishBlock := latestBlock - (e.lookbackPeriod + extraFlushBlocks)
+
+ if startBlock >= finishBlock {
+ logger.Debug("No new blocks to flush")
+ continue
+ }
- logger.Info(fmt.Sprintf("Flush started from %d to %d", start, latestBlock))
+ logger.Info(fmt.Sprintf("Flush started from %d to %d (current height: %d, lookback period: %d)", startBlock, finishBlock, latestBlock, e.lookbackPeriod))
- // consume from lastFlushedBlock to the latestBlock
- e.getAndConsumeHistory(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, start, latestBlock)
+ // consume from lastFlushedBlock to the finishBlock
+ e.getAndConsumeHistory(ctx, logger, processingQueue, messageSent, messageTransmitterAddress, messageTransmitterABI, startBlock, finishBlock)
// update lastFlushedBlock to the last block it flushed
- e.lastFlushedBlock = latestBlock
+ e.lastFlushedBlock = finishBlock
logger.Info("Flush complete")
diff --git a/ethereum/listener_test.go b/ethereum/listener_test.go
index 7d43c39..f261b24 100644
--- a/ethereum/listener_test.go
+++ b/ethereum/listener_test.go
@@ -5,10 +5,11 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
// TODO: update test. This test is currently outdated as the RPC endpoints likely won't have this much history
@@ -27,23 +28,22 @@ func TestStartListener(t *testing.T) {
processingQueue := make(chan *types.TxState, 10000)
- go eth.StartListener(ctx, a.Logger, processingQueue, 0)
+ go eth.StartListener(ctx, a.Logger, processingQueue, false, 0)
time.Sleep(5 * time.Second)
tx := <-processingQueue
expectedMsg := &types.MessageState{
- IrisLookupId: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
+ IrisLookupID: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
Status: "created",
SourceDomain: 0,
DestDomain: 4,
SourceTxHash: "0xe1d7729de300274ee3a2fd20ba179b14a8e3ffcd9d847c506b06760f0dad7802",
}
- require.Equal(t, expectedMsg.IrisLookupId, tx.Msgs[0].IrisLookupId)
+ require.Equal(t, expectedMsg.IrisLookupID, tx.Msgs[0].IrisLookupID)
require.Equal(t, expectedMsg.Status, tx.Msgs[0].Status)
require.Equal(t, expectedMsg.SourceDomain, tx.Msgs[0].SourceDomain)
require.Equal(t, expectedMsg.DestDomain, tx.Msgs[0].DestDomain)
require.Equal(t, expectedMsg.SourceTxHash, tx.Msgs[0].SourceTxHash)
-
}
diff --git a/ethereum/util.go b/ethereum/util.go
index ddb168c..11564da 100644
--- a/ethereum/util.go
+++ b/ethereum/util.go
@@ -4,12 +4,13 @@ import (
"crypto/ecdsa"
"errors"
"fmt"
+ "math/big"
+
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
- "math/big"
)
-type JsonError interface {
+type JSONError interface {
Error() string
ErrorCode() int
ErrorData() interface{}
diff --git a/ethereum/util_test.go b/ethereum/util_test.go
index 7308d5d..64fb160 100644
--- a/ethereum/util_test.go
+++ b/ethereum/util_test.go
@@ -3,9 +3,10 @@ package ethereum_test
import (
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
- "github.com/stretchr/testify/require"
)
func TestGetEthereumAccountNonce(t *testing.T) {
@@ -13,7 +14,7 @@ func TestGetEthereumAccountNonce(t *testing.T) {
ethConfig := a.Config.Chains["ethereum"].(*ethereum.ChainConfig)
_, err := ethereum.GetEthereumAccountNonce(ethConfig.RPC, "0x4996f29b254c77972fff8f25e6f7797b3c9a0eb6")
- require.Nil(t, err)
+ require.NoError(t, err)
}
// Return public ecdsa key and address given the private key
@@ -24,5 +25,5 @@ func TestGetEcdsaKeyAddress(t *testing.T) {
key, addr, err := ethereum.GetEcdsaKeyAddress(ethConfig.MinterPrivateKey)
require.NotNil(t, key)
require.NotNil(t, addr)
- require.Nil(t, err)
+ require.NoError(t, err)
}
diff --git a/go.mod b/go.mod
index 1b804e6..cf89619 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
cosmossdk.io/log v1.2.1
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/cosmos/cosmos-sdk v0.45.15
- github.com/ethereum/go-ethereum v1.12.2
+ github.com/ethereum/go-ethereum v1.13.15
github.com/golang/protobuf v1.5.3 // indirect
github.com/rs/zerolog v1.30.0
github.com/spf13/cobra v1.8.0
@@ -38,10 +38,12 @@ require (
github.com/99designs/keyring v1.2.1 // indirect
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
github.com/DataDog/zstd v1.5.2 // indirect
- github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
+ github.com/Microsoft/go-winio v0.6.1 // indirect
+ github.com/StackExchange/wmi v1.2.1 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
+ github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
@@ -49,10 +51,13 @@ require (
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cockroachdb/errors v1.10.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
- github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 // indirect
+ github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
+ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/cometbft/cometbft-db v0.8.0 // indirect
github.com/confio/ics23/go v0.9.0 // indirect
+ github.com/consensys/bavard v0.1.13 // indirect
+ github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/cosmos/btcutil v1.0.5 // indirect
github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 // indirect
github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect
@@ -60,6 +65,7 @@ require (
github.com/cosmos/gorocksdb v1.2.0 // indirect
github.com/cosmos/iavl v0.19.5 // indirect
github.com/cosmos/ledger-cosmos-go v0.12.2 // indirect
+ github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
@@ -69,6 +75,7 @@ require (
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
+ github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/getsentry/sentry-go v0.23.0 // indirect
@@ -76,11 +83,10 @@ require (
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
- github.com/go-ole/go-ole v1.2.6 // indirect
+ github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
- github.com/go-stack/stack v1.8.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gogo/protobuf v1.3.3 // indirect
@@ -101,7 +107,7 @@ require (
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
- github.com/holiman/uint256 v1.2.3 // indirect
+ github.com/holiman/uint256 v1.2.4 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -115,10 +121,10 @@ require (
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
- github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mtibben/percent v0.2.1 // indirect
@@ -145,12 +151,13 @@ require (
github.com/spf13/viper v1.18.1 // indirect
github.com/strangelove-ventures/noble v1.0.1-0.20230526193404-1e5ed6be44fd // indirect
github.com/subosito/gotenv v1.6.0 // indirect
+ github.com/supranational/blst v0.3.11 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tendermint/tendermint v0.34.27 // indirect
github.com/tendermint/tm-db v0.6.7 // indirect
- github.com/tklauser/go-sysconf v0.3.11 // indirect
- github.com/tklauser/numcpus v0.6.0 // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/zondax/hid v0.9.1 // indirect
@@ -164,12 +171,13 @@ require (
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
+ golang.org/x/tools v0.15.0 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
- gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
+ rsc.io/tmplfunc v0.0.3 // indirect
)
replace (
diff --git a/go.sum b/go.sum
index 0b957ac..02d281f 100644
--- a/go.sum
+++ b/go.sum
@@ -24,12 +24,14 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
-github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
-github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o=
-github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40=
+github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig=
@@ -48,8 +50,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo=
-github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
+github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
@@ -80,16 +82,18 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
-github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
+github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
+github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cockroachdb/errors v1.10.0 h1:lfxS8zZz1+OjtV4MtNWgboi/W5tyLEB6VQZBXN+0VUU=
github.com/cockroachdb/errors v1.10.0/go.mod h1:lknhIsEVQ9Ss/qKDBQS/UqFSvPQjOwNq2qyKAxtHRqE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
-github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk=
-github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM=
+github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A=
+github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZr9ZvoCcA=
github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M=
github.com/cometbft/cometbft v0.34.27 h1:ri6BvmwjWR0gurYjywcBqRe4bbwc3QVs9KRcCzgh/J0=
@@ -102,8 +106,8 @@ github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4
github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
-github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA=
-github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU=
+github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
+github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -133,8 +137,10 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A=
-github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4=
+github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
+github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
+github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
+github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM=
github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -169,10 +175,10 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg=
-github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
-github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y=
-github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI=
+github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
+github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
+github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo=
+github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
@@ -181,8 +187,8 @@ github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpm
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
-github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
+github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA=
+github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -196,6 +202,8 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
+github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=
+github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE=
github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -215,8 +223,9 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
-github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -226,8 +235,6 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
-github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@@ -238,8 +245,8 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0=
github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic=
-github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
-github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
@@ -277,11 +284,12 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -319,15 +327,15 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU=
github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
-github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw=
-github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
-github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
-github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
+github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
+github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
-github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -366,6 +374,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
+github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
@@ -402,6 +412,7 @@ github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjU
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
+github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -567,10 +578,10 @@ github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu
github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I=
github.com/tidwall/btree v1.5.0 h1:iV0yVY/frd7r6qGBXfEYs7DH0gTDgrKTrDjS7xt/IyQ=
github.com/tidwall/btree v1.5.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
-github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
-github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
-github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
-github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -579,8 +590,8 @@ github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3C
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU=
-github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
+github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
+github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
@@ -610,13 +621,15 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
-golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -678,7 +691,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -704,6 +717,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -753,8 +768,6 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
-gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/integration/ERC20.go b/integration/ERC20.go
index 51412e3..49c2554 100644
--- a/integration/ERC20.go
+++ b/integration/ERC20.go
@@ -1,7 +1,7 @@
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
-package integration_testing
+package integration_test
import (
"errors"
diff --git a/integration/config.go b/integration/config.go
index 634a86d..9f36c8d 100644
--- a/integration/config.go
+++ b/integration/config.go
@@ -1,4 +1,4 @@
-package integration_testing
+package integration_test
import (
"os"
@@ -26,11 +26,11 @@ type IntegrationConfig struct {
}
type IntegrationChain struct {
- ChainId string `yaml:"chain-id"`
+ ChainID string `yaml:"chain-id"`
Domain uint32 `yaml:"domain"`
Address string `yaml:"address"`
PrivateKey string `yaml:"private-key"`
- Rpc string `yaml:"rpc"`
+ RPC string `yaml:"rpc"`
UsdcTokenAddress string `yaml:"usdc-token-address"`
TokenMessengerAddress string `yaml:"token-messenger-address"`
DestinationCaller string `yaml:"destination-caller"`
diff --git a/integration/deployed_relayer_test.go b/integration/deployed_relayer_test.go
index 3fb7428..0999f66 100644
--- a/integration/deployed_relayer_test.go
+++ b/integration/deployed_relayer_test.go
@@ -1,16 +1,20 @@
-package integration_testing
+package integration_test
import (
"context"
"encoding/hex"
- "fmt"
"math/big"
"strconv"
"testing"
"time"
- "cosmossdk.io/math"
nobletypes "github.com/circlefin/noble-cctp/x/cctp/types"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethclient"
+ "github.com/stretchr/testify/require"
+
sdkClient "github.com/cosmos/cosmos-sdk/client"
clientTx "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
@@ -20,14 +24,12 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
xauthtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
- "github.com/ethereum/go-ethereum/accounts/abi/bind"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethclient"
+
+ "cosmossdk.io/math"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum/contracts"
- "github.com/stretchr/testify/require"
)
// The tests in this file are meant to test an actively deployed relayer.
@@ -65,25 +67,25 @@ func TestNobleBurnToEthDeployed(t *testing.T) {
var burnAmount = math.NewInt(1)
- fmt.Printf("\nPath: %d -> %d\n", nobleCfg.Domain, ethConfig.Domain)
+ t.Logf("Path: %d -> %d", nobleCfg.Domain, ethConfig.Domain)
- fmt.Println("Source Address: ", nobleCfg.Address)
- cc, err := cosmos.NewProvider(nobleCfg.Rpc)
+ t.Logf("Source Address: %s", nobleCfg.Address)
+ cc, err := cosmos.NewProvider(nobleCfg.RPC)
require.NoError(t, err)
originalNobleBalance, err := getNobleAccountBalance(ctx, cc, nobleCfg.Address, uusdcDenom)
require.NoError(t, err)
- fmt.Println("Source Balance: ", originalNobleBalance)
+ t.Logf("Source Balance: %d", originalNobleBalance)
- fmt.Println("Deposit Address: ", destAddress)
+ t.Logf("Deposit Address: %s", destAddress)
// Get original usdc balance on Eth to verify that funds are deposited later
- client, _ := ethclient.Dial(ethConfig.Rpc)
+ client, _ := ethclient.Dial(ethConfig.RPC)
defer client.Close()
ogBalance, err := getEthBalance(client, ethConfig.UsdcTokenAddress, destAddress)
require.NoError(t, err)
- fmt.Println("Destination Balance: ", ogBalance)
+ t.Logf("Destination Balance: %d", ogBalance)
- fmt.Println("Burn Amount: ", burnAmount.String())
+ t.Logf("Burn Amount: %s", burnAmount.String())
// set up sdk context
interfaceRegistry := codectypes.NewInterfaceRegistry()
@@ -98,7 +100,7 @@ func TestNobleBurnToEthDeployed(t *testing.T) {
keyBz, _ := hex.DecodeString(nobleCfg.PrivateKey)
privKey := secp256k1.PrivKey{Key: keyBz}
nobleAddress, err := bech32.ConvertAndEncode("noble", privKey.PubKey().Address())
- require.Nil(t, err)
+ require.NoError(t, err)
// destination address
mintRecipient := make([]byte, 32)
@@ -118,13 +120,13 @@ func TestNobleBurnToEthDeployed(t *testing.T) {
)
err = txBuilder.SetMsgs(burnMsg)
- require.Nil(t, err)
+ require.NoError(t, err)
txBuilder.SetGasLimit(200000)
// sign + broadcast txn
accountNumber, accountSequence, err := getNobleAccountNumberSequenceGRPC(cc, nobleAddress)
- require.Nil(t, err)
+ require.NoError(t, err)
sigV2 := signing.SignatureV2{
PubKey: privKey.PubKey(),
@@ -132,44 +134,47 @@ func TestNobleBurnToEthDeployed(t *testing.T) {
SignMode: sdkContext.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
- Sequence: uint64(accountSequence),
+ Sequence: accountSequence,
}
signerData := xauthsigning.SignerData{
- ChainID: nobleCfg.ChainId,
- AccountNumber: uint64(accountNumber),
- Sequence: uint64(accountSequence),
+ ChainID: nobleCfg.ChainID,
+ AccountNumber: accountNumber,
+ Sequence: accountSequence,
}
- txBuilder.SetSignatures(sigV2)
+ err = txBuilder.SetSignatures(sigV2)
+ require.NoError(t, err)
+
sigV2, err = clientTx.SignWithPrivKey(
sdkContext.TxConfig.SignModeHandler().DefaultMode(),
signerData,
txBuilder,
&privKey,
sdkContext.TxConfig,
- uint64(accountSequence),
+ accountSequence,
)
- require.Nil(t, err)
+ require.NoError(t, err)
err = txBuilder.SetSignatures(sigV2)
- require.Nil(t, err)
+ require.NoError(t, err)
// Generated Protobuf-encoded bytes.
txBytes, err := sdkContext.TxConfig.TxEncoder()(txBuilder.GetTx())
- require.Nil(t, err)
+ require.NoError(t, err)
rpcResponse, err := cc.RPCClient.BroadcastTxSync(context.Background(), txBytes)
- require.Nil(t, err)
- fmt.Printf("Deposit for Burn broadcasted: https://mintscan.io/noble-testnet/txs/%s\n", rpcResponse.Hash.String())
+ require.NoError(t, err)
+ t.Logf("Deposit for Burn broadcasted: https://mintscan.io/noble-testnet/txs/%s", rpcResponse.Hash.String())
- fmt.Println("Waiting for circle to approve and destination wallet to receive funds...")
+ t.Log("Waiting for circle to approve and destination wallet to receive funds...")
var newEthBalance uint64
for i := 0; i < 120; i++ {
newEthBalance, err = getEthBalance(client, ethConfig.UsdcTokenAddress, destAddress)
+
require.NoError(t, err)
if ogBalance+burnAmount.Uint64() == newEthBalance {
- fmt.Println("Successfully minted into " + destAddress)
+ t.Log("Successfully minted into " + destAddress)
break
}
time.Sleep(3 * time.Second)
@@ -177,10 +182,10 @@ func TestNobleBurnToEthDeployed(t *testing.T) {
newNobleBal, err := getNobleAccountBalance(ctx, cc, nobleCfg.Address, uusdcDenom)
require.NoError(t, err)
- fmt.Println("Source Balance: ", newNobleBal)
+ t.Log("Source Balance: ", newNobleBal)
// verify eth balance
- fmt.Println("Destination Balance: ", newEthBalance)
+ t.Logf("Destination Balance: %d", newEthBalance)
require.Equal(t, ogBalance+burnAmount.Uint64(), newEthBalance)
}
@@ -188,7 +193,6 @@ func TestNobleBurnToEthDeployed(t *testing.T) {
func TestEthBurnToNobleDeployed(t *testing.T) {
c, err := ParseIntegration("../.ignore/integration.yaml")
require.NoError(t, err)
-
ctx := context.Background()
// -- NETWORK --
@@ -210,41 +214,41 @@ func TestEthBurnToNobleDeployed(t *testing.T) {
destAddress := nobleCfg.Address
- fmt.Printf("\nPath: %d -> %d\n", ethConfig.Domain, nobleCfg.Domain)
+ t.Logf("Path: %d -> %d", ethConfig.Domain, nobleCfg.Domain)
- fmt.Println("Source Address: ", ethConfig.Address)
+ t.Logf("Source Address: %s", ethConfig.Address)
- client, err := ethclient.Dial(ethConfig.Rpc)
+ client, err := ethclient.Dial(ethConfig.RPC)
require.NoError(t, err)
defer client.Close()
originalEthBalance, err := getEthBalance(client, ethConfig.UsdcTokenAddress, ethConfig.Address)
require.NoError(t, err)
- fmt.Println("Source Balance: ", originalEthBalance)
+ t.Logf("Source Balance: %d", originalEthBalance)
- fmt.Println("Destination Address: ", destAddress)
+ t.Logf("Destination Address: %s", destAddress)
// Get original usdc balance on Noble to verify that funds are deposited later
- cc, err := cosmos.NewProvider(nobleCfg.Rpc)
+ cc, err := cosmos.NewProvider(nobleCfg.RPC)
require.NoError(t, err)
originalNobleBalance, err := getNobleAccountBalance(ctx, cc, destAddress, uusdcDenom)
require.NoError(t, err)
- fmt.Println("Destination Balance: ", originalNobleBalance)
+ t.Logf("Destination Balance: %d", originalNobleBalance)
- fmt.Println("Burn Amount: ", burnAmount.String())
+ t.Logf("Burn Amount: %s", burnAmount.String())
privateKey, err := crypto.HexToECDSA(ethConfig.PrivateKey)
- require.Nil(t, err)
+ require.NoError(t, err)
- i, err := strconv.ParseInt(ethConfig.ChainId, 10, 64)
+ i, err := strconv.ParseInt(ethConfig.ChainID, 10, 64)
require.NoError(t, err)
ethChainID := big.NewInt(i)
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, ethChainID)
- require.Nil(t, err)
+ require.NoError(t, err)
// deal w/ nonce
- nextNonce, err := ethereum.GetEthereumAccountNonce(ethConfig.Rpc, ethConfig.Address)
+ nextNonce, err := ethereum.GetEthereumAccountNonce(ethConfig.RPC, ethConfig.Address)
require.NoError(t, err)
auth.Nonce = big.NewInt(nextNonce)
@@ -254,14 +258,14 @@ func TestEthBurnToNobleDeployed(t *testing.T) {
// contractValue := burnAmount
_, err = erc20.Approve(auth, common.HexToAddress(ethConfig.TokenMessengerAddress), burnAmount)
- require.Nil(t, err)
+ require.NoError(t, err)
// wait for erc20 approval to be on chain
time.Sleep(10 * time.Second)
// create tokenMessenger
tokenMessenger, err := contracts.NewTokenMessenger(common.HexToAddress(ethConfig.TokenMessengerAddress), client)
- require.Nil(t, err)
+ require.NoError(t, err)
_, mintRecipientRaw, err := bech32.DecodeAndConvert(nobleCfg.Address)
require.NoError(t, err)
@@ -284,15 +288,15 @@ func TestEthBurnToNobleDeployed(t *testing.T) {
require.NoError(t, err)
time.Sleep(5 * time.Second)
- fmt.Println("Deposit for Burn broadcasted. Tx Hash: ", tx.Hash().String())
+ t.Logf("Deposit for Burn broadcasted. Tx Hash: %s", tx.Hash().String())
var newBalance uint64
- fmt.Println("Waiting for circle to approve and destination wallet to receive funds...")
+ t.Log("Waiting for circle to approve and destination wallet to receive funds...")
for i := 0; i < 250; i++ {
newBalance, err = getNobleAccountBalance(ctx, cc, destAddress, uusdcDenom)
require.NoError(t, err)
if originalNobleBalance+burnAmount.Uint64() == newBalance {
- fmt.Println("Successfully minted at https://testnet.mintscan.io/noble-testnet/account/" + destAddress)
+ t.Logf("Successfully minted at https://testnet.mintscan.io/noble-testnet/account/" + destAddress)
break
}
time.Sleep(3 * time.Second)
@@ -300,9 +304,9 @@ func TestEthBurnToNobleDeployed(t *testing.T) {
newEthBalance, err := getEthBalance(client, ethConfig.UsdcTokenAddress, ethConfig.Address)
require.NoError(t, err)
- fmt.Println("Source Balance: ", newEthBalance)
+ t.Logf("Source Balance: %d", newEthBalance)
- fmt.Println("Destination Balance: ", newBalance)
+ t.Logf("Destination Balance: %d", newBalance)
// verify noble balance
require.Equal(t, originalNobleBalance+burnAmount.Uint64(), newBalance)
}
diff --git a/integration/eth_burn_to_noble_mint_test.go b/integration/eth_burn_to_noble_mint_test.go
index f526db8..ba565a0 100644
--- a/integration/eth_burn_to_noble_mint_test.go
+++ b/integration/eth_burn_to_noble_mint_test.go
@@ -1,27 +1,28 @@
-package integration_testing
+package integration_test
import (
"context"
"encoding/hex"
- "fmt"
"math/big"
"testing"
"time"
- "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
- "github.com/cosmos/cosmos-sdk/testutil/testdata"
- "github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
+ "github.com/stretchr/testify/require"
+
+ "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
+ "github.com/cosmos/cosmos-sdk/testutil/testdata"
+ "github.com/cosmos/cosmos-sdk/types/bech32"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cmd"
"github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum/contracts"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
// TestEthBurnToNobleMint broadcasts a depositForBurn on Sepolia of 0.000001 USDC
@@ -61,7 +62,7 @@ func TestEthBurnToNobleMint(t *testing.T) {
var burnAmount = big.NewInt(1)
- fmt.Println("Starting relayer...")
+ t.Log("Starting relayer...")
registeredDomains := make(map[types.Domain]types.Chain)
registeredDomains[0] = ethChain
registeredDomains[4] = nobleChain
@@ -72,31 +73,31 @@ func TestEthBurnToNobleMint(t *testing.T) {
processingQueue := make(chan *types.TxState, 10)
- go ethChain.StartListener(ctx, a.Logger, processingQueue, 0)
+ go ethChain.StartListener(ctx, a.Logger, processingQueue, false, 0)
go cmd.StartProcessor(ctx, a, registeredDomains, processingQueue, sequenceMap, nil)
_, _, generatedWallet := testdata.KeyTestPubAddr()
destAddress, _ := bech32.ConvertAndEncode("noble", generatedWallet)
- fmt.Println("Noble destination address: ", destAddress)
- fmt.Println("Minting on Noble to https://testnet.mintscan.io/noble-testnet/account/" + destAddress)
+ t.Logf("Noble destination address: %s", destAddress)
+ t.Logf("Minting on Noble to https://testnet.mintscan.io/noble-testnet/account/%s", destAddress)
// verify noble usdc amount
cc, err := cosmos.NewProvider(nobleCfg.RPC)
- require.Nil(t, err)
+ require.NoError(t, err)
originalNobleBalance, err := getNobleAccountBalance(ctx, cc, destAddress, uusdcDenom)
require.NoError(t, err)
// eth client
client, err := ethclient.Dial(ethCfg.RPC)
- require.Nil(t, err)
+ require.NoError(t, err)
defer client.Close()
privateKey, err := crypto.HexToECDSA(ethCfg.MinterPrivateKey)
- require.Nil(t, err)
+ require.NoError(t, err)
sepoliaChainID := big.NewInt(ethCfg.ChainID)
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, sepoliaChainID)
- require.Nil(t, err)
+ require.NoError(t, err)
// deal w/ nonce
ethRelayerAddress, err := ethConvertPrivateKeytoAddress(ethCfg.MinterPrivateKey)
@@ -110,17 +111,17 @@ func TestEthBurnToNobleMint(t *testing.T) {
require.NoError(t, err)
_, err = erc20.Approve(auth, common.HexToAddress(TokenMessengerAddressSepolia), burnAmount)
- require.Nil(t, err)
+ require.NoError(t, err)
// Ensure approval is on chain
time.Sleep(20 * time.Second)
// create tokenMessenger
tokenMessenger, err := contracts.NewTokenMessenger(common.HexToAddress(TokenMessengerAddressSepolia), client)
- require.Nil(t, err)
+ require.NoError(t, err)
mintRecipientPadded := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, generatedWallet...)
- require.Nil(t, err)
+ require.NoError(t, err)
auth.Nonce = big.NewInt(nextNonce + 1)
@@ -130,7 +131,7 @@ func TestEthBurnToNobleMint(t *testing.T) {
require.NoError(t, err)
privKey := secp256k1.PrivKey{Key: keyBz}
caller, err := bech32.ConvertAndEncode("noble", privKey.PubKey().Address())
- require.Nil(t, err)
+ require.NoError(t, err)
_, callerRaw, err := bech32.DecodeAndConvert(caller)
require.NoError(t, err)
destinationCallerPadded := append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, callerRaw...)
@@ -148,15 +149,15 @@ func TestEthBurnToNobleMint(t *testing.T) {
}
time.Sleep(5 * time.Second)
- fmt.Printf("Transaction broadcasted: https://sepolia.etherscan.io/tx/%s\n", tx.Hash().String())
+ t.Logf("Transaction broadcasted: https://sepolia.etherscan.io/tx/%s", tx.Hash().String())
var newBalance uint64
- fmt.Println("Waiting for circle to approve and destination wallet to receive funds.")
+ t.Log("Waiting for circle to approve and destination wallet to receive funds.")
for i := 0; i < 250; i++ {
newBalance, err = getNobleAccountBalance(ctx, cc, destAddress, uusdcDenom)
require.NoError(t, err)
if originalNobleBalance+burnAmount.Uint64() == newBalance {
- fmt.Println("Successfully minted at https://testnet.mintscan.io/noble-testnet/account/" + destAddress)
+ t.Logf("Successfully minted at https://testnet.mintscan.io/noble-testnet/account/%s", destAddress)
break
}
time.Sleep(2 * time.Second)
diff --git a/integration/noble_burn_to_eth_mint_test.go b/integration/noble_burn_to_eth_mint_test.go
index ddcef78..239e0fb 100644
--- a/integration/noble_burn_to_eth_mint_test.go
+++ b/integration/noble_burn_to_eth_mint_test.go
@@ -1,15 +1,18 @@
-package integration_testing
+package integration_test
import (
"context"
"crypto/ecdsa"
"encoding/hex"
- "fmt"
"testing"
"time"
- "cosmossdk.io/math"
nobletypes "github.com/circlefin/noble-cctp/x/cctp/types"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethclient"
+ "github.com/stretchr/testify/require"
+
sdkClient "github.com/cosmos/cosmos-sdk/client"
clientTx "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
@@ -19,15 +22,14 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
xauthtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethclient"
+
+ "cosmossdk.io/math"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cmd"
"github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
// TestNobleBurnToEthMint broadcasts a depositForBurn on Noble of 1 cent
@@ -65,7 +67,7 @@ func TestNobleBurnToEthMint(t *testing.T) {
err = ethChain.InitializeClients(ctx, a.Logger)
require.NoError(t, err)
- fmt.Println("Starting relayer...")
+ t.Log("Starting relayer...")
registeredDomains := make(map[types.Domain]types.Chain)
registeredDomains[0] = ethChain
@@ -77,14 +79,14 @@ func TestNobleBurnToEthMint(t *testing.T) {
processingQueue := make(chan *types.TxState, 10)
- go nobleChain.StartListener(ctx, a.Logger, processingQueue, 0)
+ go nobleChain.StartListener(ctx, a.Logger, processingQueue, false, 0)
go cmd.StartProcessor(ctx, a, registeredDomains, processingQueue, sequenceMap, nil)
ethDestinationAddress, _, err := generateEthWallet()
require.NoError(t, err)
- fmt.Println("Generated dest wallet: ", ethDestinationAddress)
+ t.Logf("Generated dest wallet: %s", ethDestinationAddress)
- fmt.Println("Minting on Ethereum to https://sepolia.etherscan.io/address/" + ethDestinationAddress)
+ t.Logf("Minting on Ethereum to https://sepolia.etherscan.io/address/%s", ethDestinationAddress)
// verify ethereum usdc amount
client, _ := ethclient.Dial(ethCfg.RPC)
@@ -108,7 +110,7 @@ func TestNobleBurnToEthMint(t *testing.T) {
privKey := secp256k1.PrivKey{Key: keyBz}
nobleAddress, err := bech32.ConvertAndEncode("noble", privKey.PubKey().Address())
- require.Nil(t, err)
+ require.NoError(t, err)
mintRecipient := make([]byte, 32)
copy(mintRecipient[12:], common.FromHex(ethDestinationAddress))
@@ -119,7 +121,7 @@ func TestNobleBurnToEthMint(t *testing.T) {
callerPrivKey := ethCfg.MinterPrivateKey
privateKeyBytes := common.FromHex(callerPrivKey)
privateKey, err := crypto.ToECDSA(privateKeyBytes)
- require.Nil(t, err)
+ require.NoError(t, err)
pubKey := privateKey.Public()
publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
require.True(t, ok)
@@ -138,17 +140,17 @@ func TestNobleBurnToEthMint(t *testing.T) {
)
err = txBuilder.SetMsgs(burnMsg)
- require.Nil(t, err)
+ require.NoError(t, err)
txBuilder.SetGasLimit(nobleCfg.GasLimit)
// sign + broadcast txn
cc, err := cosmos.NewProvider(nobleCfg.RPC)
- require.Nil(t, err)
+ require.NoError(t, err)
accountNumber, accountSequence, err := getNobleAccountNumberSequenceGRPC(cc, nobleAddress)
- require.Nil(t, err)
+ require.NoError(t, err)
sigV2 := signing.SignatureV2{
PubKey: privKey.PubKey(),
@@ -156,44 +158,46 @@ func TestNobleBurnToEthMint(t *testing.T) {
SignMode: sdkContext.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
- Sequence: uint64(accountSequence),
+ Sequence: accountSequence,
}
signerData := xauthsigning.SignerData{
ChainID: nobleCfg.ChainID,
- AccountNumber: uint64(accountNumber),
- Sequence: uint64(accountSequence),
+ AccountNumber: accountNumber,
+ Sequence: accountSequence,
}
- txBuilder.SetSignatures(sigV2)
+ err = txBuilder.SetSignatures(sigV2)
+ require.NoError(t, err)
+
sigV2, err = clientTx.SignWithPrivKey(
sdkContext.TxConfig.SignModeHandler().DefaultMode(),
signerData,
txBuilder,
&privKey,
sdkContext.TxConfig,
- uint64(accountSequence),
+ accountSequence,
)
- require.Nil(t, err)
+ require.NoError(t, err)
err = txBuilder.SetSignatures(sigV2)
- require.Nil(t, err)
+ require.NoError(t, err)
// Generated Protobuf-encoded bytes.
txBytes, err := sdkContext.TxConfig.TxEncoder()(txBuilder.GetTx())
- require.Nil(t, err)
+ require.NoError(t, err)
rpcResponse, err := cc.RPCClient.BroadcastTxSync(context.Background(), txBytes)
- require.Nil(t, err)
- fmt.Printf("Update pending: https://testnet.mintscan.io/noble-testnet/txs/%s\n", rpcResponse.Hash.String())
+ require.NoError(t, err)
+ t.Logf("Update pending: https://testnet.mintscan.io/noble-testnet/txs/%s", rpcResponse.Hash.String())
- fmt.Println("Checking eth wallet...")
+ t.Log("Checking eth wallet...")
var newBalance uint64
for i := 0; i < 60; i++ {
newBalance, err = getEthBalance(client, usdcTokenAddressSepolia, ethDestinationAddress)
require.NoError(t, err)
if originalEthBalance+burnAmount.Uint64() == newBalance {
- fmt.Println("Successfully minted at https://sepolia.etherscan.io/address/" + ethDestinationAddress)
+ t.Logf("Successfully minted at https://sepolia.etherscan.io/address/%s", ethDestinationAddress)
break
}
time.Sleep(3 * time.Second)
diff --git a/integration/types.go b/integration/types.go
index 773a82b..82f72d7 100644
--- a/integration/types.go
+++ b/integration/types.go
@@ -1,4 +1,4 @@
-package integration_testing
+package integration_test
type BalanceResponse struct {
Balance struct {
@@ -21,11 +21,11 @@ type EthereumRPCPayload struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params []interface{} `json:"params"`
- Id int `json:"id"`
+ ID int `json:"id"`
}
type EthereumTxCountResponse struct {
- JsonRpc string `json:"jsonrpc"`
- Id int `json:"id"`
+ JSONRPC string `json:"jsonrpc"`
+ ID int `json:"id"`
Result string `json:"result"`
}
diff --git a/integration/util.go b/integration/util.go
index e0b6602..ac0bdc5 100644
--- a/integration/util.go
+++ b/integration/util.go
@@ -1,33 +1,35 @@
-package integration_testing
+package integration_test
import (
"context"
+ "crypto/ecdsa"
"encoding/hex"
"fmt"
"log"
"math/big"
"strings"
- "crypto/ecdsa"
-
- authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
- bankTypes "github.com/cosmos/cosmos-sdk/x/bank/types"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
+
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ bankTypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
)
+//nolint:gosec
const (
usdcTokenAddressSepolia = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
TokenMessengerAddressSepolia = "0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5"
-
- uusdcDenom = "uusdc"
+ uusdcDenom = "uusdc"
)
+// nolint:unparam
func getNobleAccountBalance(ctx context.Context, cc *cosmos.CosmosProvider, address, denom string) (uint64, error) {
qc := bankTypes.NewQueryClient(cc)
res, err := qc.Balance(ctx, &bankTypes.QueryBalanceRequest{
@@ -54,7 +56,6 @@ func getNobleAccountNumberSequenceGRPC(cc *cosmos.CosmosProvider, address string
}
return acc.GetAccountNumber(), acc.GetSequence(), nil
-
}
func getEthBalance(client *ethclient.Client, usdcTokenAddress, walletAddress string) (uint64, error) {
diff --git a/noble/broadcast.go b/noble/broadcast.go
index defdb51..d1fb967 100644
--- a/noble/broadcast.go
+++ b/noble/broadcast.go
@@ -9,8 +9,8 @@ import (
"strconv"
"time"
- "cosmossdk.io/log"
nobletypes "github.com/circlefin/noble-cctp/x/cctp/types"
+
sdkclient "github.com/cosmos/cosmos-sdk/client"
clientTx "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
@@ -19,6 +19,9 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
xauthtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/relayer"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -92,11 +95,9 @@ func (n *Noble) attemptBroadcast(
sdkContext sdkclient.Context,
txBuilder sdkclient.TxBuilder,
) error {
-
var receiveMsgs []sdk.Msg
for _, msg := range msgs {
-
- used, err := n.cc.QueryUsedNonce(ctx, types.Domain(msg.SourceDomain), msg.Nonce)
+ used, err := n.cc.QueryUsedNonce(ctx, msg.SourceDomain, msg.Nonce)
if err != nil {
return fmt.Errorf("unable to query used nonce: %w", err)
}
@@ -153,39 +154,39 @@ func (n *Noble) attemptBroadcast(
SignMode: sdkContext.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
- Sequence: uint64(accountSequence),
+ Sequence: accountSequence,
}
signerData := xauthsigning.SignerData{
ChainID: n.chainID,
- AccountNumber: uint64(n.accountNumber),
- Sequence: uint64(accountSequence),
+ AccountNumber: n.accountNumber,
+ Sequence: accountSequence,
}
- txBuilder.SetSignatures(sigV2)
+ err := txBuilder.SetSignatures(sigV2)
+ if err != nil {
+ return fmt.Errorf("failed to set signatures: %w", err)
+ }
- sigV2, err := clientTx.SignWithPrivKey(
+ sigV2, err = clientTx.SignWithPrivKey(
sdkContext.TxConfig.SignModeHandler().DefaultMode(),
signerData,
txBuilder,
n.privateKey,
sdkContext.TxConfig,
- uint64(accountSequence),
+ accountSequence,
)
if err != nil {
-
return fmt.Errorf("failed to sign tx: %w", err)
}
if err := txBuilder.SetSignatures(sigV2); err != nil {
-
return fmt.Errorf("failed to set signatures: %w", err)
}
// Generated Protobuf-encoded bytes.
txBytes, err := sdkContext.TxConfig.TxEncoder()(txBuilder.GetTx())
if err != nil {
-
return fmt.Errorf("failed to proto encode tx: %w", err)
}
diff --git a/noble/chain.go b/noble/chain.go
index bfaa5cd..d4d7753 100644
--- a/noble/chain.go
+++ b/noble/chain.go
@@ -8,11 +8,13 @@ import (
"fmt"
"sync"
- "cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cosmos"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -162,8 +164,12 @@ func (n *Noble) InitializeClients(ctx context.Context, logger log.Logger) error
return nil
}
-func (n *Noble) CloseClients() {
+func (n *Noble) CloseClients() error {
if n.cc != nil && n.cc.RPCClient.IsRunning() {
- n.cc.RPCClient.Stop()
+ err := n.cc.RPCClient.Stop()
+ if err != nil {
+ return fmt.Errorf("error stopping noble rpc client: %w", err)
+ }
}
+ return nil
}
diff --git a/noble/config.go b/noble/config.go
index d7634b7..36ad7a8 100644
--- a/noble/config.go
+++ b/noble/config.go
@@ -1,6 +1,12 @@
package noble
-import "github.com/strangelove-ventures/noble-cctp-relayer/types"
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/strangelove-ventures/noble-cctp-relayer/types"
+)
var _ types.ChainConfig = (*ChainConfig)(nil)
@@ -23,11 +29,21 @@ type ChainConfig struct {
MinMintAmount uint64 `yaml:"min-mint-amount"`
- // TODO move to keyring
MinterPrivateKey string `yaml:"minter-private-key"`
}
func (c *ChainConfig) Chain(name string) (types.Chain, error) {
+ envKey := strings.ToUpper(name) + "_PRIV_KEY"
+ privKey := os.Getenv(envKey)
+
+ if len(c.MinterPrivateKey) == 0 || len(privKey) != 0 {
+ if len(privKey) == 0 {
+ return nil, fmt.Errorf("env variable %s is empty, priv key not found for chain %s", envKey, name)
+ } else {
+ c.MinterPrivateKey = privKey
+ }
+ }
+
return NewChain(
c.RPC,
c.ChainID,
diff --git a/noble/listener.go b/noble/listener.go
index 50eddb9..e14fef1 100644
--- a/noble/listener.go
+++ b/noble/listener.go
@@ -6,6 +6,7 @@ import (
"time"
"cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/relayer"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -16,6 +17,7 @@ func (n *Noble) StartListener(
ctx context.Context,
logger log.Logger,
processingQueue chan *types.TxState,
+ flushOnlyMode bool,
flushInterval_ time.Duration,
) {
logger = logger.With("chain", n.Name(), "chain_id", n.chainID, "domain", n.Domain())
@@ -47,40 +49,42 @@ func (n *Noble) StartListener(
}
blockQueue := make(chan uint64, n.blockQueueChannelSize)
- // history
- currentBlock = currentBlock - lookback
- for currentBlock <= chainTip {
- blockQueue <- currentBlock
- currentBlock++
- }
+ if !flushOnlyMode {
+ // history
+ currentBlock -= lookback
+ for currentBlock <= chainTip {
+ blockQueue <- currentBlock
+ currentBlock++
+ }
- // listen for new blocks
- go func() {
- // inner function to queue blocks
- queueBlocks := func() {
- chainTip = n.LatestBlock()
- if chainTip >= currentBlock {
- for i := currentBlock; i <= chainTip; i++ {
- blockQueue <- i
+ // listen for new blocks
+ go func() {
+ // inner function to queue blocks
+ queueBlocks := func() {
+ chainTip = n.LatestBlock()
+ if chainTip >= currentBlock {
+ for i := currentBlock; i <= chainTip; i++ {
+ blockQueue <- i
+ }
+ currentBlock = chainTip + 1
}
- currentBlock = chainTip + 1
}
- }
- // initial queue
- queueBlocks()
-
- for {
- timer := time.NewTimer(6 * time.Second)
- select {
- case <-timer.C:
- queueBlocks()
- case <-ctx.Done():
- timer.Stop()
- return
+ // initial queue
+ queueBlocks()
+
+ for {
+ timer := time.NewTimer(6 * time.Second)
+ select {
+ case <-timer.C:
+ queueBlocks()
+ case <-ctx.Done():
+ timer.Stop()
+ return
+ }
}
- }
- }()
+ }()
+ }
// constantly query for blocks
for i := 0; i < int(n.workers); i++ {
@@ -114,19 +118,36 @@ func (n *Noble) StartListener(
}
if flushInterval > 0 {
- go n.flushMechanism(ctx, logger, blockQueue)
+ go n.flushMechanism(ctx, logger, blockQueue, flushOnlyMode)
}
<-ctx.Done()
}
+// flushMechanism looks back over the chain history every specified flushInterval.
+//
+// Each chain is configured with a lookback period which signifies how many blocks to look back
+// at each interval. The flush mechanism will start from the last flushed block and will rescan
+// the lookback period and consume all messages in that range. The flush mechanism will not flush
+// all the way to the chain's latest block to avoid consuming messages that are still in the queue.
+// There will be a minimum gap of the lookback period between the last flushed block and the latest block.
+//
+// Note: The first time the flush mechanism is run, it will set the lastFlushedBlock to the latest block
+// minus twice the lookback period.
func (n *Noble) flushMechanism(
ctx context.Context,
logger log.Logger,
blockQueue chan uint64,
+ flushOnlyMode bool,
) {
+ logger.Info(fmt.Sprintf("Starting flush mechanism. Will flush every %v", flushInterval))
- logger.Debug(fmt.Sprintf("Flush mechanism started. Will flush every %v", flushInterval))
+ // extraFlushBlocks is used to add an extra space between latest height and last flushed block
+ // this setting should only be used for the secondary, flush only relayer
+ extraFlushBlocks := uint64(0)
+ if flushOnlyMode {
+ extraFlushBlocks = 2 * n.lookbackPeriod
+ }
for {
timer := time.NewTimer(flushInterval)
@@ -145,19 +166,32 @@ func (n *Noble) flushMechanism(
continue
}
+ // initialize first lastFlushedBlock if not set
if n.lastFlushedBlock == 0 {
- n.lastFlushedBlock = latestBlock
+ n.lastFlushedBlock = latestBlock - (2*n.lookbackPeriod + extraFlushBlocks)
+
+ if latestBlock < n.lookbackPeriod {
+ n.lastFlushedBlock = 0
+ }
}
- lastFlushedBlock := n.lastFlushedBlock
- flushStart := lastFlushedBlock - n.lookbackPeriod
+ // start from the last block it flushed
+ startBlock := n.lastFlushedBlock
+
+ // set finish block to be latestBlock - lookbackPeriod
+ finishBlock := latestBlock - (n.lookbackPeriod + extraFlushBlocks)
+
+ if startBlock >= finishBlock {
+ logger.Debug("No new blocks to flush")
+ continue
+ }
- logger.Info(fmt.Sprintf("Flush started from: %d to: %d", flushStart, latestBlock))
+ logger.Info(fmt.Sprintf("Flush started from %d to %d (current height: %d, lookback period: %d)", startBlock, finishBlock, latestBlock, n.lookbackPeriod))
- for i := flushStart; i <= latestBlock; i++ {
+ for i := startBlock; i <= finishBlock; i++ {
blockQueue <- i
}
- n.lastFlushedBlock = latestBlock
+ n.lastFlushedBlock = finishBlock
logger.Info("Flush complete")
diff --git a/noble/listener_test.go b/noble/listener_test.go
index 0d886b5..45a8b8d 100644
--- a/noble/listener_test.go
+++ b/noble/listener_test.go
@@ -5,10 +5,11 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
// TODO: update test to not rely on live node with block history
@@ -26,23 +27,22 @@ func TestStartListener(t *testing.T) {
processingQueue := make(chan *types.TxState, 10000)
- go n.StartListener(ctx, a.Logger, processingQueue, 0)
+ go n.StartListener(ctx, a.Logger, processingQueue, false, 0)
time.Sleep(20 * time.Second)
tx := <-processingQueue
expectedMsg := &types.MessageState{
- IrisLookupId: "efe7cea3fd4785c3beab7f37876bdd48c5d4689c84d85a250813a2a7f01fe765",
+ IrisLookupID: "efe7cea3fd4785c3beab7f37876bdd48c5d4689c84d85a250813a2a7f01fe765",
Status: "created",
SourceDomain: 4,
DestDomain: 0,
SourceTxHash: "5002A249B1353FA59C1660EBAE5FA7FC652AC1E77F69CEF3A4533B0DF2864012",
}
- require.Equal(t, expectedMsg.IrisLookupId, tx.Msgs[0].IrisLookupId)
+ require.Equal(t, expectedMsg.IrisLookupID, tx.Msgs[0].IrisLookupID)
require.Equal(t, expectedMsg.Status, tx.Msgs[0].Status)
require.Equal(t, expectedMsg.SourceDomain, tx.Msgs[0].SourceDomain)
require.Equal(t, expectedMsg.DestDomain, tx.Msgs[0].DestDomain)
require.Equal(t, expectedMsg.SourceTxHash, tx.Msgs[0].SourceTxHash)
-
}
diff --git a/noble/message_state.go b/noble/message_state.go
index 6414b7e..20e6f4a 100644
--- a/noble/message_state.go
+++ b/noble/message_state.go
@@ -7,8 +7,10 @@ import (
"fmt"
"time"
- ctypes "github.com/cometbft/cometbft/rpc/core/types"
"github.com/ethereum/go-ethereum/crypto"
+
+ ctypes "github.com/cometbft/cometbft/rpc/core/types"
+
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)
@@ -22,7 +24,6 @@ func txToMessageState(tx *ctypes.ResultTx) ([]*types.MessageState, error) {
for _, event := range tx.TxResult.Events {
if event.Type == "circle.cctp.v1.MessageSent" {
- //fmt.Printf("Saw cctp message %s - %d:%d\n", tx., i, j)
var parsed bool
var parseErrs error
for _, attr := range event.Attributes {
@@ -31,7 +32,6 @@ func txToMessageState(tx *ctypes.ResultTx) ([]*types.MessageState, error) {
parseErrs = errors.Join(parseErrs, fmt.Errorf("failed to decode attribute key: %w", err))
}
if string(decodedKey) == "message" {
- // fmt.Printf("Saw message attribute %s - %d\n", tx.Hash, i)
decodedValue, err := base64.StdEncoding.DecodeString(attr.Value)
if err != nil {
parseErrs = errors.Join(parseErrs, fmt.Errorf("error decoding attr.value: %w", err))
@@ -59,7 +59,7 @@ func txToMessageState(tx *ctypes.ResultTx) ([]*types.MessageState, error) {
now := time.Now()
messageState := &types.MessageState{
- IrisLookupId: hashedHexStr,
+ IrisLookupID: hashedHexStr,
Status: types.Created,
SourceDomain: types.Domain(msg.SourceDomain),
DestDomain: types.Domain(msg.DestinationDomain),
@@ -82,5 +82,4 @@ func txToMessageState(tx *ctypes.ResultTx) ([]*types.MessageState, error) {
}
return messageStates, nil
-
}
diff --git a/relayer/metrics.go b/relayer/metrics.go
index 7f1281f..f9ad56b 100644
--- a/relayer/metrics.go
+++ b/relayer/metrics.go
@@ -4,6 +4,7 @@ import (
"fmt"
"log"
"net/http"
+ "time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -47,7 +48,11 @@ func InitPromMetrics(port int16) *PromMetrics {
// Expose /metrics HTTP endpoint
go func() {
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
- log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
+ server := &http.Server{
+ Addr: fmt.Sprintf(":%d", port),
+ ReadTimeout: 3 * time.Second,
+ }
+ log.Fatal(server.ListenAndServe())
}()
return m
diff --git a/test_util/setup.go b/test_util/setup.go
index 0e5d5c8..04079a3 100644
--- a/test_util/setup.go
+++ b/test_util/setup.go
@@ -1,29 +1,37 @@
package testutil
import (
- "fmt"
"os"
"testing"
"github.com/joho/godotenv"
+ "github.com/rs/zerolog"
+ "github.com/stretchr/testify/require"
+
+ "cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/cmd"
"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
- "github.com/stretchr/testify/require"
)
+var logger log.Logger
var EnvFile = os.ExpandEnv("$GOPATH/src/github.com/strangelove-ventures/noble-cctp-relayer/.env")
func init() {
+ // define logger
+ logger = log.NewLogger(os.Stdout, log.LevelOption(zerolog.ErrorLevel))
+
err := godotenv.Load(EnvFile)
if err != nil {
- fmt.Println(fmt.Errorf("error loading env file"))
+ logger.Error("error loading env file", "err", err)
os.Exit(1)
}
}
func ConfigSetup(t *testing.T) (a *cmd.AppState, registeredDomains map[types.Domain]types.Chain) {
+ t.Helper()
var testConfig = types.Config{
Chains: map[string]types.ChainConfig{
@@ -40,7 +48,7 @@ func ConfigSetup(t *testing.T) (a *cmd.AppState, registeredDomains map[types.Dom
},
},
Circle: types.CircleSettings{
- AttestationBaseUrl: "https://iris-api-sandbox.circle.com/attestations/",
+ AttestationBaseURL: "https://iris-api-sandbox.circle.com/attestations/",
FetchRetries: 0,
FetchRetryInterval: 3,
},
@@ -65,5 +73,4 @@ func ConfigSetup(t *testing.T) (a *cmd.AppState, registeredDomains map[types.Dom
}
return a, registeredDomains
-
}
diff --git a/types/chain.go b/types/chain.go
index 30851d4..92d7b7f 100644
--- a/types/chain.go
+++ b/types/chain.go
@@ -5,6 +5,7 @@ import (
"time"
"cosmossdk.io/log"
+
"github.com/strangelove-ventures/noble-cctp-relayer/relayer"
)
@@ -38,7 +39,7 @@ type Chain interface {
) error
// CloseClients is a cleanup function to close any open clients
- CloseClients()
+ CloseClients() error
// InitializeBroadcaster initializes the minter account info for the chain.
InitializeBroadcaster(
@@ -52,6 +53,7 @@ type Chain interface {
ctx context.Context,
logger log.Logger,
processingQueue chan *TxState,
+ flushOnlyMode bool,
flushInterval time.Duration,
)
diff --git a/types/config.go b/types/config.go
index dd6efd6..65af0fc 100644
--- a/types/config.go
+++ b/types/config.go
@@ -6,7 +6,7 @@ type Config struct {
Circle CircleSettings `yaml:"circle"`
ProcessorWorkerCount uint32 `yaml:"processor-worker-count"`
- Api struct {
+ API struct {
TrustedProxies []string `yaml:"trusted-proxies"`
} `yaml:"api"`
}
@@ -17,13 +17,13 @@ type ConfigWrapper struct {
Circle CircleSettings `yaml:"circle"`
ProcessorWorkerCount uint32 `yaml:"processor-worker-count"`
- Api struct {
+ API struct {
TrustedProxies []string `yaml:"trusted-proxies"`
} `yaml:"api"`
}
type CircleSettings struct {
- AttestationBaseUrl string `yaml:"attestation-base-url"`
+ AttestationBaseURL string `yaml:"attestation-base-url"`
FetchRetries int `yaml:"fetch-retries"`
FetchRetryInterval int `yaml:"fetch-retry-interval"`
}
diff --git a/types/message_state.go b/types/message_state.go
index 55fbd76..d25328c 100644
--- a/types/message_state.go
+++ b/types/message_state.go
@@ -33,7 +33,7 @@ type TxState struct {
}
type MessageState struct {
- IrisLookupId string // hex encoded MessageSent bytes
+ IrisLookupID string // hex encoded MessageSent bytes
Status string // created, pending, attested, complete, failed, filtered
Attestation string // hex encoded attestation
SourceDomain Domain // uint32 source domain id
@@ -63,7 +63,7 @@ func EvmLogToMessageState(abi abi.ABI, messageSent abi.Event, log *ethtypes.Log)
hashedHexStr := hex.EncodeToString(hashed)
messageState = &MessageState{
- IrisLookupId: hashedHexStr,
+ IrisLookupID: hashedHexStr,
Status: Created,
SourceDomain: Domain(message.SourceDomain),
DestDomain: Domain(message.DestinationDomain),
@@ -85,7 +85,7 @@ func EvmLogToMessageState(abi abi.ABI, messageSent abi.Event, log *ethtypes.Log)
// Equal checks if two MessageState instances are equal
func (m *MessageState) Equal(other *MessageState) bool {
- return (m.IrisLookupId == other.IrisLookupId &&
+ return (m.IrisLookupID == other.IrisLookupID &&
m.Status == other.Status &&
m.Attestation == other.Attestation &&
m.SourceDomain == other.SourceDomain &&
diff --git a/types/message_state_test.go b/types/message_state_test.go
index 1b83764..9752ddd 100644
--- a/types/message_state_test.go
+++ b/types/message_state_test.go
@@ -2,7 +2,6 @@ package types_test
import (
"context"
- "fmt"
"math/big"
"os"
"testing"
@@ -13,10 +12,11 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
"github.com/joho/godotenv"
"github.com/pascaldekloe/etherstream"
- testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
- "github.com/strangelove-ventures/noble-cctp-relayer/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ testutil "github.com/strangelove-ventures/noble-cctp-relayer/test_util"
+ "github.com/strangelove-ventures/noble-cctp-relayer/types"
)
// TODO: update so it doesn't rely on block history
@@ -25,15 +25,15 @@ func TestToMessageStateSuccess(t *testing.T) {
require.NoError(t, err)
messageTransmitter, err := os.Open("../cmd/ethereum/abi/MessageTransmitter.json")
- require.Nil(t, err)
+ require.NoError(t, err)
messageTransmitterABI, err := abi.JSON(messageTransmitter)
- require.Nil(t, err)
+ require.NoError(t, err)
messageSent := messageTransmitterABI.Events["MessageSent"]
ethClient, err := ethclient.DialContext(context.Background(), os.Getenv("SEPOLIA_RPC"))
- require.Nil(t, err)
+ require.NoError(t, err)
messageTransmitterAddress := common.HexToAddress("0x26413e8157CD32011E726065a5462e97dD4d03D9")
@@ -47,7 +47,7 @@ func TestToMessageStateSuccess(t *testing.T) {
etherReader := etherstream.Reader{Backend: ethClient}
_, _, history, err := etherReader.QueryWithHistory(context.Background(), &query)
- require.Nil(t, err)
+ require.NoError(t, err)
messageState, err := types.EvmLogToMessageState(messageTransmitterABI, messageSent, &history[0])
@@ -57,7 +57,7 @@ func TestToMessageStateSuccess(t *testing.T) {
rawMessageSentBytes := event["message"].([]byte)
destCaller := make([]byte, 32)
- assert.Equal(t, "e40ed0e983675678715972bd50d6abc417735051b0255f3c0916911957eda603", messageState.IrisLookupId)
+ assert.Equal(t, "e40ed0e983675678715972bd50d6abc417735051b0255f3c0916911957eda603", messageState.IrisLookupID)
assert.Equal(t, "created", messageState.Status)
assert.Equal(t, "", messageState.Attestation)
assert.Equal(t, uint32(0), messageState.SourceDomain)
@@ -67,6 +67,6 @@ func TestToMessageStateSuccess(t *testing.T) {
assert.Equal(t, rawMessageSentBytes, messageState.MsgSentBytes)
assert.Equal(t, destCaller, messageState.DestinationCaller)
assert.Equal(t, "", messageState.Channel)
- fmt.Println(messageState)
- require.Nil(t, err)
+ t.Log(messageState)
+ require.NoError(t, err)
}
diff --git a/types/state_test.go b/types/state_test.go
index c8b0f54..be5e060 100644
--- a/types/state_test.go
+++ b/types/state_test.go
@@ -12,7 +12,7 @@ func TestStateHandling(t *testing.T) {
txHash := "123456789"
msg := MessageState{
SourceTxHash: txHash,
- IrisLookupId: "123",
+ IrisLookupID: "123",
Status: Filtered,
MsgSentBytes: []byte("i like turtles"),
}
@@ -37,7 +37,7 @@ func TestStateHandling(t *testing.T) {
// even though loadedMsg is a pointer, if we add to the array, we need to re-store in cache.
msg2 := MessageState{
SourceTxHash: txHash,
- IrisLookupId: "123",
+ IrisLookupID: "123",
Status: Filtered,
MsgSentBytes: []byte("mock bytes 2"),
}
@@ -46,5 +46,5 @@ func TestStateHandling(t *testing.T) {
stateMap.Store(txHash, loadedMsg)
loadedMsg3, _ := stateMap.Load(txHash)
- require.Equal(t, 2, len(loadedMsg3.Msgs))
+ require.Len(t, loadedMsg3.Msgs, 2)
}