diff --git a/agents/agents/agentsintegration/agentsintegration_test.go b/agents/agents/agentsintegration/agentsintegration_test.go index b678591d06..9cd11bead4 100644 --- a/agents/agents/agentsintegration/agentsintegration_test.go +++ b/agents/agents/agentsintegration/agentsintegration_test.go @@ -2,11 +2,13 @@ package agentsintegration_test import ( signerConfig "github.com/synapsecns/sanguine/ethergo/signer/config" + "github.com/synapsecns/sanguine/services/scribe/backend" "math/big" "os" "testing" "time" + "github.com/Flaque/filet" awsTime "github.com/aws/smithy-go/time" "github.com/brianvoe/gofakeit/v6" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -18,12 +20,9 @@ import ( "github.com/synapsecns/sanguine/agents/config" execConfig "github.com/synapsecns/sanguine/agents/config/executor" "github.com/synapsecns/sanguine/agents/types" - "github.com/synapsecns/sanguine/services/scribe/backfill" "github.com/synapsecns/sanguine/services/scribe/client" scribeConfig "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/node" - - "github.com/Flaque/filet" + "github.com/synapsecns/sanguine/services/scribe/service" ) func RemoveAgentsTempFile(t *testing.T, fileName string) { @@ -39,11 +38,11 @@ func (u *AgentsIntegrationSuite) TestAgentsE2E() { testDone = true }() - originClient, err := backfill.DialBackend(u.GetTestContext(), u.TestBackendOrigin.RPCAddress(), u.ScribeMetrics) + originClient, err := backend.DialBackend(u.GetTestContext(), u.TestBackendOrigin.RPCAddress(), u.ScribeMetrics) u.Nil(err) - destinationClient, err := backfill.DialBackend(u.GetTestContext(), u.TestBackendDestination.RPCAddress(), u.ScribeMetrics) + destinationClient, err := backend.DialBackend(u.GetTestContext(), u.TestBackendDestination.RPCAddress(), u.ScribeMetrics) u.Nil(err) - summitClient, err := backfill.DialBackend(u.GetTestContext(), u.TestBackendSummit.RPCAddress(), u.ScribeMetrics) + summitClient, err := backend.DialBackend(u.GetTestContext(), u.TestBackendSummit.RPCAddress(), u.ScribeMetrics) u.Nil(err) originConfig := scribeConfig.ContractConfig{ @@ -55,12 +54,8 @@ func (u *AgentsIntegrationSuite) TestAgentsE2E() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: scribeConfig.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, - Contracts: []scribeConfig.ContractConfig{originConfig}, + Confirmations: 0, + Contracts: []scribeConfig.ContractConfig{originConfig}, } destinationConfig := scribeConfig.ContractConfig{ Address: u.LightInboxOnDestination.Address().String(), @@ -71,12 +66,8 @@ func (u *AgentsIntegrationSuite) TestAgentsE2E() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: scribeConfig.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, - Contracts: []scribeConfig.ContractConfig{destinationConfig}, + Confirmations: 0, + Contracts: []scribeConfig.ContractConfig{destinationConfig}, } summitConfig := scribeConfig.ContractConfig{ Address: u.InboxOnSummit.Address().String(), @@ -87,23 +78,20 @@ func (u *AgentsIntegrationSuite) TestAgentsE2E() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: scribeConfig.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, + Confirmations: 0, + Contracts: []scribeConfig.ContractConfig{summitConfig}, } scribeConfig := scribeConfig.Config{ Chains: []scribeConfig.ChainConfig{originChainConfig, destinationChainConfig, summitChainConfig}, } - clients := map[uint32][]backfill.ScribeBackend{ + clients := map[uint32][]backend.ScribeBackend{ uint32(u.TestBackendOrigin.GetChainID()): {originClient, originClient}, uint32(u.TestBackendDestination.GetChainID()): {destinationClient, destinationClient}, uint32(u.TestBackendSummit.GetChainID()): {summitClient, summitClient}, } - scribe, err := node.NewScribe(u.ScribeTestDB, clients, scribeConfig, u.ScribeMetrics) + scribe, err := service.NewScribe(u.ScribeTestDB, clients, scribeConfig, u.ScribeMetrics) u.Nil(err) scribeClient := client.NewEmbeddedScribe("sqlite", u.DBPath, u.ScribeMetrics) diff --git a/agents/agents/executor/cmd/commands.go b/agents/agents/executor/cmd/commands.go index 3a079c8d11..41c5bdd4da 100644 --- a/agents/agents/executor/cmd/commands.go +++ b/agents/agents/executor/cmd/commands.go @@ -15,10 +15,10 @@ import ( "github.com/synapsecns/sanguine/core/metrics" omnirpcClient "github.com/synapsecns/sanguine/services/omnirpc/client" scribeAPI "github.com/synapsecns/sanguine/services/scribe/api" - "github.com/synapsecns/sanguine/services/scribe/backfill" + "github.com/synapsecns/sanguine/services/scribe/backend" "github.com/synapsecns/sanguine/services/scribe/client" scribeCmd "github.com/synapsecns/sanguine/services/scribe/cmd" - "github.com/synapsecns/sanguine/services/scribe/node" + "github.com/synapsecns/sanguine/services/scribe/service" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" "golang.org/x/sync/errgroup" @@ -143,11 +143,11 @@ var ExecutorRunCommand = &cli.Command{ return fmt.Errorf("failed to initialize database: %w", err) } - scribeClients := make(map[uint32][]backfill.ScribeBackend) + scribeClients := make(map[uint32][]backend.ScribeBackend) for _, client := range executorConfig.ScribeConfig.EmbeddedScribeConfig.Chains { for confNum := 1; confNum <= scribeCmd.MaxConfirmations; confNum++ { - backendClient, err := backfill.DialBackend(ctx, fmt.Sprintf("%s/%d/rpc/%d", executorConfig.BaseOmnirpcURL, confNum, client.ChainID), handler) + backendClient, err := backend.DialBackend(ctx, fmt.Sprintf("%s/%d/rpc/%d", executorConfig.BaseOmnirpcURL, confNum, client.ChainID), handler) if err != nil { return fmt.Errorf("could not start client for %s", fmt.Sprintf("%s/1/rpc/%d", executorConfig.BaseOmnirpcURL, client.ChainID)) } @@ -156,7 +156,7 @@ var ExecutorRunCommand = &cli.Command{ } } - scribe, err := node.NewScribe(eventDB, scribeClients, executorConfig.ScribeConfig.EmbeddedScribeConfig, handler) + scribe, err := service.NewScribe(eventDB, scribeClients, executorConfig.ScribeConfig.EmbeddedScribeConfig, handler) if err != nil { return fmt.Errorf("failed to initialize scribe: %w", err) } diff --git a/agents/agents/executor/executor_test.go b/agents/agents/executor/executor_test.go index 8eef2283c4..1f21f167f0 100644 --- a/agents/agents/executor/executor_test.go +++ b/agents/agents/executor/executor_test.go @@ -1,6 +1,7 @@ package executor_test import ( + "github.com/synapsecns/sanguine/services/scribe/backend" "math/big" "time" @@ -13,10 +14,10 @@ import ( "github.com/synapsecns/sanguine/agents/types" "github.com/synapsecns/sanguine/core/merkle" agentsConfig "github.com/synapsecns/sanguine/ethergo/signer/config" - "github.com/synapsecns/sanguine/services/scribe/backfill" "github.com/synapsecns/sanguine/services/scribe/client" + "github.com/synapsecns/sanguine/services/scribe/service" + "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/node" ) func (e *ExecutorSuite) TestVerifyState() { @@ -138,23 +139,19 @@ func (e *ExecutorSuite) TestMerkleInsert() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: config.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, - Contracts: []config.ContractConfig{contractConfig}, + Confirmations: 0, + Contracts: []config.ContractConfig{contractConfig}, } scribeConfig := config.Config{ Chains: []config.ChainConfig{chainConfig}, } - simulatedClient, err := backfill.DialBackend(e.GetTestContext(), e.TestBackendOrigin.RPCAddress(), e.ScribeMetrics) + simulatedClient, err := backend.DialBackend(e.GetTestContext(), e.TestBackendOrigin.RPCAddress(), e.ScribeMetrics) e.Nil(err) - clients := map[uint32][]backfill.ScribeBackend{ + clients := map[uint32][]backend.ScribeBackend{ chainID: {simulatedClient, simulatedClient}, } - scribe, err := node.NewScribe(e.ScribeTestDB, clients, scribeConfig, e.ScribeMetrics) + scribe, err := service.NewScribe(e.ScribeTestDB, clients, scribeConfig, e.ScribeMetrics) e.Nil(err) scribeClient := client.NewEmbeddedScribe("sqlite", e.DBPath, e.ScribeMetrics) @@ -484,11 +481,11 @@ func (e *ExecutorSuite) TestExecutor() { testContractDest, _ := e.TestDeployManager.GetAgentsTestContract(e.GetTestContext(), e.TestBackendDestination) - originClient, err := backfill.DialBackend(e.GetTestContext(), e.TestBackendOrigin.RPCAddress(), e.ScribeMetrics) + originClient, err := backend.DialBackend(e.GetTestContext(), e.TestBackendOrigin.RPCAddress(), e.ScribeMetrics) e.Nil(err) - destinationClient, err := backfill.DialBackend(e.GetTestContext(), e.TestBackendDestination.RPCAddress(), e.ScribeMetrics) + destinationClient, err := backend.DialBackend(e.GetTestContext(), e.TestBackendDestination.RPCAddress(), e.ScribeMetrics) e.Nil(err) - summitClient, err := backfill.DialBackend(e.GetTestContext(), e.TestBackendSummit.RPCAddress(), e.ScribeMetrics) + summitClient, err := backend.DialBackend(e.GetTestContext(), e.TestBackendSummit.RPCAddress(), e.ScribeMetrics) e.Nil(err) originConfig := config.ContractConfig{ @@ -500,12 +497,8 @@ func (e *ExecutorSuite) TestExecutor() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: config.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, - Contracts: []config.ContractConfig{originConfig}, + Confirmations: 0, + Contracts: []config.ContractConfig{originConfig}, } destinationConfig := config.ContractConfig{ Address: e.DestinationContract.Address().String(), @@ -516,11 +509,8 @@ func (e *ExecutorSuite) TestExecutor() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: config.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, + Confirmations: 0, + Contracts: []config.ContractConfig{destinationConfig}, } summitConfig := config.ContractConfig{ @@ -532,23 +522,20 @@ func (e *ExecutorSuite) TestExecutor() { GetLogsBatchAmount: 1, StoreConcurrency: 1, GetLogsRange: 1, - ConfirmationConfig: config.ConfirmationConfig{ - RequiredConfirmations: 1, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, + Confirmations: 0, + Contracts: []config.ContractConfig{summitConfig}, } scribeConfig := config.Config{ Chains: []config.ChainConfig{originChainConfig, destinationChainConfig, summitChainConfig}, } - clients := map[uint32][]backfill.ScribeBackend{ + clients := map[uint32][]backend.ScribeBackend{ chainID: {originClient, originClient}, destination: {destinationClient, destinationClient}, summit: {summitClient, summitClient}, } - scribe, err := node.NewScribe(e.ScribeTestDB, clients, scribeConfig, e.ScribeMetrics) + scribe, err := service.NewScribe(e.ScribeTestDB, clients, scribeConfig, e.ScribeMetrics) e.Nil(err) scribeClient := client.NewEmbeddedScribe("sqlite", e.DBPath, e.ScribeMetrics) diff --git a/agents/go.mod b/agents/go.mod index 61a55054bc..48fcf3cd5d 100644 --- a/agents/go.mod +++ b/agents/go.mod @@ -26,10 +26,10 @@ require ( github.com/synapsecns/sanguine/core v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/ethergo v0.0.2 github.com/synapsecns/sanguine/services/omnirpc v0.0.0-00010101000000-000000000000 - github.com/synapsecns/sanguine/services/scribe v0.0.63 + github.com/synapsecns/sanguine/services/scribe v0.0.194 github.com/synapsecns/sanguine/tools v0.0.0-00010101000000-000000000000 github.com/ugorji/go/codec v1.2.11 - github.com/urfave/cli/v2 v2.24.4 + github.com/urfave/cli/v2 v2.25.5 github.com/vburenin/ifacemaker v1.2.0 github.com/vektra/mockery/v2 v2.14.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 @@ -49,7 +49,7 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.13.0 // indirect cloud.google.com/go/kms v1.10.1 // indirect - github.com/99designs/gqlgen v0.17.31 // indirect + github.com/99designs/gqlgen v0.17.36 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/DataDog/appsec-internal-go v1.0.0 // indirect github.com/DataDog/datadog-agent/pkg/obfuscate v0.45.0-rc.1 // indirect @@ -169,7 +169,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect - github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.1 // indirect @@ -241,6 +241,7 @@ require ( github.com/pyroscope-io/client v0.7.0 // indirect github.com/pyroscope-io/godeltaprof v0.1.0 // indirect github.com/pyroscope-io/otel-profiling-go v0.4.0 // indirect + github.com/ravilushqa/otelgqlgen v0.13.1 // indirect github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rjeczalik/notify v0.9.2 // indirect @@ -251,7 +252,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect github.com/segmentio/fasthash v1.0.3 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.8.1 // indirect @@ -277,7 +278,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 // indirect - github.com/vektah/gqlparser/v2 v2.5.1 // indirect + github.com/vektah/gqlparser/v2 v2.5.8 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -285,9 +286,10 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 // indirect + go.opentelemetry.io/contrib v1.16.1 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect @@ -307,13 +309,13 @@ require ( golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 // indirect golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.121.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/agents/go.sum b/agents/go.sum index 9651779ec5..fba202ee2e 100644 --- a/agents/go.sum +++ b/agents/go.sum @@ -66,8 +66,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= -github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158= -github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4= +github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go= +github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= @@ -141,7 +141,6 @@ github.com/Yamashou/gqlgenc v0.10.0 h1:JI4CLa9Uk2nXeKgsRkEKJEyph1ngc/jHfensl2PSZ github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -510,10 +509,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= 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-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -711,8 +708,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= -github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= +github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -802,8 +799,6 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/josephburnett/jd v1.6.1 h1:Uzqhcje4WqvVyp85F3Oj0ezISPTlnhnr/KaLZIy8qh0= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -896,7 +891,6 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/manifoldco/promptui v0.3.0/go.mod h1:zoCNXiJnyM03LlBgTsWv8mq28s7aTC71UgKasqRJHww= github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -1113,6 +1107,8 @@ github.com/pyroscope-io/godeltaprof v0.1.0 h1:UBqtjt0yZi4jTxqZmLAs34XG6ycS3vUTlh github.com/pyroscope-io/godeltaprof v0.1.0/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= github.com/pyroscope-io/otel-profiling-go v0.4.0 h1:Hk/rbUqOWoByoWy1tt4r5BX5xoKAvs5drr0511Ki8ic= github.com/pyroscope-io/otel-profiling-go v0.4.0/go.mod h1:MXaofiWU7PgLP7eISUZJYVO4Z8WYMqpkYgeP4XrPLyg= +github.com/ravilushqa/otelgqlgen v0.13.1 h1:V+zFE75iDd2/CSzy5kKnb+Fi09SsE5535wv9U2nUEFE= +github.com/ravilushqa/otelgqlgen v0.13.1/go.mod h1:ZIyWykK2paCuNi9k8gk5edcNSwDJuxZaW90vZXpafxw= github.com/rbretecher/go-postman-collection v0.9.0 h1:vXw6KBhASpz0L0igH3OsJCx5pjKbWXn9RiYMMnOO4QQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= @@ -1164,8 +1160,8 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1278,8 +1274,8 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVH github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= @@ -1288,8 +1284,8 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vburenin/ifacemaker v1.2.0 h1:jREjCJ8RgTZuH5EYWB0/1ZHdTpJVqhMBU87XIUeX+2I= github.com/vburenin/ifacemaker v1.2.0/go.mod h1:oZwuhpbmYD8SjjofPhscHVmYxNtRLdczDCslWrb/q2w= -github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4= -github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= +github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= +github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= github.com/vektra/mockery/v2 v2.14.0 h1:KZ1p5Hrn8tiY+LErRMr14HHle6khxo+JKOXLBW/yfqs= github.com/vektra/mockery/v2 v2.14.0/go.mod h1:bnD1T8tExSgPD1ripLkDbr60JA9VtQeu12P3wgLZd7M= github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k= @@ -1335,14 +1331,16 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib v1.16.1 h1:EpASvVyGx6/ZTlmXzxYfTMZxHROelCeXXa2uLiwltcs= +go.opentelemetry.io/contrib v1.16.1/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= @@ -1484,8 +1482,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1793,8 +1791,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/contrib/git-changes-action/go.mod b/contrib/git-changes-action/go.mod index 3f844ac6ce..57e6257dfa 100644 --- a/contrib/git-changes-action/go.mod +++ b/contrib/git-changes-action/go.mod @@ -23,7 +23,7 @@ require ( github.com/vishalkuo/bimap v0.0.0-20220726225509-e0b4f20de28b github.com/xlab/treeprint v1.1.0 golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 - golang.org/x/mod v0.9.0 + golang.org/x/mod v0.10.0 golang.org/x/oauth2 v0.7.0 ) @@ -56,7 +56,7 @@ require ( github.com/pjbgf/sha1cd v0.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/sethvargo/go-envconfig v0.8.0 // indirect github.com/skeema/knownhosts v1.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect @@ -69,7 +69,7 @@ require ( golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/contrib/git-changes-action/go.sum b/contrib/git-changes-action/go.sum index 117d83bb8f..bdd3d8d538 100644 --- a/contrib/git-changes-action/go.sum +++ b/contrib/git-changes-action/go.sum @@ -239,8 +239,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sethvargo/go-envconfig v0.8.0 h1:AcmdAewSFAc7pQ1Ghz+vhZkilUtxX559QlDuLLiSkdI= github.com/sethvargo/go-envconfig v0.8.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= github.com/sethvargo/go-githubactions v1.1.0 h1:mg03w+b+/s5SMS298/2G6tHv8P0w0VhUFaqL1THIqzY= @@ -340,8 +340,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -521,8 +521,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= @@ -632,6 +632,7 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/contrib/promexporter/dashboards/bridges.json b/contrib/promexporter/dashboards/bridges.json index d514943283..c4efb73b72 100644 --- a/contrib/promexporter/dashboards/bridges.json +++ b/contrib/promexporter/dashboards/bridges.json @@ -1,41 +1,4 @@ { - "__inputs": [ - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__elements": {}, - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "10.0.2" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "table", - "name": "Table", - "version": "" - }, - { - "type": "panel", - "id": "timeseries", - "name": "Time series", - "version": "" - } - ], "annotations": { "list": [ { @@ -55,18 +18,234 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": null, + "id": 189, "links": [], "liveNow": false, "panels": [ { - "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, + "id": 13, + "title": "Omnirpc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "eq", + "reducer": "lastNotNull", + "value": 0 + } + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "0": { + "color": "dark-red", + "index": 0, + "text": "Not Found" + } + }, + "type": "value" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 12, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "block_age", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{chain_id}}", + "range": false, + "refId": "A" + } + ], + "title": "Block Age", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "labelsToFields": false, + "mode": "seriesToRows", + "reducers": [ + "lastNotNull" + ] + } + }, + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "Field" + } + ], + "fields": {} + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Field": "Chain ID", + "Last *": "Block Age" + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Block Age": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Chain ID": { + "aggregations": [], + "operation": "groupby" + }, + "Gas Balance (Ether)": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "Block Age (lastNotNull)" + } + ] + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Gas Balance (Ether) (lastNotNull)": "Gas Balance (Ether)" + } + } + }, + { + "id": "extractFields", + "options": { + "format": "kvp", + "keepTime": false, + "replace": false, + "source": "Chain ID" + } + }, + { + "id": "rowsToFields", + "options": { + "mappings": [ + { + "fieldName": "Chain ID", + "handlerKey": "field.name" + }, + { + "fieldName": "Block Age (lastNotNull)", + "handlerKey": "field.value" + } + ] + } + } + ], + "type": "bargauge" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, "id": 5, "panels": [], "title": "Overview", @@ -75,7 +254,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "description": "", "fieldConfig": { @@ -108,10 +287,10 @@ "overrides": [] }, "gridPos": { - "h": 8, + "h": 10, "w": 12, "x": 0, - "y": 1 + "y": 10 }, "id": 1, "options": { @@ -132,7 +311,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "exemplar": false, @@ -225,7 +404,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "fieldConfig": { "defaults": { @@ -283,7 +462,7 @@ "h": 8, "w": 12, "x": 12, - "y": 1 + "y": 10 }, "id": 2, "options": { @@ -302,7 +481,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "expr": "avg by(chain_id) (nonce{eoa_address=\"0x230A1AC45690B9Ae1176389434610B9526d2f21b\"})", @@ -316,13 +495,82 @@ "transformations": [], "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 14, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "editorMode": "builder", + "expr": "pending_bridges", + "instant": false, + "legendFormat": "{{chain_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Pending Bridges (explorer)", + "transformations": [], + "type": "bargauge" + }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 9 + "y": 28 }, "id": 3, "panels": [], @@ -332,7 +580,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "fieldConfig": { "defaults": { @@ -390,7 +638,7 @@ "h": 8, "w": 12, "x": 0, - "y": 10 + "y": 29 }, "id": 4, "options": { @@ -409,7 +657,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "expr": "avg by(chain_name) (dfk_pending_heroes)", @@ -425,7 +673,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "description": "", "fieldConfig": { @@ -461,7 +709,7 @@ "h": 8, "w": 12, "x": 12, - "y": 10 + "y": 29 }, "id": 6, "options": { @@ -478,7 +726,7 @@ "sortBy": [ { "desc": true, - "displayName": "Gas Balance (Ether)" + "displayName": "Chain ID" } ] }, @@ -487,11 +735,11 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "exemplar": false, - "expr": "gas_balance{name=\"messenger\"}", + "expr": "gas_balance{eoa_address=\"0xAA920f7b9039e556d2442113f1fd339e4927Dd9A\"}", "instant": false, "interval": "", "legendFormat": "{{chain_id}}", @@ -580,7 +828,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "fieldConfig": { "defaults": { @@ -638,7 +886,7 @@ "h": 8, "w": 12, "x": 0, - "y": 18 + "y": 37 }, "id": 8, "options": { @@ -657,7 +905,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "expr": "avg by(chain_id) (nonce{name=\"messenger\"})", @@ -677,7 +925,7 @@ "h": 1, "w": 24, "x": 0, - "y": 26 + "y": 45 }, "id": 7, "panels": [], @@ -687,7 +935,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "description": "", "fieldConfig": { @@ -723,7 +971,7 @@ "h": 8, "w": 12, "x": 0, - "y": 27 + "y": 46 }, "id": 9, "options": { @@ -744,7 +992,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "exemplar": false, @@ -837,7 +1085,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "fieldConfig": { "defaults": { @@ -895,7 +1143,7 @@ "h": 8, "w": 12, "x": 12, - "y": 27 + "y": 46 }, "id": 10, "options": { @@ -914,7 +1162,7 @@ { "datasource": { "type": "prometheus", - "uid": "${DS_PROMETHEUS}" + "uid": "prometheus" }, "editorMode": "builder", "expr": "avg by(chain_id) (nonce{name=\"cctp\"})", @@ -937,13 +1185,13 @@ "list": [] }, "time": { - "from": "now-15m", + "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Bridge", "uid": "e79ee84d-73cb-4645-a630-a34df143184b", - "version": 8, + "version": 15, "weekStart": "" } diff --git a/contrib/promexporter/exporters/exporter.go b/contrib/promexporter/exporters/exporter.go index a46d8caba2..c6d115e4e9 100644 --- a/contrib/promexporter/exporters/exporter.go +++ b/contrib/promexporter/exporters/exporter.go @@ -23,6 +23,7 @@ import ( "math/big" "net" "net/http" + "os" "time" ) @@ -52,9 +53,12 @@ type exporter struct { // StartExporterServer starts the exporter server. func StartExporterServer(ctx context.Context, handler metrics.Handler, cfg config.Config) error { + // the main server serves metrics since this is only a prom exporter + _ = os.Setenv(metrics.MetricsPortEnabledEnv, "false") + router := ginhelper.New(logger) router.Use(handler.Gin()) - router.GET(ginhelper.MetricsEndpoint, gin.WrapH(handler.Handler())) + router.GET(metrics.MetricsPathDefault, gin.WrapH(handler.Handler())) var lc net.ListenConfig listener, err := lc.Listen(ctx, "tcp", fmt.Sprintf(":%d", cfg.Port)) diff --git a/contrib/promexporter/go.mod b/contrib/promexporter/go.mod index 031703c2d1..abec311489 100644 --- a/contrib/promexporter/go.mod +++ b/contrib/promexporter/go.mod @@ -16,7 +16,7 @@ replace ( ) require ( - github.com/99designs/gqlgen v0.17.31 + github.com/99designs/gqlgen v0.17.36 github.com/Yamashou/gqlgenc v0.10.0 github.com/creasty/defaults v1.7.0 github.com/ethereum/go-ethereum v1.10.26 @@ -30,7 +30,7 @@ require ( github.com/synapsecns/sanguine/core v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/services/explorer v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/services/omnirpc v0.0.0-00010101000000-000000000000 - github.com/urfave/cli/v2 v2.24.4 + github.com/urfave/cli/v2 v2.25.5 go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel/metric v1.16.0 go.opentelemetry.io/otel/trace v1.16.0 @@ -122,7 +122,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect - github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.1 // indirect @@ -191,7 +191,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -217,14 +217,14 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 // indirect - github.com/vektah/gqlparser/v2 v2.5.1 // indirect + github.com/vektah/gqlparser/v2 v2.5.8 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opentelemetry.io/contrib v1.16.1 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect @@ -242,12 +242,12 @@ require ( golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/contrib/promexporter/go.sum b/contrib/promexporter/go.sum index 75db67ab62..6d40e62952 100644 --- a/contrib/promexporter/go.sum +++ b/contrib/promexporter/go.sum @@ -1,4 +1,3 @@ -bitbucket.org/tentontrain/math v0.0.0-20220519191623-a4e86beba92a h1:6QCkYok6wNGonv0ya01Ay5uV8zT412p4wm2stFZsUQM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -55,8 +54,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= -github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158= -github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4= +github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go= +github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= @@ -128,7 +127,6 @@ github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNu github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -172,7 +170,6 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad h1:kXfVkP8xPSJXzicomzjECcw6tv1Wl9h1lNenWBfNKdg= github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad/go.mod h1:r5ZalvRl3tXevRNJkwIB6DC4DD3DMjIlY9NEU1XGoaQ= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -432,10 +429,8 @@ github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dT github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 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-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -619,8 +614,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= -github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= +github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -702,8 +697,6 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/josephburnett/jd v1.6.1 h1:Uzqhcje4WqvVyp85F3Oj0ezISPTlnhnr/KaLZIy8qh0= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -780,7 +773,6 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/manifoldco/promptui v0.3.0/go.mod h1:zoCNXiJnyM03LlBgTsWv8mq28s7aTC71UgKasqRJHww= github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -1031,8 +1023,8 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1146,16 +1138,16 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVH github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4= -github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= +github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= +github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= @@ -1193,13 +1185,13 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib v1.16.1 h1:EpASvVyGx6/ZTlmXzxYfTMZxHROelCeXXa2uLiwltcs= go.opentelemetry.io/contrib v1.16.1/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= @@ -1335,8 +1327,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1629,8 +1621,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/contrib/terraform-provider-helmproxy/go.mod b/contrib/terraform-provider-helmproxy/go.mod index 49b95bf2d0..de1fba94d0 100644 --- a/contrib/terraform-provider-helmproxy/go.mod +++ b/contrib/terraform-provider-helmproxy/go.mod @@ -184,7 +184,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect golang.org/x/crypto v0.9.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.3.0 // indirect @@ -192,7 +192,7 @@ require ( golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.121.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/contrib/terraform-provider-helmproxy/go.sum b/contrib/terraform-provider-helmproxy/go.sum index 7f5f9e4d1e..c7d80d3ced 100644 --- a/contrib/terraform-provider-helmproxy/go.sum +++ b/contrib/terraform-provider-helmproxy/go.sum @@ -1056,7 +1056,7 @@ github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdk github.com/securego/gosec/v2 v2.7.0/go.mod h1:xNbGArrGUspJLuz3LS5XCY1EBW/0vABAl/LWfSklmiM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1313,8 +1313,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1636,8 +1636,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/contrib/terraform-provider-iap/go.mod b/contrib/terraform-provider-iap/go.mod index bf39858b9e..d6a49af80b 100644 --- a/contrib/terraform-provider-iap/go.mod +++ b/contrib/terraform-provider-iap/go.mod @@ -89,13 +89,13 @@ require ( github.com/zclconf/go-cty v1.12.1 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/crypto v0.9.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.121.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/contrib/terraform-provider-iap/go.sum b/contrib/terraform-provider-iap/go.sum index b38e051243..fe9c5c8f0b 100644 --- a/contrib/terraform-provider-iap/go.sum +++ b/contrib/terraform-provider-iap/go.sum @@ -763,7 +763,7 @@ github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdk github.com/securego/gosec/v2 v2.7.0/go.mod h1:xNbGArrGUspJLuz3LS5XCY1EBW/0vABAl/LWfSklmiM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -960,8 +960,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1261,8 +1261,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/contrib/terraform-provider-kubeproxy/go.mod b/contrib/terraform-provider-kubeproxy/go.mod index 97e4711f76..ff2bd63dba 100644 --- a/contrib/terraform-provider-kubeproxy/go.mod +++ b/contrib/terraform-provider-kubeproxy/go.mod @@ -18,8 +18,8 @@ require ( github.com/synapsecns/sanguine/contrib/tfcore v0.0.0-00010101000000-000000000000 github.com/zclconf/go-cty v1.12.1 golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 - golang.org/x/mod v0.9.0 - golang.org/x/tools v0.7.0 + golang.org/x/mod v0.10.0 + golang.org/x/tools v0.9.3 google.golang.org/grpc v1.55.0 k8s.io/apiextensions-apiserver v0.25.5 k8s.io/apimachinery v0.25.5 diff --git a/contrib/terraform-provider-kubeproxy/go.sum b/contrib/terraform-provider-kubeproxy/go.sum index f9581efa28..22caa856f5 100644 --- a/contrib/terraform-provider-kubeproxy/go.sum +++ b/contrib/terraform-provider-kubeproxy/go.sum @@ -953,7 +953,7 @@ github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdk github.com/securego/gosec/v2 v2.7.0/go.mod h1:xNbGArrGUspJLuz3LS5XCY1EBW/0vABAl/LWfSklmiM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -1205,8 +1205,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1534,8 +1534,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/contrib/tfcore/go.mod b/contrib/tfcore/go.mod index 925f3e84d6..0a33e08cae 100644 --- a/contrib/tfcore/go.mod +++ b/contrib/tfcore/go.mod @@ -21,11 +21,11 @@ require ( github.com/mitchellh/hashstructure v1.1.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/stretchr/testify v1.8.4 - golang.org/x/mod v0.9.0 + golang.org/x/mod v0.10.0 golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.7.0 golang.org/x/sync v0.3.0 - golang.org/x/tools v0.7.0 + golang.org/x/tools v0.9.3 google.golang.org/api v0.121.0 google.golang.org/grpc v1.55.0 ) @@ -87,6 +87,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/contrib/tfcore/go.sum b/contrib/tfcore/go.sum index 90f24b0f79..64f3f91c35 100644 --- a/contrib/tfcore/go.sum +++ b/contrib/tfcore/go.sum @@ -746,7 +746,8 @@ github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdk github.com/securego/gosec/v2 v2.7.0/go.mod h1:xNbGArrGUspJLuz3LS5XCY1EBW/0vABAl/LWfSklmiM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -943,8 +944,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1203,8 +1204,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2-0.20210512205948-8287d5da45e4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/core/bytes_test.go b/core/bytes_test.go new file mode 100644 index 0000000000..691fd441cf --- /dev/null +++ b/core/bytes_test.go @@ -0,0 +1,35 @@ +package core_test + +import ( + "github.com/synapsecns/sanguine/core" + "reflect" + "testing" +) + +func TestBytesToSlice(t *testing.T) { + tests := []struct { + name string + bytes [32]byte + want []byte + }{ + { + name: "all zeros", + bytes: [32]byte{}, + want: make([]byte, 32), + }, + { + name: "random bytes", + bytes: [32]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + want: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + if got := core.BytesToSlice(tt.bytes); !reflect.DeepEqual(got, tt.want) { + t.Errorf("BytesToSlice() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/core/ginhelper/server.go b/core/ginhelper/server.go index 5fa593fbe2..e755b53afe 100644 --- a/core/ginhelper/server.go +++ b/core/ginhelper/server.go @@ -27,7 +27,6 @@ var robots []byte // - cors (used for requests from the frontend) // - health-checks // - restrictive robots.txt. -// TODO: optionally include metrics. func New(logger *log.ZapEventLogger) *gin.Engine { server := gin.New() // required for opentracing. @@ -101,4 +100,5 @@ const RequestIDHeader = "X-Request-ID" var bootTime = time.Now() // MetricsEndpoint is used for prometheus metrics. +// Deprecated: use METRICS_PATH instead. const MetricsEndpoint string = "/metrics" diff --git a/core/go.mod b/core/go.mod index 824431dcd0..8244356887 100644 --- a/core/go.mod +++ b/core/go.mod @@ -39,10 +39,10 @@ require ( github.com/stretchr/testify v1.8.4 github.com/temoto/robotstxt v1.1.2 github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 - github.com/urfave/cli/v2 v2.24.4 - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 + github.com/urfave/cli/v2 v2.25.5 + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel/exporters/jaeger v1.14.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 @@ -159,7 +159,7 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/skeema/knownhosts v1.1.0 // indirect @@ -184,13 +184,13 @@ require ( go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/core/go.sum b/core/go.sum index c41ab80999..674cc416c9 100644 --- a/core/go.sum +++ b/core/go.sum @@ -501,8 +501,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOa github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA= github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -556,8 +556,8 @@ github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21/go.mod h1:bI63nwuxN0y github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVHXwQesKK0+JSwnHE/1c7fgic= github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -584,12 +584,12 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= @@ -697,8 +697,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -922,8 +922,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/core/metrics/README.md b/core/metrics/README.md index 8deb46e23b..1e1ad7170c 100644 --- a/core/metrics/README.md +++ b/core/metrics/README.md @@ -25,3 +25,15 @@ Pass in the `JAEGER_ENDPOINT` enviornment variable ## Pyroscope Pass in the `PYROSCOPE_ENDPOINT` environment variable + +## Metrics Endpoint + +The metrics endpoint is exposed on `/metrics` on port `8080` by default and is compatible with prometheus. The following options control the metrics endpoint: + +| Enviornment Variable | Description | Default | +|------------------------|-----------------------------------------------|------------| +| `METRICS_PORT_ENABLED` | Wether or not to enable the metrics endpoint. | `true` | +| `METRICS_PORT` | Port to serve metrics on. | `8080` | +| `METRICS_PATH` | Path to serve metrics on | `/metrics` | + +**Note: this server failing to bind to `METRICS_PORT` will not cause the application to fail to start. The error will be logged.** diff --git a/core/metrics/base.go b/core/metrics/base.go index 0a1296c625..1f932fede4 100644 --- a/core/metrics/base.go +++ b/core/metrics/base.go @@ -5,8 +5,11 @@ import ( "fmt" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/synapsecns/sanguine/core" "github.com/synapsecns/sanguine/core/config" + "github.com/synapsecns/sanguine/core/ginhelper" "github.com/synapsecns/sanguine/core/metrics/internal" + baseServer "github.com/synapsecns/sanguine/core/server" "github.com/uptrace/opentelemetry-go-extra/otelgorm" "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -73,9 +76,51 @@ func (b *baseHandler) Start(ctx context.Context) error { _ = b.meter.Shutdown(ctx) }() + go func() { + b.startMetricsServer(ctx) + }() + return nil } +const ( + // MetricsPortEnabledEnv is the environment variable that controls whether the metrics server is enabled. + MetricsPortEnabledEnv = "METRICS_PORT_ENABLED" + metricsPortEnv = "METRICS_PORT" + // MetricsPath is the environment variable that controls the path for the metrics server. + MetricsPath = "METRICS_PATH" + metricsPortDefault = 8080 + // MetricsPathDefault is the default path for the metrics server. + MetricsPathDefault = "/metrics" +) + +// startMetricsServer starts the metrics server on the given port. +// this should be run in a separate goroutine. +func (b *baseHandler) startMetricsServer(ctx context.Context) { + if !core.GetEnvBool(MetricsPortEnabledEnv, true) { + return + } + + port := core.GetEnvInt(metricsPortEnv, metricsPortDefault) + path := core.GetEnv(MetricsPath, MetricsPathDefault) + + logger.Infof("starting metrics server on port %d at path %s", port, path) + + // create the metrics server + server := ginhelper.New(logger) + // note: this is a global setter, so it will affect all gin servers. + // this is probably not wise, but a better workaround is required. + gin.SetMode(gin.ReleaseMode) + server.Use(b.Gin()) + server.GET(path, gin.WrapH(b.handler)) + + connection := baseServer.Server{} + err := connection.ListenAndServe(ctx, fmt.Sprintf(":%d", port), server) + if err != nil { + logger.Warnf("running metrics server failed: %v", err) + } +} + func (b *baseHandler) Gin() gin.HandlerFunc { return otelgin.Middleware(b.name, otelgin.WithTracerProvider(b.tp), otelgin.WithPropagators(b.propagator)) } diff --git a/core/os.go b/core/os.go index 3829455e53..34cafa9b6c 100644 --- a/core/os.go +++ b/core/os.go @@ -26,6 +26,19 @@ func GetEnv(name, defaultVal string) string { return val } +// GetEnvBool gets an environment variable as a bool. If not found the default value is used. +func GetEnvBool(name string, defaultVal bool) bool { + val := os.Getenv(name) + if val == name { + return defaultVal + } + res, err := strconv.ParseBool(val) + if err != nil { + return defaultVal + } + return res +} + // HasEnv checks if an environment variable is set. func HasEnv(name string) bool { val := os.Getenv(name) diff --git a/core/os_test.go b/core/os_test.go index 5c064dc9d9..b0ee034075 100644 --- a/core/os_test.go +++ b/core/os_test.go @@ -5,6 +5,7 @@ import ( . "github.com/stretchr/testify/assert" common "github.com/synapsecns/sanguine/core" "os" + "testing" ) // TestGetEnv makes sure that default variables are set/fetched. @@ -41,3 +42,71 @@ func (c *CoreSuite) TestGetEnvInt() { func (c *CoreSuite) TestIsTest() { True(c.T(), common.IsTest()) } + +func TestGetEnvBool(t *testing.T) { + type args struct { + name string + defaultVal bool + } + tests := []struct { + name string + args args + want bool + envVal string + setupEnv bool + }{ + { + name: "Environment variable not set", + args: args{ + name: "NOT_SET", + defaultVal: true, + }, + want: true, + setupEnv: false, + }, + { + name: "Environment variable set to true", + args: args{ + name: "SET_TRUE", + defaultVal: false, + }, + want: true, + envVal: "true", + setupEnv: true, + }, + { + name: "Environment variable set to false", + args: args{ + name: "SET_FALSE", + defaultVal: true, + }, + want: false, + envVal: "false", + setupEnv: true, + }, + { + name: "Environment variable set to non-boolean", + args: args{ + name: "SET_NON_BOOLEAN", + defaultVal: true, + }, + want: true, + envVal: "non-boolean", + setupEnv: true, + }, + } + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + if tt.setupEnv { + t.Setenv(tt.args.name, tt.envVal) + } else { + _ = os.Unsetenv(tt.args.name) + } + + if got := common.GetEnvBool(tt.args.name, tt.args.defaultVal); got != tt.want { + t.Errorf("GetEnvBool() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/core/retry/retry.go b/core/retry/retry.go index 682edae5a4..3e752e070d 100644 --- a/core/retry/retry.go +++ b/core/retry/retry.go @@ -68,10 +68,11 @@ func WithMaxAttemptsTime(maxAttemptTime time.Duration) WithBackoffConfigurator { func defaultConfig() retryWithBackoffConfig { return retryWithBackoffConfig{ - factor: 2, - jitter: true, - min: 200 * time.Millisecond, - max: 5 * time.Second, + factor: 2, + jitter: true, + min: 200 * time.Millisecond, + max: 5 * time.Second, + // TODO: default to negative, do not enforce a max when negative maxAttempts: 3, } } diff --git a/ethergo/backends/mocks/simulated_test_backend.go b/ethergo/backends/mocks/simulated_test_backend.go index f67c99ddd3..202a004a8e 100644 --- a/ethergo/backends/mocks/simulated_test_backend.go +++ b/ethergo/backends/mocks/simulated_test_backend.go @@ -296,13 +296,13 @@ func (_m *SimulatedTestBackend) ClientID() string { return r0 } -// CodeAt provides a mock function with given fields: ctx, contract, blockNumber -func (_m *SimulatedTestBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, contract, blockNumber) +// CodeAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *SimulatedTestBackend) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, blockNumber) var r0 []byte if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { - r0 = rf(ctx, contract, blockNumber) + r0 = rf(ctx, account, blockNumber) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) @@ -311,7 +311,7 @@ func (_m *SimulatedTestBackend) CodeAt(ctx context.Context, contract common.Addr var r1 error if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, contract, blockNumber) + r1 = rf(ctx, account, blockNumber) } else { r1 = ret.Error(1) } diff --git a/ethergo/chain/mocks/chain.go b/ethergo/chain/mocks/chain.go index 98e9a8fff8..829b9b6a8c 100644 --- a/ethergo/chain/mocks/chain.go +++ b/ethergo/chain/mocks/chain.go @@ -263,13 +263,13 @@ func (_m *Chain) ClientID() string { return r0 } -// CodeAt provides a mock function with given fields: ctx, contract, blockNumber -func (_m *Chain) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, contract, blockNumber) +// CodeAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *Chain) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, blockNumber) var r0 []byte if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { - r0 = rf(ctx, contract, blockNumber) + r0 = rf(ctx, account, blockNumber) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) @@ -278,7 +278,7 @@ func (_m *Chain) CodeAt(ctx context.Context, contract common.Address, blockNumbe var r1 error if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, contract, blockNumber) + r1 = rf(ctx, account, blockNumber) } else { r1 = ret.Error(1) } diff --git a/ethergo/go.mod b/ethergo/go.mod index 663f769919..0a324e870a 100644 --- a/ethergo/go.mod +++ b/ethergo/go.mod @@ -113,6 +113,7 @@ require ( github.com/cloudflare/circl v1.1.0 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e // indirect github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect @@ -131,12 +132,16 @@ require ( github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/gin-contrib/cors v1.4.0 // indirect + github.com/gin-contrib/requestid v0.0.6 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-contrib/zap v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.0 // indirect github.com/go-git/go-git/v5 v5.5.2 // indirect + github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -233,7 +238,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/skeema/knownhosts v1.1.0 // indirect @@ -253,7 +258,7 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 // indirect - github.com/urfave/cli/v2 v2.24.4 // indirect + github.com/urfave/cli/v2 v2.25.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -261,9 +266,9 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect @@ -278,12 +283,12 @@ require ( go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/ethergo/go.sum b/ethergo/go.sum index 6b71208c54..e00884b621 100644 --- a/ethergo/go.sum +++ b/ethergo/go.sum @@ -203,6 +203,7 @@ github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad h1:kXfVkP8xPSJXzicomzjECcw6tv1Wl9h1lNenWBfNKdg= github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad/go.mod h1:r5ZalvRl3tXevRNJkwIB6DC4DD3DMjIlY9NEU1XGoaQ= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -306,6 +307,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= +github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -408,8 +411,15 @@ github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= +github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +github.com/gin-contrib/requestid v0.0.6 h1:mGcxTnHQ45F6QU5HQRgQUDsAfHprD3P7g2uZ4cSZo9o= +github.com/gin-contrib/requestid v0.0.6/go.mod h1:9i4vKATX/CdggbkY252dPVasgVucy/ggBeELXuQztm4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-contrib/zap v0.1.0 h1:RMSFFJo34XZogV62OgOzvrlaMNmXrNxmJ3bFmMwl6Cc= +github.com/gin-contrib/zap v0.1.0/go.mod h1:hvnZaPs478H1PGvRP8w89ZZbyJUiyip4ddiI/53WG3o= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -435,6 +445,8 @@ github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4B github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -445,6 +457,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -454,11 +467,15 @@ 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-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 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-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= @@ -471,6 +488,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me 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.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -775,6 +793,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -785,6 +804,7 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 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.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -947,6 +967,7 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= @@ -962,6 +983,7 @@ github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0je github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -1027,6 +1049,8 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -1051,8 +1075,8 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1129,6 +1153,7 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/teivah/onecontext v1.3.0 h1:tbikMhAlo6VhAuEGCvhc8HlTnpX4xTNPTOseWuhO1J0= github.com/teivah/onecontext v1.3.0/go.mod h1:hoW1nmdPVK/0jrvGtcx8sCKYs2PiS4z0zzfdeuEVyb0= +github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/tenderly/tenderly-cli v1.4.6 h1:l27YYmtJIZjrhXNyreTp6X6UKyPcgkAIlEZV2/Lq+cU= @@ -1150,6 +1175,8 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 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/uptrace/opentelemetry-go-extra/otelgorm v0.1.21 h1:PsmFQCoiULTVpXqFb2S/3E7WbA9ev6CkKFejJt2SFB0= @@ -1158,8 +1185,8 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVH github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -1206,13 +1233,14 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= +go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel/exporters/jaeger v1.14.0 h1:CjbUNd4iN2hHmWekmOqZ+zSCU+dzZppG8XsV+A3oc8Q= @@ -1234,6 +1262,7 @@ go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= +go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1246,6 +1275,7 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1285,6 +1315,7 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1342,8 +1373,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1642,8 +1673,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= @@ -1813,6 +1844,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/DataDog/dd-trace-go.v1 v1.52.0 h1:9tzXTBnx/KX/fcPw096+z342qXoe+5OC1DFJ8rzytM0= diff --git a/go.work.sum b/go.work.sum index 61af8181b9..9d7957993c 100644 --- a/go.work.sum +++ b/go.work.sum @@ -249,853 +249,49 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.4 h1:ksUxwH3OD5sxkjzEqGxNTl+ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= github.com/99designs/gqlgen v0.16.0/go.mod h1:nbeSjFkqphIqpZsYe1ULVz0yfH8hjpJdJIQoX/e0G2I= -github.com/99designs/gqlgen v0.17.16/go.mod h1:dnJdUkgfh8iw8CEx2hhTdgTQO/GvVWKLcm/kult5gwI= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc= github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 h1:qoVeMsc9/fh/yhxVaA0obYjVH/oI/ihrOoMwsLS9KSA= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM= github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 h1:E+m3SkZCN0Bf5q7YdTs5lSm2CYY3CK4spn5OmUIiQtk= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4= github.com/Azure/azure-storage-blob-go v0.7.0 h1:MuueVOYkufCxJw5YZzF842DY2MBsp+hLuh2apKY0mck= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= -github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFor3D/HDsvBME35Xy9rwW9DecL+M2sNw1ybjPtwA0= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.43.0/go.mod h1:VVMDDibJxYEkwcLdZBT2g8EHKpbMT4JdOhRbQ9GdjbM= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= -github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= -github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= -github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= -github.com/Masterminds/vcs v1.13.3 h1:IIA2aBdXvfbIM+yl/eTnL4hb1XwdpvuQLglAix1gweE= -github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= -github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.22.0/go.mod h1:lm3THZ8reqBDBQKQyb5HB3sY1lKp3grEbQ81aWSgPp4= -github.com/Shopify/sarama v1.23.1 h1:XxJBCZEoWJtoWjf/xRbmGUpAmTZGnuuF0ON0EvxxBrs= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= -github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= -github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6 h1:1d9pzdbkth4D9AX6ndKSl7of3UTV0RYl3z64U2dXMGo= -github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= -github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= -github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= -github.com/alecthomas/kingpin/v2 v2.3.1 h1:ANLJcKmQm4nIaog7xdr/id6FM6zm5hHnfZrvtKPxqGg= -github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae h1:C4Q9m+oXOxcSWwYk9XzzafY2xAVAaeubZbUHJkw3PlY= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= -github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f h1:NNJE6p4LchkmNfNskDUaSbrwxZzr7t2/lj2aS+q4oF0= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed h1:ue9pVfIcP+QMEjfgo/Ez4ZjNZfonGgR6NgjMaJMu1Cg= -github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db h1:nxAtV4VajJDhKysp2kdcJZsq8Ss1xSA0vZTkVHHJd0E= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= -github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0= -github.com/aristanetworks/fsnotify v1.4.2 h1:it2ydpY6k0aXB7qjb4vGhOYOL6YDC/sr8vhqwokFQwQ= -github.com/aristanetworks/glog v0.0.0-20180419172825-c15b03b3054f h1:Gj+4e4j6g8zOhckHfGbZnpa0k8yDrc0XRmiyQj2jzlU= -github.com/aristanetworks/goarista v0.0.0-20190924011532-60b7b74727fd h1:2gXWYquahfk3RfmyLuMk47NCaf+1FFQ95FNM+HZN3Oo= -github.com/aristanetworks/splunk-hec-go v0.3.3 h1:O7zlcm4ve7JvqTyEK3vSBh1LngLezraqcxv8Ya6tQFY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= -github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= -github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/ashanbrown/forbidigo v1.1.0 h1:SJOPJyqsrVL3CvR0veFZFmIM0fXS/Kvyikqvfphd0Z4= -github.com/ashanbrown/makezero v0.0.0-20210308000810-4155955488a0 h1:27owMIbvO33XL56BKWPy+SCU69I9wPwPXuMf5mAbVGU= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.24 h1:zsg+5ouVLLbePknVZlUMm1ptwyQLkjjLMWnN+kVs5dA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.24/go.mod h1:+fFaIjycTmpV6hjmPTbyU9Kp5MI/lA+bbibcAtmlhYA= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 h1:w/fPGB0t5rWwA43mux4e9ozFSH5zF1moQemlA131PWc= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.4 h1:0PlAM5X9Tbjr9OpQh3uVIwIbm3kxJpPculFAZQB2u8M= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.4/go.mod h1:2XzQIYZ2VeZzxUnFIe0EpYIdkol6eEgs3vSAFjTLw4Q= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.93.2 h1:c6a19AjfhEXKlEX63cnlWtSQ4nzENihHZOG0I3wH6BE= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.93.2/go.mod h1:VX22JN3HQXDtQ3uS4h4TtM+K11vydq58tpHTlsm8TL8= -github.com/aws/aws-sdk-go-v2/service/eventbridge v1.18.9 h1:ZRs58K4BH5u8Zzvsy0z9yZlhYW7BsbyUXEsDjy+wZVg= -github.com/aws/aws-sdk-go-v2/service/eventbridge v1.18.9/go.mod h1:eQx2HIMJsUQhEXStHzwtbTOcCKUsmWKgJwowhahrEZE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.27 h1:qIw7Hg5eJEc1uSxg3hRwAthPAO7NeOd4dPxhaTi0yB0= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.27/go.mod h1:Zz0kvhcSlu3NX4XJkaGgdjaa+u7a9LYuy8JKxA5v3RM= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.26 h1:XsLNgECTon/ughUzILFbbeC953tTbXnJv4GQPUHm80A= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.26/go.mod h1:zSW1SZ9ZQQZlRfqur2sI2Mn/ptcDLi6mtlPaXIIw0IE= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1 h1:lRWp3bNu5wy0X3a8GS42JvZFlv++AKsMdzEnoiVJrkg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1/go.mod h1:VXBHSxdN46bsJrkniN68psSwbyBKsazQfU2yX/iSDso= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.17.10 h1:bfR+hoEQD1vokNTV1JxSmmaBskT4yI/iF1SjvAYzbvA= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.17.10/go.mod h1:hj0KX0oXSiPyVhjYUqZvC02ElFlp47fe5srakVIVDNU= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1 h1:cKr6St+CtC3/dl/rEBJvlk7A/IN5D5F02GNkGzfbtVU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.32.0 h1:NAc8WQsVQ3+kz3rU619mlz8NcbpZI6FVJHQfH33QK0g= -github.com/aws/aws-sdk-go-v2/service/s3 v1.32.0/go.mod h1:aSl9/LJltSz1cVusiR/Mu8tvI4Sv/5w/WWrJmmkNii0= -github.com/aws/aws-sdk-go-v2/service/sfn v1.17.9 h1:u6nKx6nKoDrWVpeLqwMFs2eC4Emn2Fjm+2iZ3+qJQYY= -github.com/aws/aws-sdk-go-v2/service/sfn v1.17.9/go.mod h1:kXJNJcl+dIeh3Hz6XvzzoOVWHjB0lyZHYnxXquHmsa0= -github.com/aws/aws-sdk-go-v2/service/sns v1.20.8 h1:wy1jYAot40/Odzpzeq9S3OfSddJJ5RmpaKujvj5Hz7k= -github.com/aws/aws-sdk-go-v2/service/sns v1.20.8/go.mod h1:HmCFGnmh0Tx4Onh9xUklrVhNcCsBTeDx4n53WGhp+oY= -github.com/aws/aws-sdk-go-v2/service/sqs v1.20.8 h1:SDZBYFUp70hI2T0z9z+KD1iJBz9jGeT7xgU5hPPC9zs= -github.com/aws/aws-sdk-go-v2/service/sqs v1.20.8/go.mod h1:w058QQWcK1MLEnIrD0DmkQtSvC1pLY0EWRQsPXPWppM= -github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= -github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= -github.com/bketelsen/crypt v0.0.4 h1:w/jqZtC9YD4DS/Vp9GhWfWcCpuAL58oTnLoI8vE9YHU= -github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40 h1:y4B3+GPxKlrigF1ha5FFErxK+sr6sWxQovRMzwMhejo= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= -github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM= -github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw= -github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= -github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= -github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= -github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= -github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72 h1:fUmDBbSvv1uOzo/t8WaxZMVb7BxJ8JECo5lGoR9c5bA= -github.com/bwesterb/go-ristretto v1.2.0 h1:xxWOVbN5m8NNKiSDZXE1jtZvZnC6JSJ9cYFADiZcWtw= -github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= -github.com/celo-org/celo-blockchain v0.0.0-20210222234634-f8c8f6744526 h1:rdY1F8vUybjjsv+V58eaSYsYPTNO+AXK9o7l+BQuhhU= -github.com/celo-org/celo-bls-go v0.2.4 h1:V1y92kM5IRJWQZ6DCwqiKLW7swmUA5y/dPJ9YbU4HfA= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= -github.com/charithe/durationcheck v0.0.6 h1:Tsy7EppNow2pDC0jN7Hsmcb6mHd71ZbI1vFissRBtc0= -github.com/chavacava/garif v0.0.0-20210405163807-87a70f3d418b h1:StHNkfM8nXnNQnk5/0uYYhIqvvENd14hoHPnZsakTNo= -github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= -github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc= -github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= -github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= -github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= -github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 h1:58f1tJ1ra+zFINPlwLWvQsR9CzAKt2e+EWV2yX9oXQ4= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= -github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= -github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= -github.com/coinbase/kryptology v1.8.0 h1:Aoq4gdTsJhSU3lNWsD5BWmFSz2pE0GlmrljaOxepdYY= -github.com/confluentinc/confluent-kafka-go v1.4.0 h1:GCEMecax8zLZsCVn1cea7Y1uR/lRCdCDednpkc0NLsY= -github.com/confluentinc/confluent-kafka-go v1.4.0/go.mod h1:u2zNLny2xq+5rWeTQjFHbDzzNuba4P1vo31r9r4uAdg= -github.com/confluentinc/confluent-kafka-go/v2 v2.1.1 h1:qwZtgyGS4OjvebR4TkZPxHAQRN/IbdaxpCQyhDpxeaE= -github.com/confluentinc/confluent-kafka-go/v2 v2.1.1/go.mod h1:mfGzHbxQ6LRc25qqaLotDHkhdYmeZQ3ctcKNlPUjDW4= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572 h1:+R8G1+Ftumd0DaveLgMIjrFPcAS4G8MsVXWXiyZL5BY= -github.com/consensys/gnark-crypto v0.5.3 h1:4xLFGZR3NWEH2zy+YzvzHicpToQR8FXFbfLNvpGB+rE= -github.com/containerd/aufs v1.0.0 h1:2oeJiwX5HstO7shSrPZjrohJZLzK36wvpdmzDRkL/LY= -github.com/containerd/btrfs v1.0.0 h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5GrnA= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU= -github.com/containerd/go-cni v1.1.6 h1:el5WPymG5nRRLQF1EfB97FWob4Tdc8INg8RZMaXWZlo= -github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0= -github.com/containerd/imgcrypt v1.1.4 h1:iKTstFebwy3Ak5UF0RHSeuCTahC5OIrPJa6vjMAM81s= -github.com/containerd/nri v0.1.0 h1:6QioHRlThlKh2RkRTR4kIT3PKAcrLo3gIWnjkM4dQmQ= -github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI= -github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= -github.com/containerd/zfs v1.0.0 h1:cXLJbx+4Jj7rNsTiqVfm6i+RNLx6FFA2fMmDlEf+Wm8= -github.com/containernetworking/cni v1.1.1 h1:ky20T7c0MvKvbMOwS/FrlbNwjEoqJEUUYfsL4b0mc4k= -github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE= -github.com/containers/ocicrypt v1.1.3 h1:uMxn2wTb4nDR7GqG3rnZSfpJXqWURfzZ7nKydzIeKpA= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= -github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= -github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= -github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a h1:W8b4lQ4tFF21aspRGoBuCNV6V2fFJBF+pm1J6OY8Lys= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/creachadair/staticfile v0.1.2 h1:QG0u27/Ietu0UVOk1aMbF6jrWrEzPIdZP4ju3c1PPfY= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c h1:/ovYnF02fwL0kvspmy9AuyKg1JhdTRUgPw4nUxd9oZM= -github.com/daixiang0/gci v0.2.8 h1:1mrIGMBQsBu0P7j7m1M8Lb+ZeZxsZL+jyGX4YoMJJpg= -github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= -github.com/dave/jennifer v1.2.0 h1:S15ZkFMRoJ36mGAQgWL1tnr0NQJh9rZ8qatseX/VbBc= -github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c= -github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= -github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7qg9dX7pc218= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI= -github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8 h1:akOQj8IVgoeFfBTzGOEQakCYshWD6RNo1M5pivFXt70= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= -github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ= -github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw= -github.com/dmarkham/enumer v1.5.5 h1:LpOGL3PQTPOM87rgowZEf7Z5EmkgnKqUtS92Vo+vqzs= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= -github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf h1:Yt+4K30SdjOkRoRRm3vYNQgR+/ZIy0RmeUDZo7Y8zeQ= -github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI= -github.com/dynamicgo/go-config v1.0.0 h1:iY97zNL+b3ds6IKddlFLIBMWPomnwTYxnFtnu5rDuAE= -github.com/dynamicgo/xerrors v0.0.0-20190219051451-ec7525ce5de1 h1:bp3Xehls+lEKwcD2uaTXR8qgpSzkfCLuqKYOIOEG2TM= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= -github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= -github.com/elastic/elastic-transport-go/v8 v8.1.0 h1:NeqEz1ty4RQz+TVbUrpSU7pZ48XkzGWQj02k5koahIE= -github.com/elastic/elastic-transport-go/v8 v8.1.0/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= -github.com/elastic/go-elasticsearch/v6 v6.8.5 h1:U2HtkBseC1FNBmDr0TR2tKltL6FxoY+niDAlj5M8TK8= -github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= -github.com/elastic/go-elasticsearch/v7 v7.17.1 h1:49mHcHx7lpCL8cW1aioEwSEVKQF3s+Igi4Ye/QTWwmk= -github.com/elastic/go-elasticsearch/v7 v7.17.1/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4= -github.com/elastic/go-elasticsearch/v8 v8.4.0 h1:Rn1mcqaIMcNT43hnx2H62cIFZ+B6mjWtzj85BDKrvCE= -github.com/elastic/go-elasticsearch/v8 v8.4.0/go.mod h1:yY52i2Vj0unLz+N3Nwx1gM5LXwoj3h2dgptNGBYkMLA= -github.com/elastic/gosigar v0.10.5 h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo= github.com/emicklei/go-restful v2.16.0+incompatible h1:rgqiKNjTnFQA6kkhFe16D8epTksy9HQ1MyrbDXSdYhM= -github.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.11.0 h1:jtLewhRR2vMRNnq2ZZUoCjUlgut+Y0+sDDWPOfwOi1o= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/protoc-gen-validate v0.10.0 h1:oIfnZFdC0YhpNNEX+SuIqko4cqqVZeN9IGTrhZje83Y= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/esimonov/ifshort v1.0.2 h1:K5s1W2fGfkoWXsFlxBNqT6J0ZCncPaKrGM5qe0bni68= -github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c h1:CndMRAH4JIwxbW8KYq6Q+cGWcGHz0FjGR3QqcInWcW0= -github.com/flowstack/go-jsonschema v0.1.1 h1:dCrjGJRXIlbDsLAgTJZTjhwUJnnxVWl1OgNyYh5nyDc= -github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e h1:Ss/B3/5wWRh8+emnK0++g5zQzwDTi30W10pKxKc4JXI= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90 h1:WXb3TSNmHp2vHoCroCIB1foO/yQ36swABL8aOVeDpgg= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/fullstorydev/grpcurl v1.6.0 h1:p8BB6VZF8O7w6MxGr3KJ9E6EVKaswCevSALK6FBtMzA= -github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= -github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= -github.com/garyburd/redigo v1.6.3 h1:HCeeRluvAgMusMomi1+6Y5dmFOdYV/JzoRrrbFlkGIc= -github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw= -github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd h1:r04MMPyLHj/QwZuMJ5+7tJcBr1AQjpiAK/rZWRrQT7o= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= -github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-chi/chi v1.5.0 h1:2ZcJZozJ+rj6BA0c19ffBUGXEKAT/aOLOtQjD46vBRA= -github.com/go-chi/chi v1.5.0/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= -github.com/go-chi/chi/v5 v5.0.0 h1:DBPx88FjZJH3FsICfDAfIfnb7XxKIYVGG6lOPlhENAg= -github.com/go-critic/go-critic v0.5.6 h1:siUR1+322iVikWXoV75I1YRfNaC/yaLzhdF9Zwd8Tus= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-pg/pg/v10 v10.11.0 h1:CMKJqLgTrfpE/aOVeLdybezR2om071Vh38OLZjsyMI0= -github.com/go-pg/pg/v10 v10.11.0/go.mod h1:4BpHRoxE61y4Onpof3x1a2SQvi9c+q1dJnrNdMjsroA= -github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU= -github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= +github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= -github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-redis/redis/v7 v7.1.0 h1:I4C4a8UGbFejiVjtYVTRVOiMIJ5pm5Yru6ibvDX/OS0= -github.com/go-redis/redis/v7 v7.1.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= -github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= -github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= -github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21 h1:wP6mXeB2V/d1P1K7bZ5vDUO3YqEzcvOREOxZPEu3gVI= -github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= -github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg= -github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= -github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= -github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= -github.com/gocql/gocql v0.0.0-20220224095938-0eacd3183625 h1:6ImvI6U901e1ezn/8u2z3bh1DZIvMOia0yTSBxhy4Ao= -github.com/gocql/gocql v0.0.0-20220224095938-0eacd3183625/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= -github.com/godror/godror v0.24.2 h1:uxGAD7UdnNGjX5gf4NnEIGw0JAPTIFiqAyRBZTPKwXs= -github.com/gofiber/fiber/v2 v2.24.0 h1:18rpLoQMJBVlLtX/PwgHj3hIxPSeWfN1YeDJ2lEnzjU= -github.com/gofiber/fiber/v2 v2.24.0/go.mod h1:MR1usVH3JHYRyQwMe2eZXRSZHRX38fkV+A7CPB+DlDQ= -github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= -github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec h1:lJwO/92dFXWeXOZdoGXgptLmNLwynMSHUmU6besqtiw= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= -github.com/golangci/golangci-lint v1.40.1 h1:pBrCqt9BgI9LfGCTKRTSe1DfMjR6BkOPERPaXJYXA6Q= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= -github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo= -github.com/golangci/revgrep v0.0.0-20210208091834-cd28932614b5 h1:c9Mqqrm/Clj5biNaG7rABrmwUq88nHh0uABo2b/WYmc= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e h1:KhcknUwkWHKZPbFy2P7jH5LKJ3La+0ZeknkkmrSgqb0= -github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= -github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8= -github.com/google/certificate-transparency-go v1.1.1 h1:6JHXZhXEvilMcTjR4MGZn5KV0IRkcFl4CJx5iHVhjFE= -github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= -github.com/google/trillian v1.3.11 h1:pPzJPkK06mvXId1LHEAJxIegGgHzzp/FUnycPYfoCMI= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= -github.com/gookit/color v1.3.8 h1:w2WcSwaCa1ojRWO60Mm4GJUJomBNKR9G+x9DwaaCL1c= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254 h1:Nb2aRlC404yz7gQIfRZxX9/MLvQiqXyiBTJtgAy6yrI= -github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gostaticanalysis/analysisutil v0.4.1 h1:/7clKqrVfiVwiBQLM0Uke4KvXnO6JcCTS7HwF2D6wG8= -github.com/gostaticanalysis/comment v1.4.1 h1:xHopR5L2lRz6OsjH4R2HG5wRhW9ySl3FsHIvi5pcXwc= -github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5 h1:rx8127mFPqXXsfPSo8BwnIU97MKFZc89WHAHt8PwDVY= -github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/go-getter v1.5.0 h1:ciWJaeZWSMbc5OiLMpKp40MKFPqO44i0h3uyfXPBkkk= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= -github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= -github.com/hashicorp/memberlist v0.1.6/go.mod h1:5VDNHjqFMgEcclnwmkCnC99IPwxBmIsxwY8qn+Nl0H4= -github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA= -github.com/hashicorp/serf v0.8.6/go.mod h1:P/AVgr4UHsUYqVHG1y9eFhz8S35pqhGhLZaDpfGKIMo= -github.com/hashicorp/serf v0.9.7 h1:hkdgbqizGQHuU5IPqYM1JdSMV8nKfpuOnZYXssk9muY= -github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY= -github.com/hashicorp/vault/api v1.1.0 h1:QcxC7FuqEl0sZaIjcXB/kNEeBa0DH5z57qbWBvZwLC4= -github.com/hashicorp/vault/api v1.1.0/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk= -github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267 h1:e1ok06zGrWJW91rzRroyl5nRNqraaBe4d5hiKcVZuHM= -github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= -github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff h1:LeVKjw8pcDQj7WVVnbFvbD7ovcv+r/l15ka1NH6Lswc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150 h1:vlNjIqmUZ9CMAWsbURYl3a6wZbw7q5RHVvlXTNS/Bs8= -github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= -github.com/ianlancetaylor/demangle v0.0.0-20220517205856-0058ec4f073c h1:rwmN+hgiyp8QyBqzdEX43lTjKAxaqCrYHaU5op5P9J8= -github.com/ianlancetaylor/demangle v0.0.0-20220517205856-0058ec4f073c/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/influxdata/flux v0.65.1 h1:77BcVUCzvN5HMm8+j9PRBQ4iZcu98Dl4Y9rf+J5vhnc= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385 h1:ED4e5Cc3z5vSN2Tz2GkOHN7vs4Sxe2yds6CXvDnvZFE= -github.com/influxdata/promql/v2 v2.12.0 h1:kXn3p0D7zPw16rOtfDR+wo6aaiH8tSMfhPwONTxrlEc= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6 h1:UzJnB7VRL4PSkUJHwsyzseGOmrO/r4yA+AuxGJxiZmA= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9 h1:MHTrDWmQpHq/hkq+7cw9oYAt2PqUw52TZazRA0N7PGE= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368 h1:+TUUmaFa4YD1Q+7bH9o5NCHQGPMqZCYJiNW6lIIS9z4= -github.com/intel/goresctrl v0.2.0 h1:JyZjdMQu9Kl/wLXe9xA6s1X+tF6BWsQPFGJMEeCfWzE= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk= -github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= -github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= -github.com/jackc/puddle v1.2.2-0.20220404125616-4e959849469a h1:oH7y/b+q2BEerCnARr/HZc1NxOYbKSJor4MqQXlhh+s= -github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels= -github.com/jackpal/gateway v1.0.7 h1:7tIFeCGmpyrMx9qvT0EgYUi7cxVW48a0mMvnIL17bPM= -github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jgautheron/goconst v1.4.0 h1:hp9XKUpe/MPyDamUbfsrGpe+3dnY2whNK4EtB86dvLM= -github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg= -github.com/jhump/goprotoc v0.5.0 h1:Y1UgUX+txUznfqcGdDef8ZOVlyQvnV0pKWZH08RmZuo= -github.com/jingyugao/rowserrcheck v0.0.0-20210315055705-d907ca737bb1 h1:4Rlb26NqzNtbDH69CRpr0vZooj3jAlXTycWCX3xRYAY= -github.com/jinzhu/gorm v1.9.10 h1:HvrsqdhCW78xpJF67g1hMxS6eCToo9PZH4LDB8WKPac= -github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY= -github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= -github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= -github.com/jsternberg/zap-logfmt v1.0.0 h1:0Dz2s/eturmdUS34GM82JwNEdQ9hPoJgqptcEKcbpzY= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= -github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d h1:XeSMXURZPtUffuWAaq90o6kLgZdgu+QA8wk4MPC8ikI= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef h1:2jNeR4YUziVtswNP9sEFAI913cVrzH85T+8Q6LpYbT0= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= -github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= -github.com/kevinmbeaulieu/eq-go v1.0.0 h1:AQgYHURDOmnVJ62jnEk0W/7yFKEn+Lv8RHN6t7mB0Zo= -github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba h1:NARVGAAgEXvoMeNPHhPFt1SBt1VMznA3Gnz9d0qj+co= -github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6RlY= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 h1:KAZ1BW2TCmT6PRihDPpocIy1QTtsAsrx6TneU/4+CMg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada h1:3L+neHp83cTjegPdCiOxVOJtRIy7/8RldvMTsyPYH10= -github.com/klauspost/reedsolomon v1.9.2 h1:E9CMS2Pqbv+C7tsrYad4YC9MfhnMVWhMRsTi7U0UB18= -github.com/kortschak/utter v1.0.1 h1:AJVccwLrdrikvkH0aI5JKlzZIORLpfMeGBQ5tHfIXis= -github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= -github.com/kulti/thelper v0.4.0 h1:2Nx7XbdbE/BYZeoip2mURKUdtHQRuy6Ug+wR7K9ywNM= -github.com/kunwardeep/paralleltest v1.0.2 h1:/jJRv0TiqPoEy/Y8dQxCFJhD56uS/pnvtatgTZBHokU= -github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M= +github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= -github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY= -github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= -github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/ldez/gomoddirectives v0.2.1 h1:9pAcW9KRZW7HQjFwbozNvFMcNVwdCBufU7os5QUwLIY= -github.com/ldez/tagliatelle v0.2.0 h1:693V8Bf1NdShJ8eu/s84QySA0J2VWBanVBa2WwXD/Wk= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libs4go/bcf4go v0.0.17 h1:PzA0xC67L8yDjXwd7Cy9N5cR0GmDmE4FPDkHOW1Qa1U= -github.com/libs4go/fixed v0.0.4 h1:gJEnJ7MfzLwCcKf2jf7jd48iQvcd5fsXRk+lS/Md7T4= -github.com/libs4go/scf4go v0.0.1 h1:KYpHjom3+rqg1jGQ/yBmtN8mgup7pwwlZCZ9jHQf0v4= -github.com/libs4go/sdi4go v0.0.6 h1:s662OqbB3QK9dl8c55NINn925ptSwm2xqVGNxgsc4xM= -github.com/libs4go/slf4go v0.0.4 h1:TEnFk5yVZWeR6q56SxacOUWRarhvdzw850FikXnw6XM= -github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= -github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4= -github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77 h1:6xiz3+ZczT3M4+I+JLpcPGG1bQKm8067HktB17EDWEE= -github.com/lyft/protoc-gen-star v0.5.3 h1:zSGLzsUew8RT+ZKPHc3jnf8XLaVyHzTcAFBzHtCNR20= -github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ= -github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA= -github.com/matryer/moq v0.2.3/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE= -github.com/matryer/moq v0.2.7 h1:RtpiPUM8L7ZSCbSwK+QcZH/E9tgqAkFjKQxsRs25b4w= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d h1:oNAwILwmgWKFpuU+dXvI6dl9jG2mAWAZLX3r9s0PPiw= +github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= +github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-oci8 v0.1.1 h1:aEUDxNAyDG0tv8CA3TArnDQNyc4EhnWlsfxRgDHABHM= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/goveralls v0.0.2 h1:7eJB6EqsPhRVxvwEXGnqdO2sJI0PTsrWoTMXEk9/OQc= -github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= -github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 h1:QASJXOGm2RZ5Ardbc86qNFvby9AqkLDibfChMtAg5QM= -github.com/mgechev/revive v1.0.6 h1:MgRQ3ys2uQCyVjelaDhVs8oSvOPYInzGA/nNGMa+MNU= -github.com/microsoft/go-mssqldb v0.19.0/go.mod h1:ukJCBnnzLzpVF0qYRT+eg1e+eSwjeQ7IvenUv8QPook= -github.com/microsoft/go-mssqldb v0.21.0 h1:p2rpHIL7TlSv1QrbXJUAcbyRKnIT0C9rRkH2E4OjLn8= -github.com/microsoft/go-mssqldb v0.21.0/go.mod h1:+4wZTUnz/SV6nffv+RRRB/ss8jPng5Sho2SmM1l2ts4= -github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= -github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA= -github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= -github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615 h1:/mD+ABZyXD39BzJI2XyRJlqdZG11gXFo0SSynL+OFeU= -github.com/moby/sys/signal v0.6.0 h1:aDpY94H8VlhTGa9sNYUFCFsMZIUh5wm0B6XkIoJj/iY= -github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4= -github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1 h1:29NKShH4TWd3lxCDUhS4Xe16EWMA753dtIxYtwddklU= -github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880 h1:DXaIt8v4XXkFoVZXkG/PjLS5Rz3I2yoflOQrnuGgJeA= -github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= -github.com/mwitkow/go-proto-validators v0.2.0 h1:F6LFfmgVnfULfaRsQWBbe7F7ocuHCr9+7m+GAeDzNbQ= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/nakabonne/nestif v0.3.0 h1:+yOViDGhg8ygGrmII72nV9B/zGxY188TYpfolntsaPw= -github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= -github.com/nats-io/jwt/v2 v2.0.3 h1:i/O6cmIsjpcQyWDYNcq2JyZ3/VTF8SJ4JWluI5OhpvI= -github.com/nats-io/nats-server/v2 v2.5.0 h1:wsnVaaXH9VRSg+A2MVg5Q727/CqxnmPLGFQ3YZYKTQg= -github.com/nats-io/nats.go v1.12.1 h1:+0ndxwUPz3CmQ2vjbXdkC1fo3FdiOQDim4gl3Mge8Qo= -github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= -github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= -github.com/neilotoole/errgroup v0.1.6 h1:PODGqPXdT5BC/zCYIMoTrwV+ujKcW+gBXM6Ye9Ve3R8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/nishanths/exhaustive v0.1.0 h1:kVlMw8h2LHPMGUVqUj6230oQjjTMFjwcZrnkhXzFfl8= -github.com/nishanths/predeclared v0.2.1 h1:1TXtjmy4f3YCFjTxRd8zcFHOmoUir+gp0ESzjFzG2sw= -github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= -github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c h1:a380JP+B7xlMbEQOlha1buKhzBPXFqgFXplyWCEIGEY= -github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696 h1:yHCGAHg2zMaW8olLrqEt3SAHGcEx2aJPEQWMRCyravY= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= -github.com/opencontainers/selinux v1.10.1 h1:09LIPVRP3uuZGQvgR+SgMSNBd1Eb3vlRbGqQpoHsF8w= -github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU= github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= -github.com/paulbellamy/ratecounter v0.2.0 h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK/zFXVJGs= -github.com/paulmach/protoscan v0.2.1 h1:rM0FpcTjUMvPUNk2BhPJrreDKetq43ChnL+x1sRg8O8= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ulk9xVsepYy9ZY= -github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= -github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= -github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= -github.com/polyfloyd/go-errorlint v0.0.0-20210418123303-74da32850375 h1:uuOfAQo7em74dKh41UzjlQ6dXmE9wYxjvUcfg2EHTDw= -github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= -github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/pseudomuto/protoc-gen-doc v1.3.2 h1:61vWZuxYa8D7Rn4h+2dgoTNqnluBmJya2MgbqO32z6g= -github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c h1:JoUA0uz9U0FVFq5p4LjEq4C0VgQ0El320s3Ms0V4eww= -github.com/quasilyte/go-ruleguard v0.3.4 h1:F6l5p6+7WBcTKS7foNQ4wqA39zjn2+RbdbyzGxIq1B0= -github.com/quasilyte/go-ruleguard/dsl v0.3.2 h1:ULi3SLXvDUgb0u2IM5xU6er9KeWBSaUh1NlDjCgLHU8= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88 h1:PeTrJiH/dSeruL/Z9Db39NRMwI/yoA3oHCdCkg+Wh8A= -github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= -github.com/redis/go-redis/v9 v9.0.0 h1:r2ctp2J2+TcXTVIyPU6++FniED/Nyo4SDMKvLtpszx0= -github.com/redis/go-redis/v9 v9.0.0/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52 h1:RnWNS9Hlm8BIkjr6wx8li5abe0fr73jljLycdfemTp0= -github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20= -github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= -github.com/ryancurrah/gomodguard v1.2.0 h1:YWfhGOrXwLGiqcC/u5EqG6YeS8nh+1fw0HEc85CVZro= -github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/sagikazarmark/crypt v0.6.0 h1:REOEXCs/NFY/1jOCEouMuT4zEniE5YoXbvpC5X/TLF8= -github.com/sanposhiho/wastedassign v1.0.0 h1:dB+7OV0iJ5b0SpGwKjKlPCr8GDZJX6Ylm3YG+66xGpc= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= -github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= -github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= -github.com/securego/gosec/v2 v2.7.0 h1:mOhJv5w6UyNLpSssQOQCc7eGkKLuicAxvf66Ey/X4xk= -github.com/segmentio/kafka-go v0.4.29 h1:4ujULpikzHG0HqKhjumDghFjy/0RRCSl/7lbriwQAH0= -github.com/segmentio/kafka-go v0.4.29/go.mod h1:m1lXeqJtIFYZayv0shM/tjrAFljvWLTprxBHd+3PnaU= -github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= -github.com/shirou/gopsutil/v3 v3.21.4 h1:XB/+p+kVnyYLuPHCfa99lxz2aJyvVhnyd+FxZqH/k7M= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY= -github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= -github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ= -github.com/ssgreg/nlreturn/v2 v2.1.0 h1:6/s4Rc49L6Uo6RLjhWZGBpWWjfzk2yrf1nIW8m4wgVA= -github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= -github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8= -github.com/summerwind/h2spec v2.2.1+incompatible h1:Ex8kpG4LjIeudEtfbM892Os2PawIZBsEvukHJcvZHho= -github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 h1:m+8fKfQwCAy1QjzINvKe/pYtLjo2dl59x2w9YSEJxuY= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= -github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck= -github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b h1:HxLVTlqcHhFAz3nWUcuvpH7WuOMv8LQoCWmruLfFH2U= -github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= -github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM= -github.com/tetafro/godot v1.4.6 h1:NCglcF0Ct5vVUeRJVsUz9TPKyxkE/lKv7QYJfjxRuvw= -github.com/tidwall/btree v0.3.0/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= -github.com/tidwall/btree v1.1.0 h1:5P+9WU8ui5uhmcg3SoPyTwoI0mVyZ1nps7YQzTZFkYM= -github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= -github.com/tidwall/buntdb v1.2.0 h1:8KOzf5Gg97DoCMSOgcwZjnM0FfROtq0fcZkPW54oGKU= -github.com/tidwall/buntdb v1.2.0/go.mod h1:XLza/dhlwzO6dc5o/KWor4kfZSt3BP8QV+77ZMKfI58= -github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= -github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= -github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo= -github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/grect v0.1.0/go.mod h1:sa5O42oP6jWfTShL9ka6Sgmg3TgIK649veZe05B7+J8= -github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= -github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= -github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= -github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= -github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= -github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= -github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94 h1:ig99OeTyDwQWhPe2iw9lwfQVF1KB3Q4fpP3X7/2VBG8= -github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= -github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= -github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/tomarrell/wrapcheck/v2 v2.1.0 h1:LTzwrYlgBUwi9JldazhbJN84fN9nS2UNGrZIo2syqxE= -github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= -github.com/tommy-muehle/go-mnd/v2 v2.3.2 h1:SLkFtxVVkoypCu6eTERr5U2IC3Kce/zOhA4IyNesPV4= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= -github.com/twitchtv/twirp v8.1.1+incompatible h1:s5WnVKMhC4Xz1jOfNAqTg85iguOWAvsrCJoPiezlLFA= -github.com/twitchtv/twirp v8.1.1+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= -github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= -github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg= +github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs= -github.com/valyala/fasthttp v1.31.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= -github.com/valyala/quicktemplate v1.6.3 h1:O7EuMwuH7Q94U2CXD6sOX8AYHqQqWtmIk690IhmpkKA= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/vektah/gqlparser/v2 v2.2.0/go.mod h1:i3mQIGIrbK2PD1RrCeMTlVbkF2FJ6WkU1KJlJlC+3F4= -github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8 h1:EVObHAr8DqpoJCVv6KYTle8FEImKhtkfcZetNqxDoJQ= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 h1:+UB2BJA852UkGH42H+Oee69djmxS3ANzl2b/JtT1YiA= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94= -github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= -github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo= -github.com/willf/bitset v1.1.3 h1:ekJIKh6+YbUIVt9DfNbkR5d6aFcFTLDRyJNAACURBg8= -github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= -github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= -github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= -github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= -github.com/xtaci/kcp-go v5.4.5+incompatible h1:CdPonwNu3RKu7HcXSno5r0GXfTViDY2iFV2RDOao/4U= -github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= -github.com/yeya24/promlinter v0.1.0 h1:goWULN0jH5Yajmu/K+v1xCqIREeB+48OiJ2uu2ssc7U= -github.com/yookoala/realpath v1.0.0 h1:7OA9pj4FZd+oZDsyvXWQvjn5oBdcHRTV44PpdMSuImQ= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= -github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= -github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= -github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c h1:/RwRVN9EdXAVtdHxP7Ndn/tfmM9/goiwU0QTnLBgS4w= -go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= -go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= -go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao= -go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= -go.etcd.io/etcd/pkg/v3 v3.5.4 h1:V5Dvl7S39ZDwjkKqJG2BfXgxZ3QREqqKifWQgIw5IM0= -go.etcd.io/etcd/raft/v3 v3.5.4 h1:YGrnAgRfgXloBNuqa+oBI/aRZMcK/1GS6trJePJ/Gqc= -go.etcd.io/etcd/server/v3 v3.5.4 h1:CMAZd0g8Bn5NRhynW6pKhc4FRg41/0QYy3d7aNm9874= -go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403 h1:rKyWXYDfrVOpMFBion4Pmx5sJbQreQNXycHvm4KwJSg= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= -go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -golang.org/dl v0.0.0-20190829154251-82a15e2f2ead h1:jeP6FgaSLNTMP+Yri3qjlACywQLye+huGLmNGhBzm6k= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/mobile v0.0.0-20200801112145-973feb4309de h1:OVJ6QQUBAesB8CZijKDSsXX7xYVtUhrkY0gwMfbi4p4= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -gonum.org/v1/gonum v0.6.0 h1:DJy6UzXbahnGUf1ujUNkh/NEtK14qMo2nvlBPs4U5yw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b h1:Qh4dB5D/WpoUUp3lSod7qgoyEHbDGPUWjIbnqdqqe1k= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a h1:stTHdEoWg1pQ8riaP5ROrjS6zy6wewH/Q2iwnLCQUXY= -gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= -gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= -gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= -gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= -gopkg.in/jcmturner/gokrb5.v7 v7.2.3 h1:hHMV/yKPwMnJhPuPx7pH2Uw/3Qyf+thJYlisUc44010= -gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= -gopkg.in/jinzhu/gorm.v1 v1.9.1 h1:63D1Sk0C0mhCbK930D0PkD3nKT8wLxz6lLPh5V6D2hM= -gopkg.in/jinzhu/gorm.v1 v1.9.1/go.mod h1:56JJPUzbikvTVnoyP1nppSkbJ2L8sunqTBDY2fDrmFg= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= -gopkg.in/olivere/elastic.v3 v3.0.75 h1:u3B8p1VlHF3yNLVOlhIWFT3F1ICcHfM5V6FFJe6pPSo= -gopkg.in/olivere/elastic.v3 v3.0.75/go.mod h1:yDEuSnrM51Pc8dM5ov7U8aI/ToR3PG0llA8aRv2qmw0= -gopkg.in/olivere/elastic.v5 v5.0.84 h1:acF/tRSg5geZpE3rqLglkS79CQMIMzOpWZE7hRXIkjs= -gopkg.in/olivere/elastic.v5 v5.0.84/go.mod h1:LXF6q9XNBxpMqrcgax95C6xyARXWbbCXUrtTxrNrxJI= -gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375 h1:hPki/oSSWOLiI9Gc9jyIoj33O3j29fUc9PlLha2yDj0= -gopkg.in/redis.v4 v4.2.4 h1:y3XbwQAiHwgNLUng56mgWYK39vsPqo8sT84XTEcxjr0= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw= -gorm.io/driver/postgres v1.4.6 h1:1FPESNXqIKG5JmraaH2bfCVlMQ7paLoCreFxDtqzwdc= -gorm.io/driver/postgres v1.4.6/go.mod h1:UJChCNLFKeBqQRE+HrkFUbKbq9idPXmTOk2u4Wok8S4= -gorm.io/driver/sqlserver v1.4.2 h1:nMtEeKqv2R/vv9FoHUFWfXfP6SskAgRar0TPlZV1stk= -gorm.io/driver/sqlserver v1.4.2/go.mod h1:XHwBuB4Tlh7DqO0x7Ema8dmyWsQW7wi38VQOAFkrbXY= -gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk= -k8s.io/api v0.23.17/go.mod h1:upM9VIzXUjEyLTmGGi0KnH8kdlPnvgv+fEJ3tggDHfE= -k8s.io/apimachinery v0.23.17/go.mod h1:87v5Wl9qpHbnapX1PSNgln4oO3dlyjAU3NSIwNhT4Lo= -k8s.io/client-go v0.23.17/go.mod h1:X5yz7nbJHS7q8977AKn8BWKgxeAXjl1sFsgstczUsCM= -k8s.io/code-generator v0.25.5 h1:K3MSqc27VT6fGJtVlE037N2dGmtqyhZi3S+1GkrKH+c= -k8s.io/component-helpers v0.24.2 h1:gtXmI/TjVINtkAdZn7m5p8+Vd0Mk4d1q8kwJMMLBdwY= -k8s.io/cri-api v0.25.0 h1:INwdXsCDSA/0hGNdPxdE2dQD6ft/5K1EaKXZixvSQxg= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHzPMyB0t8BaFeBYI= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/metrics v0.24.2 h1:3lgEq973VGPWAEaT9VI/p0XmI0R5kJgb/r9Ufr5fz8k= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= -mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= -mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7 h1:HT3e4Krq+IE44tiN36RvVEb6tvqeIdtsVSsxmNPqlFU= -rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 h1:LYqFq+6Cj2D0gFfrJvL7iElD4ET6ir3VDdhDdTK7rgc= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/cmd/config v0.10.6 h1:Qjs7z/Q1NrVmW86tavmhM7wZtgWJ7aitLMARlUKrj98= -sigs.k8s.io/kustomize/kustomize/v4 v4.5.4 h1:rzGrL+DA4k8bT6SMz7/U+2z3iiZf1t2RaYJWx8OeTmE= diff --git a/packages/contracts-core/script/utils/DeployerUtils.sol b/packages/contracts-core/script/utils/DeployerUtils.sol index 586a8fc911..3c74d97053 100644 --- a/packages/contracts-core/script/utils/DeployerUtils.sol +++ b/packages/contracts-core/script/utils/DeployerUtils.sol @@ -98,6 +98,7 @@ contract DeployerUtils is Script { /// @notice Predicts the deployment address for a contract. function predictFactoryDeployment(string memory contractName) internal view returns (address) { + require(Address.isContract(address(FACTORY)), "Factory not deployed"); return FACTORY.getDeployed(broadcasterAddress, getDeploymentSalt(contractName)); } diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 591452dcfd..30aedbd71c 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.1.92](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.1.91...@synapsecns/synapse-interface@0.1.92) (2023-08-11) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + +## [0.1.91](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.1.90...@synapsecns/synapse-interface@0.1.91) (2023-08-10) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + +## [0.1.90](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.1.89...@synapsecns/synapse-interface@0.1.90) (2023-08-06) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + ## [0.1.89](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.1.88...@synapsecns/synapse-interface@0.1.89) (2023-08-05) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/constants/chains/master.tsx b/packages/synapse-interface/constants/chains/master.tsx index 9e2c326be9..ca24cf5cef 100644 --- a/packages/synapse-interface/constants/chains/master.tsx +++ b/packages/synapse-interface/constants/chains/master.tsx @@ -107,7 +107,7 @@ export const OPTIMISM: Chain = { layer: 2, codeName: 'optimism', blockTime: 10000, - rpc: 'https://rpc.ankr.com/optimism', + rpc: 'https://mainnet.optimism.io', nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://optimistic.etherscan.io', color: 'red', @@ -294,7 +294,7 @@ export const BASE: Chain = { codeName: 'base', chainImg: baseImg, layer: 2, - rpc: 'https://developer-access-mainnet.base.org', + rpc: 'https://base.blockpi.network/v1/rpc/public', explorerUrl: 'https://basescan.org', blockTime: 5000, nativeCurrency: { diff --git a/packages/synapse-interface/constants/tokens/master.tsx b/packages/synapse-interface/constants/tokens/master.tsx index eb6ea6db22..57298364c8 100644 --- a/packages/synapse-interface/constants/tokens/master.tsx +++ b/packages/synapse-interface/constants/tokens/master.tsx @@ -361,6 +361,7 @@ export const UNIDX = new Token({ [CHAINS.ARBITRUM.id]: '0x5429706887FCb58a595677B73E9B0441C25d993D', // redeem [CHAINS.FANTOM.id]: '0x0483a76D80D0aFEC6bd2afd12C1AD865b9DF1471', [CHAINS.OPTIMISM.id]: '0x28b42698Caf46B4B012CF38b6C75867E0762186D', + [CHAINS.BASE.id]: '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', }, decimals: 18, symbol: 'UNIDX', diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index bcffabea38..ebe43e3548 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.1.89", + "version": "0.1.92", "private": true, "engines": { "node": ">=16.0.0" diff --git a/packages/synapse-interface/pages/landing/sections/IntegrationSection.tsx b/packages/synapse-interface/pages/landing/sections/IntegrationSection.tsx index afeca11d92..7ced509aef 100644 --- a/packages/synapse-interface/pages/landing/sections/IntegrationSection.tsx +++ b/packages/synapse-interface/pages/landing/sections/IntegrationSection.tsx @@ -5,6 +5,9 @@ import { SectionContainer } from '../../../components/landing/shared' import { ORDERED_CHAINS_BY_ID, ChainId, CHAINS_BY_ID } from '@/constants/chains' import { Chain } from '@/utils/types' import { getNetworkButtonBorderHover } from '@/styles/chains' +import { useAppDispatch } from '@/store/hooks' +import { setFromChainId } from '@/slices/bridge/reducer' +import { NAVIGATION } from '@/constants/routes' export default function IntegrationSection() { const OrderedSupportedNetworks: Chain[] = ORDERED_CHAINS_BY_ID.filter( @@ -62,31 +65,6 @@ export default function IntegrationSection() { ) } -function generateNetworkCardHref(chainId) { - let inputCurrency - let outputCurrency - - switch (chainId) { - case ChainId.DOGECHAIN: - inputCurrency = 'ETH' - outputCurrency = 'WETH' - break - case ChainId.MOONBEAM: - inputCurrency = 'SYN' - outputCurrency = 'SYN' - break - case ChainId.MOONRIVER: - inputCurrency = 'SYN' - outputCurrency = 'SYN' - break - default: - inputCurrency = 'USDC' - outputCurrency = 'USDC' - } - - return `/?inputCurrency=${inputCurrency}&outputCurrency=${outputCurrency}&outputChain=${chainId}` -} - function ChainImg({ src }: { src: string }) { return (
@@ -108,10 +86,15 @@ function NetworkCard({ layer, chainImg, }: NetworkCardProps) { - const href = generateNetworkCardHref(chainId) + const dispatch = useAppDispatch() const chain = CHAINS_BY_ID[chainId] + + const handleSelection = () => { + dispatch(setFromChainId(chainId)) + } + return ( - + explorer diff --git a/services/explorer/api/server.go b/services/explorer/api/server.go index e2aa4bd285..eccd124116 100644 --- a/services/explorer/api/server.go +++ b/services/explorer/api/server.go @@ -10,7 +10,6 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "net" - "os" "time" "github.com/ipfs/go-log" @@ -39,6 +38,8 @@ type Config struct { Address string // ScribeURL is the url of the scribe service ScribeURL string + // HydrateCache is whether or not to hydrate the cache + HydrateCache bool } const cacheRehydrationInterval = 1800 @@ -50,13 +51,8 @@ var logger = log.Logger("explorer-api") // nolint:cyclop func Start(ctx context.Context, cfg Config, handler metrics.Handler) error { router := ginhelper.New(logger) - router.Use(handler.Gin()) router.GET(ginhelper.MetricsEndpoint, gin.WrapH(handler.Handler())) - hostname, err := os.Hostname() - if err != nil { - return fmt.Errorf("could not get hostname %w", err) - } // initialize the database consumerDB, err := InitDB(ctx, cfg.Address, true, handler) if err != nil { @@ -80,14 +76,14 @@ func Start(ctx context.Context, cfg Config, handler metrics.Handler) error { gqlServer.EnableGraphql(router, consumerDB, fetcher, responseCache, handler) - fmt.Printf("started graphiql gqlServer on port: http://%s:%d/graphiql\n", hostname, cfg.HTTPPort) + fmt.Printf("started graphiql gqlServer on port: http://localhost:%d/graphiql\n", cfg.HTTPPort) ticker := time.NewTicker(cacheRehydrationInterval * time.Second) defer ticker.Stop() first := make(chan bool, 1) first <- true g, ctx := errgroup.WithContext(ctx) - url := fmt.Sprintf("http://%s/graphql", net.JoinHostPort(hostname, fmt.Sprintf("%d", cfg.HTTPPort))) + url := fmt.Sprintf("http://%s/graphql", net.JoinHostPort("localhost", fmt.Sprintf("%d", cfg.HTTPPort))) client := gqlClient.NewClient(httpClient, url) err = registerObservableMetrics(handler, consumerDB) @@ -95,29 +91,30 @@ func Start(ctx context.Context, cfg Config, handler metrics.Handler) error { return fmt.Errorf("could not register observable metrics: %w", err) } - // refill cache - go func() { - for { - select { - case <-ctx.Done(): - ticker.Stop() - return - case <-ticker.C: - err = RehydrateCache(ctx, client, responseCache, handler) - if err != nil { - logger.Warnf("rehydration failed: %s", err) - } - case <-first: - // buffer to wait for everything to get initialized - time.Sleep(10 * time.Second) - err = RehydrateCache(ctx, client, responseCache, handler) - if err != nil { - logger.Errorf("initial rehydration failed: %s", err) + if cfg.HydrateCache { + // refill cache + go func() { + for { + select { + case <-ctx.Done(): + ticker.Stop() + return + case <-ticker.C: + err = RehydrateCache(ctx, client, responseCache, handler) + if err != nil { + logger.Warnf("rehydration failed: %s", err) + } + case <-first: + // buffer to wait for everything to get initialized + time.Sleep(10 * time.Second) + err = RehydrateCache(ctx, client, responseCache, handler) + if err != nil { + logger.Errorf("initial rehydration failed: %s", err) + } } } - } - }() - + }() + } g.Go(func() error { connection := baseServer.Server{} err = connection.ListenAndServe(ctx, fmt.Sprintf(":%d", cfg.HTTPPort), router) diff --git a/services/explorer/api/suite_test.go b/services/explorer/api/suite_test.go index 11814ea6a5..9d0d9377ea 100644 --- a/services/explorer/api/suite_test.go +++ b/services/explorer/api/suite_test.go @@ -1,31 +1,33 @@ package api_test import ( + "context" gosql "database/sql" "fmt" + "math/big" + "net/http" + "testing" + "github.com/phayes/freeport" + . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/core/metrics/localmetrics" + "github.com/synapsecns/sanguine/core/retry" + "github.com/synapsecns/sanguine/core/testsuite" "github.com/synapsecns/sanguine/ethergo/backends" "github.com/synapsecns/sanguine/services/explorer/api" explorerclient "github.com/synapsecns/sanguine/services/explorer/consumer/client" + "github.com/synapsecns/sanguine/services/explorer/db" "github.com/synapsecns/sanguine/services/explorer/db/sql" + "github.com/synapsecns/sanguine/services/explorer/graphql/client" + "github.com/synapsecns/sanguine/services/explorer/graphql/server" "github.com/synapsecns/sanguine/services/explorer/metadata" "github.com/synapsecns/sanguine/services/explorer/testutil" "github.com/synapsecns/sanguine/services/explorer/testutil/clickhouse" scribedb "github.com/synapsecns/sanguine/services/scribe/db" gqlServer "github.com/synapsecns/sanguine/services/scribe/graphql/server" scribeMetadata "github.com/synapsecns/sanguine/services/scribe/metadata" - "math/big" - "net/http" - "testing" - - . "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "github.com/synapsecns/sanguine/core/testsuite" - "github.com/synapsecns/sanguine/services/explorer/db" - "github.com/synapsecns/sanguine/services/explorer/graphql/client" - "github.com/synapsecns/sanguine/services/explorer/graphql/server" "go.uber.org/atomic" ) @@ -189,7 +191,8 @@ func (g *APISuite) SetupSuite() { var err error g.scribeMetrics, err = metrics.NewByType(g.GetSuiteContext(), scribeMetadata.BuildInfo(), metrics.Jaeger) g.Require().Nil(err) - g.explorerMetrics, err = metrics.NewByType(g.GetSuiteContext(), metadata.BuildInfo(), metrics.Jaeger) + // TODO: there may be an issue w/ syncer for local test nevs, investigate, but this probably comes from heavy load ending every span of every field synchronously + g.explorerMetrics, err = metrics.NewByType(g.GetSuiteContext(), metadata.BuildInfo(), metrics.Null) g.Require().Nil(err) } @@ -198,7 +201,6 @@ func (g *APISuite) SetupTest() { g.db, g.eventDB, g.gqlClient, g.logIndex, g.cleanup, g.testBackend, g.deployManager = testutil.NewTestEnvDB(g.GetTestContext(), g.T(), g.scribeMetrics) - httpport := freeport.GetPort() cleanup, port, err := clickhouse.NewClickhouseStore("explorer") NotNil(g.T(), cleanup) NotNil(g.T(), port) @@ -215,8 +217,10 @@ func (g *APISuite) SetupTest() { Nil(g.T(), err) g.chainIDs = []uint32{1, 10, 25, 56, 137} + httpport := freeport.GetPort() + go func() { - Nil(g.T(), api.Start(g.GetSuiteContext(), api.Config{ + Nil(g.T(), api.Start(g.GetTestContext(), api.Config{ HTTPPort: uint16(httpport), Address: address, ScribeURL: g.gqlClient.Client.BaseURL, @@ -227,7 +231,7 @@ func (g *APISuite) SetupTest() { g.client = client.NewClient(http.DefaultClient, fmt.Sprintf("%s%s", baseURL, gqlServer.GraphqlEndpoint)) - g.Eventually(func() bool { + err = retry.WithBackoff(g.GetTestContext(), func(ctx context.Context) error { request, err := http.NewRequestWithContext(g.GetTestContext(), http.MethodGet, fmt.Sprintf("%s%s", baseURL, server.GraphiqlEndpoint), nil) Nil(g.T(), err) res, err := g.client.Client.Client.Do(request) @@ -235,10 +239,12 @@ func (g *APISuite) SetupTest() { defer func() { _ = res.Body.Close() }() - return true + return nil } - return false - }) + return fmt.Errorf("failed to connect to graphql server: %w", err) + }, retry.WithMaxAttempts(1000)) + + g.Require().Nil(err) } func TestAPISuite(t *testing.T) { diff --git a/services/explorer/backfill/chain.go b/services/explorer/backfill/chain.go index afcf1e065a..1cb07e63cf 100644 --- a/services/explorer/backfill/chain.go +++ b/services/explorer/backfill/chain.go @@ -154,6 +154,7 @@ func (c *ChainBackfiller) backfillContractLogs(parentCtx context.Context, contra } var endHeight uint64 err = c.retryWithBackoff(parentCtx, func(ctx context.Context) error { + // TODO change to get last unconfirmed block endHeight, err = c.Fetcher.FetchLastIndexed(parentCtx, c.chainConfig.ChainID, contract.Address) if err != nil { return fmt.Errorf("could not get last indexed height, %w", err) @@ -183,7 +184,7 @@ func (c *ChainBackfiller) backfillContractLogs(parentCtx context.Context, contra b := &backoff.Backoff{ Factor: 2, Jitter: true, - Min: 30 * time.Millisecond, + Min: 1 * time.Second, Max: 3 * time.Second, } @@ -211,6 +212,7 @@ func (c *ChainBackfiller) backfillContractLogs(parentCtx context.Context, contra logger.Warnf("could not process logs for chain %d: %s", c.chainConfig.ChainID, err) continue } + if len(parsedLogs) > 0 { g.Go(func() error { return c.storeParsedLogs(groupCtx, parsedLogs) @@ -226,7 +228,6 @@ func (c *ChainBackfiller) backfillContractLogs(parentCtx context.Context, contra return fmt.Errorf("error while backfilling chain %d: %w", c.chainConfig.ChainID, err) } logger.Infof("backfilling contract %s chunk completed, %d to %d", contract.Address, chunkStart, chunkEnd) - // Store the last block in clickhouse err = c.retryWithBackoff(parentCtx, func(ctx context.Context) error { err = c.consumerDB.StoreLastBlock(parentCtx, c.chainConfig.ChainID, chunkEnd, contract.Address) @@ -241,6 +242,7 @@ func (c *ChainBackfiller) backfillContractLogs(parentCtx context.Context, contra } currentHeight = chunkEnd + 1 } + return nil } @@ -251,8 +253,8 @@ func (c *ChainBackfiller) processLogs(ctx context.Context, logs []ethTypes.Log, b := &backoff.Backoff{ Factor: 2, Jitter: true, - Min: 30 * time.Millisecond, - Max: 3 * time.Second, + Min: 1 * time.Second, + Max: 10 * time.Second, } timeout := time.Duration(0) @@ -266,15 +268,18 @@ func (c *ChainBackfiller) processLogs(ctx context.Context, logs []ethTypes.Log, return parsedLogs, nil } parsedLog, err := eventParser.Parse(ctx, logs[logIdx], c.chainConfig.ChainID) - if err != nil && err.Error() != parser.ErrUnknownTopic { - logger.Errorf("could not parse and store log %d, %s blocknumber: %d, %s", c.chainConfig.ChainID, logs[logIdx].Address, logs[logIdx].BlockNumber, err) - timeout = b.Duration() - continue - } - if parsedLog != nil { - parsedLogs = append(parsedLogs, parsedLog) + if err != nil || parsedLog == nil { + if err.Error() == parser.ErrUnknownTopic { + logger.Warnf("could not parse log (ErrUnknownTopic) %d, %s %s blocknumber: %d, %s", c.chainConfig.ChainID, logs[logIdx].TxHash, logs[logIdx].Address, logs[logIdx].BlockNumber, err) + } else { // retry + logger.Errorf("could not parse log %d, %s blocknumber: %d, %s", c.chainConfig.ChainID, logs[logIdx].Address, logs[logIdx].BlockNumber, err) + timeout = b.Duration() + continue + } } + parsedLogs = append(parsedLogs, parsedLog) + logIdx++ // Reset the backoff after successful log parse run to prevent bloated back offs. diff --git a/services/explorer/backfill/chain_test.go b/services/explorer/backfill/chain_test.go index 2c0d5fa095..549050e0e1 100644 --- a/services/explorer/backfill/chain_test.go +++ b/services/explorer/backfill/chain_test.go @@ -3,6 +3,7 @@ package backfill_test import ( gosql "database/sql" "fmt" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" "math/big" "github.com/synapsecns/sanguine/ethergo/mocks" @@ -39,6 +40,7 @@ func arrayToTokenIndexMap(input []*big.Int) map[uint8]string { //nolint:maintidx func (b *BackfillSuite) TestBackfill() { testChainID := b.testBackend.GetBigChainID() + bridgeContract, bridgeRef := b.testDeployManager.GetTestSynapseBridge(b.GetTestContext(), b.testBackend) bridgeV1Contract, bridgeV1Ref := b.testDeployManager.GetTestSynapseBridgeV1(b.GetTestContext(), b.testBackend) swapContractA, swapRefA := b.testDeployManager.GetTestSwapFlashLoan(b.GetTestContext(), b.testBackend) @@ -319,12 +321,12 @@ func (b *BackfillSuite) TestBackfill() { // Go through each contract and save the end height in scribe for i := range chainConfigs[0].Contracts { // the last block store per contract - err = b.eventDB.StoreLastIndexed(b.GetTestContext(), common.HexToAddress(chainConfigs[0].Contracts[i].Address), uint32(testChainID.Uint64()), lastBlock) + err = b.eventDB.StoreLastIndexed(b.GetTestContext(), common.HexToAddress(chainConfigs[0].Contracts[i].Address), uint32(testChainID.Uint64()), lastBlock, scribeTypes.IndexingConfirmed) Nil(b.T(), err) } for i := range chainConfigsV1[0].Contracts { // the last block store per contract - err = b.eventDB.StoreLastIndexed(b.GetTestContext(), common.HexToAddress(chainConfigsV1[0].Contracts[i].Address), uint32(testChainID.Uint64()), lastBlock) + err = b.eventDB.StoreLastIndexed(b.GetTestContext(), common.HexToAddress(chainConfigsV1[0].Contracts[i].Address), uint32(testChainID.Uint64()), lastBlock, scribeTypes.IndexingConfirmed) Nil(b.T(), err) } @@ -362,8 +364,10 @@ func (b *BackfillSuite) TestBackfill() { msp, err := parser.NewSwapParser(b.db, metaSwapContract.Address(), true, b.consumerFetcher, msr, tokenDataService, tokenPriceService) Nil(b.T(), err) - // cp is the cctp ref for getting token data - cp, err := parser.NewCCTPParser(b.db, cctpRef.Address(), b.consumerFetcher, tokenPriceService) + // msr is the meta swap ref for getting token data + cr, err := fetcher.NewCCTPFetcher(cctpRef.Address(), b.testBackend) + Nil(b.T(), err) + cp, err := parser.NewCCTPParser(b.db, cctpRef.Address(), b.consumerFetcher, cr, tokenDataService, tokenPriceService) Nil(b.T(), err) spMap := map[common.Address]*parser.SwapParser{} @@ -383,17 +387,24 @@ func (b *BackfillSuite) TestBackfill() { // Backfill the blocks var count int64 err = chainBackfiller.Backfill(b.GetTestContext(), false, 1) + Nil(b.T(), err) swapEvents := b.db.UNSAFE_DB().WithContext(b.GetTestContext()).Find(&sql.SwapEvent{}).Count(&count) Nil(b.T(), swapEvents.Error) Equal(b.T(), int64(11), count) + bridgeEvents := b.db.UNSAFE_DB().WithContext(b.GetTestContext()).Find(&sql.BridgeEvent{}).Count(&count) Nil(b.T(), bridgeEvents.Error) - Equal(b.T(), int64(10), count) + Equal(b.T(), int64(12), count) // 10 + 2 cctp events + messageEvents := b.db.UNSAFE_DB().WithContext(b.GetTestContext()).Find(&sql.MessageBusEvent{}).Count(&count) Nil(b.T(), messageEvents.Error) Equal(b.T(), int64(3), count) + cctpEvents := b.db.UNSAFE_DB().WithContext(b.GetTestContext()).Find(&sql.CCTPEvent{}).Count(&count) + Nil(b.T(), cctpEvents.Error) + Equal(b.T(), int64(2), count) + // Test cctp parity err = b.sendCircleTokenParity(requestSentLog, cp) Nil(b.T(), err) @@ -474,8 +485,9 @@ func (b *BackfillSuite) TestBackfill() { Nil(b.T(), err) bridgeEvents = b.db.UNSAFE_DB().WithContext(b.GetTestContext()).Find(&sql.BridgeEvent{}).Count(&count) + Nil(b.T(), bridgeEvents.Error) - Equal(b.T(), int64(16), count) + Equal(b.T(), int64(18), count) // 16 + 2 cctp events lastBlockStored, err := b.db.GetLastStoredBlock(b.GetTestContext(), uint32(testChainID.Uint64()), chainConfigsV1[0].Contracts[0].Address) @@ -515,10 +527,7 @@ func (b *BackfillSuite) sendCircleTokenParity(log *types.Log, parser *parser.CCT Int64: int64(parsedLog.Nonce), Valid: true, } - burnToken := gosql.NullString{ - String: parsedLog.Token.String(), - Valid: true, - } + requestVersion := gosql.NullInt32{ Int32: int32(parsedLog.RequestVersion), Valid: true, @@ -537,8 +546,8 @@ func (b *BackfillSuite) sendCircleTokenParity(log *types.Log, parser *parser.CCT DestinationChainID: parsedLog.ChainId, Sender: sender, Nonce: nonce, - BurnToken: burnToken, - SentAmount: parsedLog.Amount, + Token: parsedLog.Token.String(), + Amount: parsedLog.Amount, RequestVersion: requestVersion, FormattedRequest: formattedRequest, }).Count(&count) @@ -566,10 +575,7 @@ func (b *BackfillSuite) receiveCircleTokenParity(log *types.Log, parser *parser. String: parsedLog.Recipient.String(), Valid: true, } - token := gosql.NullString{ - String: parsedLog.Token.String(), - Valid: true, - } + domainToChain := []int64{1, 43114, 10, 42161} events := b.db.UNSAFE_DB().WithContext(b.GetTestContext()).Model(&sql.CCTPEvent{}). Where(&sql.CCTPEvent{ ContractAddress: log.Address.String(), @@ -577,12 +583,12 @@ func (b *BackfillSuite) receiveCircleTokenParity(log *types.Log, parser *parser. TxHash: log.TxHash.String(), EventType: cctpTypes.CircleRequestFulfilledEvent.Int(), RequestID: common.Bytes2Hex(parsedLog.RequestID[:]), - OriginChainID: big.NewInt(int64(parsedLog.OriginDomain)), + OriginChainID: big.NewInt(domainToChain[parsedLog.OriginDomain]), MintToken: mintToken, - ReceivedAmount: parsedLog.Amount, + Amount: parsedLog.Amount, Recipient: recipient, Fee: parsedLog.Fee, - Token: token, + Token: parsedLog.Token.String(), }).Count(&count) if events.Error != nil { return fmt.Errorf("error querying for event: %w", events.Error) diff --git a/services/explorer/backfill/suite_test.go b/services/explorer/backfill/suite_test.go index 7d477e88af..bde9cc418d 100644 --- a/services/explorer/backfill/suite_test.go +++ b/services/explorer/backfill/suite_test.go @@ -10,6 +10,7 @@ import ( "github.com/synapsecns/sanguine/core/metrics/localmetrics" "github.com/synapsecns/sanguine/core/testsuite" "github.com/synapsecns/sanguine/ethergo/backends" + "github.com/synapsecns/sanguine/ethergo/backends/geth" "github.com/synapsecns/sanguine/ethergo/contracts" "github.com/synapsecns/sanguine/services/explorer/consumer/client" "github.com/synapsecns/sanguine/services/explorer/consumer/fetcher" @@ -93,7 +94,10 @@ var testTokens = []TestToken{{ func (b *BackfillSuite) SetupTest() { b.TestSuite.SetupTest() - b.db, b.eventDB, b.gqlClient, b.logIndex, b.cleanup, b.testBackend, b.deployManager = testutil.NewTestEnvDB(b.GetTestContext(), b.T(), b.metrics) + b.db, b.eventDB, b.gqlClient, b.logIndex, b.cleanup, _, b.deployManager = testutil.NewTestEnvDB(b.GetTestContext(), b.T(), b.metrics) + + chainID := big.NewInt(1) + b.testBackend = geth.NewEmbeddedBackendForChainID(b.GetTestContext(), b.T(), chainID) b.testDeployManager = testcontracts.NewDeployManager(b.T()) b.consumerFetcher = fetcher.NewFetcher(b.gqlClient, b.metrics) diff --git a/services/explorer/cmd/commands.go b/services/explorer/cmd/commands.go index abc71af979..26118755d0 100644 --- a/services/explorer/cmd/commands.go +++ b/services/explorer/cmd/commands.go @@ -67,9 +67,10 @@ var serverCommand = &cli.Command{ Action: func(c *cli.Context) error { fmt.Println("port", c.Uint("port")) err := api.Start(c.Context, api.Config{ - HTTPPort: uint16(c.Uint(portFlag.Name)), - Address: c.String(addressFlag.Name), - ScribeURL: c.String(scribeURL.Name), + HTTPPort: uint16(c.Uint(portFlag.Name)), + Address: c.String(addressFlag.Name), + ScribeURL: c.String(scribeURL.Name), + HydrateCache: true, // TODO make this a flag }, metrics.Get()) if err != nil { return fmt.Errorf("could not start server: %w", err) diff --git a/services/explorer/config.yaml b/services/explorer/config.yaml index 9837801f22..7273a3761a 100644 --- a/services/explorer/config.yaml +++ b/services/explorer/config.yaml @@ -217,3 +217,16 @@ chains: - contract_type: bridge address: '0x9508BF380c1e6f751D97604732eF1Bae6673f299' start_block: -1 + - chain_id: 8453 + fetch_block_increment: 10000 + max_goroutines: 1 + contracts: + - contract_type: bridge + address: '0xf07d1C752fAb503E47FEF309bf14fbDD3E867089' + start_block: -1 + - contract_type: swap + address: '0x6223bD82010E2fB69F329933De20897e7a4C225f' + start_block: -1 + - contract_type: swap #ETH Pool + address: '0xa9E90579eb086bcdA910dD94041ffE041Fb4aC89' + start_block: -1 diff --git a/services/explorer/configOrigin.yaml b/services/explorer/configOrigin.yaml index f27796a4b5..6dd135c841 100644 --- a/services/explorer/configOrigin.yaml +++ b/services/explorer/configOrigin.yaml @@ -210,3 +210,13 @@ chains: - contract_type: swap #nUSDNOTEPool address: '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c' start_block: 1690002 + - chain_id: 8453 + fetch_block_increment: 10000 + max_goroutines: 1 + contracts: + - contract_type: bridge + address: '0xf07d1C752fAb503E47FEF309bf14fbDD3E867089' + start_block: 1001137 + - contract_type: swap + address: '0x6223bD82010E2fB69F329933De20897e7a4C225f' + start_block: 1001141 diff --git a/services/explorer/consumer/client/client.go b/services/explorer/consumer/client/client.go index 9d62c54965..187e4472c7 100644 --- a/services/explorer/consumer/client/client.go +++ b/services/explorer/consumer/client/client.go @@ -34,6 +34,9 @@ type Query struct { LogCount *int "json:\"logCount\" graphql:\"logCount\"" ReceiptCount *int "json:\"receiptCount\" graphql:\"receiptCount\"" BlockTimeCount *int "json:\"blockTimeCount\" graphql:\"blockTimeCount\"" + LogsAtHeadRange []*model.Log "json:\"logsAtHeadRange\" graphql:\"logsAtHeadRange\"" + ReceiptsAtHeadRange []*model.Receipt "json:\"receiptsAtHeadRange\" graphql:\"receiptsAtHeadRange\"" + TransactionsAtHeadRange []*model.Transaction "json:\"transactionsAtHeadRange\" graphql:\"transactionsAtHeadRange\"" } type GetLogsRange struct { Response []*struct { diff --git a/services/explorer/consumer/client/resolver-client/server.go b/services/explorer/consumer/client/resolver-client/server.go index edf6a195b3..66c8ab7706 100644 --- a/services/explorer/consumer/client/resolver-client/server.go +++ b/services/explorer/consumer/client/resolver-client/server.go @@ -79,11 +79,14 @@ type ComplexityRoot struct { LastStoredBlockNumber func(childComplexity int, chainID int) int LogCount func(childComplexity int, contractAddress string, chainID int) int Logs func(childComplexity int, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, page int) int + LogsAtHeadRange func(childComplexity int, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) int LogsRange func(childComplexity int, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) int ReceiptCount func(childComplexity int, chainID int) int Receipts func(childComplexity int, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, page int) int + ReceiptsAtHeadRange func(childComplexity int, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) int ReceiptsRange func(childComplexity int, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) int Transactions func(childComplexity int, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, page int) int + TransactionsAtHeadRange func(childComplexity int, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, lastIndexed int, page int) int TransactionsRange func(childComplexity int, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, page int) int TxSender func(childComplexity int, txHash string, chainID int) int } @@ -149,6 +152,9 @@ type QueryResolver interface { LogCount(ctx context.Context, contractAddress string, chainID int) (*int, error) ReceiptCount(ctx context.Context, chainID int) (*int, error) BlockTimeCount(ctx context.Context, chainID int) (*int, error) + LogsAtHeadRange(ctx context.Context, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) ([]*model.Log, error) + ReceiptsAtHeadRange(ctx context.Context, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) ([]*model.Receipt, error) + TransactionsAtHeadRange(ctx context.Context, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, lastIndexed int, page int) ([]*model.Transaction, error) } type ReceiptResolver interface { Logs(ctx context.Context, obj *model.Receipt) ([]*model.Log, error) @@ -172,7 +178,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -391,6 +397,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Logs(childComplexity, args["contract_address"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["tx_hash"].(*string), args["tx_index"].(*int), args["block_hash"].(*string), args["index"].(*int), args["confirmed"].(*bool), args["page"].(int)), true + case "Query.logsAtHeadRange": + if e.complexity.Query.LogsAtHeadRange == nil { + break + } + + args, err := ec.field_Query_logsAtHeadRange_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.LogsAtHeadRange(childComplexity, args["contract_address"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["tx_hash"].(*string), args["tx_index"].(*int), args["block_hash"].(*string), args["index"].(*int), args["confirmed"].(*bool), args["start_block"].(int), args["end_block"].(int), args["page"].(int)), true + case "Query.logsRange": if e.complexity.Query.LogsRange == nil { break @@ -427,6 +445,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Receipts(childComplexity, args["chain_id"].(int), args["tx_hash"].(*string), args["contract_address"].(*string), args["block_hash"].(*string), args["block_number"].(*int), args["tx_index"].(*int), args["confirmed"].(*bool), args["page"].(int)), true + case "Query.receiptsAtHeadRange": + if e.complexity.Query.ReceiptsAtHeadRange == nil { + break + } + + args, err := ec.field_Query_receiptsAtHeadRange_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.ReceiptsAtHeadRange(childComplexity, args["chain_id"].(int), args["tx_hash"].(*string), args["contract_address"].(*string), args["block_hash"].(*string), args["block_number"].(*int), args["tx_index"].(*int), args["confirmed"].(*bool), args["start_block"].(int), args["end_block"].(int), args["page"].(int)), true + case "Query.receiptsRange": if e.complexity.Query.ReceiptsRange == nil { break @@ -451,6 +481,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Transactions(childComplexity, args["tx_hash"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["block_hash"].(*string), args["confirmed"].(*bool), args["page"].(int)), true + case "Query.transactionsAtHeadRange": + if e.complexity.Query.TransactionsAtHeadRange == nil { + break + } + + args, err := ec.field_Query_transactionsAtHeadRange_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TransactionsAtHeadRange(childComplexity, args["tx_hash"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["block_hash"].(*string), args["confirmed"].(*bool), args["start_block"].(int), args["end_block"].(int), args["last_indexed"].(int), args["page"].(int)), true + case "Query.transactionsRange": if e.complexity.Query.TransactionsRange == nil { break @@ -712,25 +754,40 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true switch rc.Operation.Operation { case ast.Query: return func(ctx context.Context) *graphql.Response { - if !first { - return nil + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, rc.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { + result := <-ec.deferredResults + atomic.AddInt32(&ec.pendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } } - first = false - ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data := ec._Query(ctx, rc.Operation.SelectionSet) var buf bytes.Buffer data.MarshalGQL(&buf) - - return &graphql.Response{ - Data: buf.Bytes(), + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + + return &response } default: @@ -741,6 +798,28 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema + deferred int32 + pendingDeferred int32 + deferredResults chan graphql.DeferredResult +} + +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() } func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { @@ -879,6 +958,45 @@ directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITI blockTimeCount( chain_id: Int! ): Int + # returns all logs that match the given filter and range (including from the unconfirmed logs table) + logsAtHeadRange( + contract_address: String + chain_id: Int! + block_number: Int + tx_hash: String + tx_index: Int + block_hash: String + index: Int + confirmed: Boolean + start_block: Int! + end_block: Int! + page: Int! + ): [Log] + # returns all receipts that match the given filter and range (including from the unconfirmed receipts table) + receiptsAtHeadRange( + chain_id: Int! + tx_hash: String + contract_address: String + block_hash: String + block_number: Int + tx_index: Int + confirmed: Boolean + start_block: Int! + end_block: Int! + page: Int! + ): [Receipt] + # returns all transactions that match the given filter and range (including from the unconfirmed transactions table) + transactionsAtHeadRange( + tx_hash: String + chain_id: Int! + block_number: Int + block_hash: String + confirmed: Boolean + start_block: Int! + end_block: Int! + last_indexed: Int! + page: Int! + ): [Transaction] } `, BuiltIn: false}, {Name: "../schema/types.graphql", Input: `scalar JSON @@ -1100,6 +1218,111 @@ func (ec *executionContext) field_Query_logCount_args(ctx context.Context, rawAr return args, nil } +func (ec *executionContext) field_Query_logsAtHeadRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["contract_address"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contract_address")) + arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["contract_address"] = arg0 + var arg1 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg1, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["chain_id"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_number"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_hash"] = arg3 + var arg4 *int + if tmp, ok := rawArgs["tx_index"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_index")) + arg4, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_index"] = arg4 + var arg5 *string + if tmp, ok := rawArgs["block_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) + arg5, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_hash"] = arg5 + var arg6 *int + if tmp, ok := rawArgs["index"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("index")) + arg6, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["index"] = arg6 + var arg7 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg7, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["confirmed"] = arg7 + var arg8 int + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg8, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg8 + var arg9 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg9, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg9 + var arg10 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + arg10, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["page"] = arg10 + return args, nil +} + func (ec *executionContext) field_Query_logsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1307,7 +1530,7 @@ func (ec *executionContext) field_Query_receiptCount_args(ctx context.Context, r return args, nil } -func (ec *executionContext) field_Query_receiptsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_receiptsAtHeadRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 int @@ -1403,7 +1626,7 @@ func (ec *executionContext) field_Query_receiptsRange_args(ctx context.Context, return args, nil } -func (ec *executionContext) field_Query_receipts_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_receiptsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 int @@ -1470,47 +1693,65 @@ func (ec *executionContext) field_Query_receipts_args(ctx context.Context, rawAr } args["confirmed"] = arg6 var arg7 int + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg7, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg7 + var arg8 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg8, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg8 + var arg9 int if tmp, ok := rawArgs["page"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - arg7, err = ec.unmarshalNInt2int(ctx, tmp) + arg9, err = ec.unmarshalNInt2int(ctx, tmp) if err != nil { return nil, err } } - args["page"] = arg7 + args["page"] = arg9 return args, nil } -func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_receipts_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 *string - if tmp, ok := rawArgs["tx_hash"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) - arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + var arg0 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg0, err = ec.unmarshalNInt2int(ctx, tmp) if err != nil { return nil, err } } - args["tx_hash"] = arg0 - var arg1 int - if tmp, ok := rawArgs["chain_id"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) - arg1, err = ec.unmarshalNInt2int(ctx, tmp) + args["chain_id"] = arg0 + var arg1 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp) if err != nil { return nil, err } } - args["chain_id"] = arg1 - var arg2 *int - if tmp, ok := rawArgs["block_number"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) - arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + args["tx_hash"] = arg1 + var arg2 *string + if tmp, ok := rawArgs["contract_address"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contract_address")) + arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) if err != nil { return nil, err } } - args["block_number"] = arg2 + args["contract_address"] = arg2 var arg3 *string if tmp, ok := rawArgs["block_hash"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) @@ -1520,33 +1761,33 @@ func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Conte } } args["block_hash"] = arg3 - var arg4 *bool - if tmp, ok := rawArgs["confirmed"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) - arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + var arg4 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg4, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["confirmed"] = arg4 - var arg5 int - if tmp, ok := rawArgs["start_block"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) - arg5, err = ec.unmarshalNInt2int(ctx, tmp) + args["block_number"] = arg4 + var arg5 *int + if tmp, ok := rawArgs["tx_index"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_index")) + arg5, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["start_block"] = arg5 - var arg6 int - if tmp, ok := rawArgs["end_block"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) - arg6, err = ec.unmarshalNInt2int(ctx, tmp) + args["tx_index"] = arg5 + var arg6 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg6, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) if err != nil { return nil, err } } - args["end_block"] = arg6 + args["confirmed"] = arg6 var arg7 int if tmp, ok := rawArgs["page"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) @@ -1559,7 +1800,7 @@ func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Conte return args, nil } -func (ec *executionContext) field_Query_transactions_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_transactionsAtHeadRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 *string @@ -1608,8 +1849,173 @@ func (ec *executionContext) field_Query_transactions_args(ctx context.Context, r } args["confirmed"] = arg4 var arg5 int - if tmp, ok := rawArgs["page"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg5, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg5 + var arg6 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg6, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg6 + var arg7 int + if tmp, ok := rawArgs["last_indexed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("last_indexed")) + arg7, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["last_indexed"] = arg7 + var arg8 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + arg8, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["page"] = arg8 + return args, nil +} + +func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_hash"] = arg0 + var arg1 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg1, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["chain_id"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_number"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["block_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_hash"] = arg3 + var arg4 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["confirmed"] = arg4 + var arg5 int + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg5, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg5 + var arg6 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg6, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg6 + var arg7 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + arg7, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["page"] = arg7 + return args, nil +} + +func (ec *executionContext) field_Query_transactions_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_hash"] = arg0 + var arg1 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg1, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["chain_id"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_number"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["block_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_hash"] = arg3 + var arg4 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["confirmed"] = arg4 + var arg5 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) arg5, err = ec.unmarshalNInt2int(ctx, tmp) if err != nil { return nil, err @@ -2576,7 +2982,7 @@ func (ec *executionContext) fieldContext_Query_logs(ctx context.Context, field g ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_logs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2658,7 +3064,7 @@ func (ec *executionContext) fieldContext_Query_logsRange(ctx context.Context, fi ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_logsRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2742,7 +3148,7 @@ func (ec *executionContext) fieldContext_Query_receipts(ctx context.Context, fie ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_receipts_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2826,7 +3232,7 @@ func (ec *executionContext) fieldContext_Query_receiptsRange(ctx context.Context ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_receiptsRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2916,7 +3322,7 @@ func (ec *executionContext) fieldContext_Query_transactions(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_transactions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3006,7 +3412,7 @@ func (ec *executionContext) fieldContext_Query_transactionsRange(ctx context.Con ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_transactionsRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3058,7 +3464,7 @@ func (ec *executionContext) fieldContext_Query_blockTime(ctx context.Context, fi ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_blockTime_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3110,7 +3516,7 @@ func (ec *executionContext) fieldContext_Query_lastStoredBlockNumber(ctx context ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_lastStoredBlockNumber_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3162,7 +3568,7 @@ func (ec *executionContext) fieldContext_Query_firstStoredBlockNumber(ctx contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_firstStoredBlockNumber_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3214,7 +3620,7 @@ func (ec *executionContext) fieldContext_Query_lastConfirmedBlockNumber(ctx cont ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_lastConfirmedBlockNumber_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3266,7 +3672,7 @@ func (ec *executionContext) fieldContext_Query_txSender(ctx context.Context, fie ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_txSender_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3318,7 +3724,7 @@ func (ec *executionContext) fieldContext_Query_lastIndexed(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_lastIndexed_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3370,7 +3776,7 @@ func (ec *executionContext) fieldContext_Query_logCount(ctx context.Context, fie ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_logCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3422,7 +3828,7 @@ func (ec *executionContext) fieldContext_Query_receiptCount(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_receiptCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3474,13 +3880,13 @@ func (ec *executionContext) fieldContext_Query_blockTimeCount(ctx context.Contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_blockTimeCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } -func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___type(ctx, field) +func (ec *executionContext) _Query_logsAtHeadRange(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_logsAtHeadRange(ctx, field) if err != nil { return graphql.Null } @@ -3493,7 +3899,7 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.introspectType(fc.Args["name"].(string)) + return ec.resolvers.Query().LogsAtHeadRange(rctx, fc.Args["contract_address"].(*string), fc.Args["chain_id"].(int), fc.Args["block_number"].(*int), fc.Args["tx_hash"].(*string), fc.Args["tx_index"].(*int), fc.Args["block_hash"].(*string), fc.Args["index"].(*int), fc.Args["confirmed"].(*bool), fc.Args["start_block"].(int), fc.Args["end_block"].(int), fc.Args["page"].(int)) }) if err != nil { ec.Error(ctx, err) @@ -3502,41 +3908,49 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.([]*model.Log) fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOLog2ᚕᚖgithubᚗcomᚋsynapsecnsᚋsanguineᚋservicesᚋexplorerᚋconsumerᚋclientᚋmodelᚐLog(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_logsAtHeadRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, - IsResolver: false, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "contract_address": + return ec.fieldContext_Log_contract_address(ctx, field) + case "chain_id": + return ec.fieldContext_Log_chain_id(ctx, field) + case "topics": + return ec.fieldContext_Log_topics(ctx, field) + case "data": + return ec.fieldContext_Log_data(ctx, field) + case "block_number": + return ec.fieldContext_Log_block_number(ctx, field) + case "tx_hash": + return ec.fieldContext_Log_tx_hash(ctx, field) + case "tx_index": + return ec.fieldContext_Log_tx_index(ctx, field) + case "block_hash": + return ec.fieldContext_Log_block_hash(ctx, field) + case "index": + return ec.fieldContext_Log_index(ctx, field) + case "removed": + return ec.fieldContext_Log_removed(ctx, field) + case "page": + return ec.fieldContext_Log_page(ctx, field) + case "transaction": + return ec.fieldContext_Log_transaction(ctx, field) + case "receipt": + return ec.fieldContext_Log_receipt(ctx, field) + case "json": + return ec.fieldContext_Log_json(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Log", field.Name) }, } defer func() { @@ -3546,15 +3960,15 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Query_logsAtHeadRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } -func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___schema(ctx, field) +func (ec *executionContext) _Query_receiptsAtHeadRange(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_receiptsAtHeadRange(ctx, field) if err != nil { return graphql.Null } @@ -3567,7 +3981,7 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.introspectSchema() + return ec.resolvers.Query().ReceiptsAtHeadRange(rctx, fc.Args["chain_id"].(int), fc.Args["tx_hash"].(*string), fc.Args["contract_address"].(*string), fc.Args["block_hash"].(*string), fc.Args["block_number"].(*int), fc.Args["tx_index"].(*int), fc.Args["confirmed"].(*bool), fc.Args["start_block"].(int), fc.Args["end_block"].(int), fc.Args["page"].(int)) }) if err != nil { ec.Error(ctx, err) @@ -3576,42 +3990,290 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Schema) + res := resTmp.([]*model.Receipt) fc.Result = res - return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) + return ec.marshalOReceipt2ᚕᚖgithubᚗcomᚋsynapsecnsᚋsanguineᚋservicesᚋexplorerᚋconsumerᚋclientᚋmodelᚐReceipt(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_receiptsAtHeadRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, - IsResolver: false, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "description": - return ec.fieldContext___Schema_description(ctx, field) - case "types": - return ec.fieldContext___Schema_types(ctx, field) - case "queryType": - return ec.fieldContext___Schema_queryType(ctx, field) - case "mutationType": - return ec.fieldContext___Schema_mutationType(ctx, field) - case "subscriptionType": - return ec.fieldContext___Schema_subscriptionType(ctx, field) - case "directives": - return ec.fieldContext___Schema_directives(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _Receipt_chain_id(ctx context.Context, field graphql.CollectedField, obj *model.Receipt) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Receipt_chain_id(ctx, field) - if err != nil { - return graphql.Null + case "chain_id": + return ec.fieldContext_Receipt_chain_id(ctx, field) + case "type": + return ec.fieldContext_Receipt_type(ctx, field) + case "post_state": + return ec.fieldContext_Receipt_post_state(ctx, field) + case "status": + return ec.fieldContext_Receipt_status(ctx, field) + case "cumulative_gas_used": + return ec.fieldContext_Receipt_cumulative_gas_used(ctx, field) + case "bloom": + return ec.fieldContext_Receipt_bloom(ctx, field) + case "tx_hash": + return ec.fieldContext_Receipt_tx_hash(ctx, field) + case "contract_address": + return ec.fieldContext_Receipt_contract_address(ctx, field) + case "gas_used": + return ec.fieldContext_Receipt_gas_used(ctx, field) + case "block_number": + return ec.fieldContext_Receipt_block_number(ctx, field) + case "transaction_index": + return ec.fieldContext_Receipt_transaction_index(ctx, field) + case "page": + return ec.fieldContext_Receipt_page(ctx, field) + case "logs": + return ec.fieldContext_Receipt_logs(ctx, field) + case "transaction": + return ec.fieldContext_Receipt_transaction(ctx, field) + case "json": + return ec.fieldContext_Receipt_json(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Receipt", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_receiptsAtHeadRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_transactionsAtHeadRange(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_transactionsAtHeadRange(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TransactionsAtHeadRange(rctx, fc.Args["tx_hash"].(*string), fc.Args["chain_id"].(int), fc.Args["block_number"].(*int), fc.Args["block_hash"].(*string), fc.Args["confirmed"].(*bool), fc.Args["start_block"].(int), fc.Args["end_block"].(int), fc.Args["last_indexed"].(int), fc.Args["page"].(int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*model.Transaction) + fc.Result = res + return ec.marshalOTransaction2ᚕᚖgithubᚗcomᚋsynapsecnsᚋsanguineᚋservicesᚋexplorerᚋconsumerᚋclientᚋmodelᚐTransaction(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_transactionsAtHeadRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "chain_id": + return ec.fieldContext_Transaction_chain_id(ctx, field) + case "tx_hash": + return ec.fieldContext_Transaction_tx_hash(ctx, field) + case "protected": + return ec.fieldContext_Transaction_protected(ctx, field) + case "type": + return ec.fieldContext_Transaction_type(ctx, field) + case "data": + return ec.fieldContext_Transaction_data(ctx, field) + case "gas": + return ec.fieldContext_Transaction_gas(ctx, field) + case "gas_price": + return ec.fieldContext_Transaction_gas_price(ctx, field) + case "gas_tip_cap": + return ec.fieldContext_Transaction_gas_tip_cap(ctx, field) + case "gas_fee_cap": + return ec.fieldContext_Transaction_gas_fee_cap(ctx, field) + case "value": + return ec.fieldContext_Transaction_value(ctx, field) + case "nonce": + return ec.fieldContext_Transaction_nonce(ctx, field) + case "to": + return ec.fieldContext_Transaction_to(ctx, field) + case "page": + return ec.fieldContext_Transaction_page(ctx, field) + case "sender": + return ec.fieldContext_Transaction_sender(ctx, field) + case "timestamp": + return ec.fieldContext_Transaction_timestamp(ctx, field) + case "logs": + return ec.fieldContext_Transaction_logs(ctx, field) + case "receipt": + return ec.fieldContext_Transaction_receipt(ctx, field) + case "json": + return ec.fieldContext_Transaction_json(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Transaction", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_transactionsAtHeadRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(fc.Args["name"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___schema(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + fc.Result = res + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "description": + return ec.fieldContext___Schema_description(ctx, field) + case "types": + return ec.fieldContext___Schema_types(ctx, field) + case "queryType": + return ec.fieldContext___Schema_queryType(ctx, field) + case "mutationType": + return ec.fieldContext___Schema_mutationType(ctx, field) + case "subscriptionType": + return ec.fieldContext___Schema_subscriptionType(ctx, field) + case "directives": + return ec.fieldContext___Schema_directives(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Receipt_chain_id(ctx context.Context, field graphql.CollectedField, obj *model.Receipt) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Receipt_chain_id(ctx, field) + if err != nil { + return graphql.Null } ctx = graphql.WithFieldContext(ctx, fc) defer func() { @@ -6605,7 +7267,7 @@ func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, fiel ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6793,7 +7455,7 @@ func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6969,41 +7631,48 @@ var blockTimeImplementors = []string{"BlockTime"} func (ec *executionContext) _BlockTime(ctx context.Context, sel ast.SelectionSet, obj *model.BlockTime) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, blockTimeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("BlockTime") case "chain_id": - out.Values[i] = ec._BlockTime_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "block_number": - out.Values[i] = ec._BlockTime_block_number(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "timestamp": - out.Values[i] = ec._BlockTime_timestamp(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7011,93 +7680,72 @@ var logImplementors = []string{"Log"} func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj *model.Log) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, logImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Log") case "contract_address": - out.Values[i] = ec._Log_contract_address(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "chain_id": - out.Values[i] = ec._Log_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "topics": - out.Values[i] = ec._Log_topics(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "data": - out.Values[i] = ec._Log_data(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "block_number": - out.Values[i] = ec._Log_block_number(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_hash": - out.Values[i] = ec._Log_tx_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_index": - out.Values[i] = ec._Log_tx_index(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "block_hash": - out.Values[i] = ec._Log_block_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "index": - out.Values[i] = ec._Log_index(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "removed": - out.Values[i] = ec._Log_removed(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "page": - out.Values[i] = ec._Log_page(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "transaction": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7105,19 +7753,35 @@ func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj }() res = ec._Log_transaction(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "receipt": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7125,19 +7789,35 @@ func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj }() res = ec._Log_receipt(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "json": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7145,23 +7825,51 @@ func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj }() res = ec._Log_json(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7174,7 +7882,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr }) out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ Object: field.Name, @@ -7187,7 +7895,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr case "logs": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7198,16 +7906,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "logsRange": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7218,16 +7925,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "receipts": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7238,16 +7944,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "receiptsRange": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7258,16 +7963,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "transactions": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7278,16 +7982,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "transactionsRange": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7298,16 +8001,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "blockTime": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7318,16 +8020,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "lastStoredBlockNumber": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7338,16 +8039,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "firstStoredBlockNumber": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7358,16 +8058,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "lastConfirmedBlockNumber": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7378,16 +8077,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "txSender": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7398,16 +8096,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "lastIndexed": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7418,16 +8115,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "logCount": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7438,16 +8134,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "receiptCount": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7458,16 +8153,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "blockTimeCount": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7478,32 +8172,96 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) - case "__type": + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "logsAtHeadRange": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_logsAtHeadRange(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "receiptsAtHeadRange": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_receiptsAtHeadRange(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "transactionsAtHeadRange": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_transactionsAtHeadRange(ctx, field) + return res + } + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___type(ctx, field) }) - case "__schema": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___schema(ctx, field) }) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7511,100 +8269,77 @@ var receiptImplementors = []string{"Receipt"} func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, obj *model.Receipt) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, receiptImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Receipt") case "chain_id": - out.Values[i] = ec._Receipt_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "type": - out.Values[i] = ec._Receipt_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "post_state": - out.Values[i] = ec._Receipt_post_state(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "status": - out.Values[i] = ec._Receipt_status(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "cumulative_gas_used": - out.Values[i] = ec._Receipt_cumulative_gas_used(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "bloom": - out.Values[i] = ec._Receipt_bloom(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_hash": - out.Values[i] = ec._Receipt_tx_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "contract_address": - out.Values[i] = ec._Receipt_contract_address(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_used": - out.Values[i] = ec._Receipt_gas_used(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "block_number": - out.Values[i] = ec._Receipt_block_number(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "transaction_index": - out.Values[i] = ec._Receipt_transaction_index(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "page": - out.Values[i] = ec._Receipt_page(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "logs": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7614,14 +8349,30 @@ func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "transaction": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7629,19 +8380,35 @@ func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, }() res = ec._Receipt_transaction(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "json": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7649,23 +8416,51 @@ func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, }() res = ec._Receipt_json(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7673,121 +8468,92 @@ var transactionImplementors = []string{"Transaction"} func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionSet, obj *model.Transaction) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, transactionImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Transaction") case "chain_id": - out.Values[i] = ec._Transaction_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_hash": - out.Values[i] = ec._Transaction_tx_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "protected": - out.Values[i] = ec._Transaction_protected(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "type": - out.Values[i] = ec._Transaction_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "data": - out.Values[i] = ec._Transaction_data(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas": - out.Values[i] = ec._Transaction_gas(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_price": - out.Values[i] = ec._Transaction_gas_price(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_tip_cap": - out.Values[i] = ec._Transaction_gas_tip_cap(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_fee_cap": - out.Values[i] = ec._Transaction_gas_fee_cap(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "value": - out.Values[i] = ec._Transaction_value(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "nonce": - out.Values[i] = ec._Transaction_nonce(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "to": - out.Values[i] = ec._Transaction_to(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "page": - out.Values[i] = ec._Transaction_page(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "sender": - out.Values[i] = ec._Transaction_sender(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "timestamp": - out.Values[i] = ec._Transaction_timestamp(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "logs": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7797,14 +8563,30 @@ func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionS return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "receipt": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7812,19 +8594,35 @@ func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionS }() res = ec._Transaction_receipt(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "json": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7832,23 +8630,51 @@ func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionS }() res = ec._Transaction_json(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7856,52 +8682,55 @@ var __DirectiveImplementors = []string{"__Directive"} func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Directive") case "name": - out.Values[i] = ec.___Directive_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Directive_description(ctx, field, obj) - case "locations": - out.Values[i] = ec.___Directive_locations(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "args": - out.Values[i] = ec.___Directive_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7909,42 +8738,47 @@ var __EnumValueImplementors = []string{"__EnumValue"} func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__EnumValue") case "name": - out.Values[i] = ec.___EnumValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___EnumValue_description(ctx, field, obj) - case "isDeprecated": - out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7952,56 +8786,57 @@ var __FieldImplementors = []string{"__Field"} func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Field") case "name": - out.Values[i] = ec.___Field_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Field_description(ctx, field, obj) - case "args": - out.Values[i] = ec.___Field_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "type": - out.Values[i] = ec.___Field_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isDeprecated": - out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -8009,42 +8844,47 @@ var __InputValueImplementors = []string{"__InputValue"} func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__InputValue") case "name": - out.Values[i] = ec.___InputValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___InputValue_description(ctx, field, obj) - case "type": - out.Values[i] = ec.___InputValue_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "defaultValue": - out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -8052,53 +8892,54 @@ var __SchemaImplementors = []string{"__Schema"} func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Schema") case "description": - out.Values[i] = ec.___Schema_description(ctx, field, obj) - case "types": - out.Values[i] = ec.___Schema_types(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "queryType": - out.Values[i] = ec.___Schema_queryType(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "mutationType": - out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) - case "subscriptionType": - out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) - case "directives": - out.Values[i] = ec.___Schema_directives(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -8106,63 +8947,56 @@ var __TypeImplementors = []string{"__Type"} func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Type") case "kind": - out.Values[i] = ec.___Type_kind(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "name": - out.Values[i] = ec.___Type_name(ctx, field, obj) - case "description": - out.Values[i] = ec.___Type_description(ctx, field, obj) - case "fields": - out.Values[i] = ec.___Type_fields(ctx, field, obj) - case "interfaces": - out.Values[i] = ec.___Type_interfaces(ctx, field, obj) - case "possibleTypes": - out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) - case "enumValues": - out.Values[i] = ec.___Type_enumValues(ctx, field, obj) - case "inputFields": - out.Values[i] = ec.___Type_inputFields(ctx, field, obj) - case "ofType": - out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } diff --git a/services/explorer/consumer/fetcher/cctpfetcher.go b/services/explorer/consumer/fetcher/cctpfetcher.go new file mode 100644 index 0000000000..583857cb53 --- /dev/null +++ b/services/explorer/consumer/fetcher/cctpfetcher.go @@ -0,0 +1,47 @@ +package fetcher + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/synapsecns/sanguine/services/explorer/contracts/cctp" +) + +// CCTPService --output=mocks --case=underscore. +type CCTPService interface { + // GetTokenSymbol gets the token symbol from the cctp ref.. + GetTokenSymbol(ctx context.Context, tokenAddress common.Address) (*string, error) +} + +// cctpFetcher is the fetcher for token data from the cctp contract. +type cctpFetcher struct { + cctp *cctp.SynapseCCTP + backend bind.ContractBackend + cctpAddress common.Address +} + +// NewCCTPFetcher creates a new cctp fetcher. +func NewCCTPFetcher(cctpAddress common.Address, backend bind.ContractBackend) (CCTPService, error) { + cctpRef, err := cctp.NewSynapseCCTP(cctpAddress, backend) + if err != nil { + return nil, fmt.Errorf("could not bind cctp contract: %w", err) + } + + return &cctpFetcher{cctpRef, backend, cctpAddress}, nil +} +func (c *cctpFetcher) GetTokenSymbol(ctx context.Context, tokenAddress common.Address) (*string, error) { + symbol, err := c.cctp.TokenToSymbol(&bind.CallOpts{ + Context: ctx, + }, tokenAddress) + if err != nil { + return nil, fmt.Errorf("could not get cctp token symbol: %w", err) + } + + if symbol == "" { + payload := NoTokenID + return &payload, nil + } + + return &symbol, nil +} diff --git a/services/explorer/consumer/fetcher/swapfetcher.go b/services/explorer/consumer/fetcher/swapfetcher.go index 4c9f93ed31..85885be165 100644 --- a/services/explorer/consumer/fetcher/swapfetcher.go +++ b/services/explorer/consumer/fetcher/swapfetcher.go @@ -32,13 +32,13 @@ func NewSwapFetcher(swapAddress common.Address, backend bind.ContractBackend, is if isMetaSwap { metaSwap, err := metaswap.NewMetaSwapRef(swapAddress, backend) if err != nil { - return nil, fmt.Errorf("could not bind metaswap config contract: %w", err) + return nil, fmt.Errorf("could not bind metaswap contract: %w", err) } return &swapFetcher{nil, metaSwap, backend, swapAddress}, nil } swap, err := swap.NewSwapRef(swapAddress, backend) if err != nil { - return nil, fmt.Errorf("could not bind swap config contract: %w", err) + return nil, fmt.Errorf("could not bind swap contract: %w", err) } return &swapFetcher{swap, nil, backend, swapAddress}, nil diff --git a/services/explorer/consumer/parser/bridgeparser.go b/services/explorer/consumer/parser/bridgeparser.go index a084032637..52e8db9a07 100644 --- a/services/explorer/consumer/parser/bridgeparser.go +++ b/services/explorer/consumer/parser/bridgeparser.go @@ -187,6 +187,11 @@ func eventToBridgeEvent(event bridgeTypes.EventLog, chainID uint32) model.Bridge } } +// ParserType returns the type of parser. +func (p *BridgeParser) ParserType() string { + return "bridge" +} + // ParseAndStore parses the bridge logs and returns a model that can be stored // Deprecated: use Parse and store separately. func (p *BridgeParser) ParseAndStore(ctx context.Context, log ethTypes.Log, chainID uint32) error { @@ -394,7 +399,7 @@ func (p *BridgeParser) Parse(ctx context.Context, log ethTypes.Log, chainID uint if tokenData.TokenID() == fetcher.NoTokenID { logger.Errorf("could not get token data token id chain: %d address %s", chainID, log.Address.Hex()) // handle an inauthentic token. - return &bridgeEvent, nil + return bridgeEvent, nil } realDecimals := tokenData.Decimals() diff --git a/services/explorer/consumer/parser/cctpparser.go b/services/explorer/consumer/parser/cctpparser.go index ff02475829..308b6c7fe2 100644 --- a/services/explorer/consumer/parser/cctpparser.go +++ b/services/explorer/consumer/parser/cctpparser.go @@ -4,6 +4,8 @@ import ( "context" "database/sql" "fmt" + "github.com/jpillora/backoff" + "github.com/synapsecns/sanguine/services/explorer/consumer/parser/tokendata" "time" "github.com/ethereum/go-ethereum/common" @@ -13,6 +15,7 @@ import ( "github.com/synapsecns/sanguine/services/explorer/contracts/cctp" "github.com/synapsecns/sanguine/services/explorer/db" model "github.com/synapsecns/sanguine/services/explorer/db/sql" + bridgeTypes "github.com/synapsecns/sanguine/services/explorer/types/bridge" cctpTypes "github.com/synapsecns/sanguine/services/explorer/types/cctp" ) @@ -26,6 +29,10 @@ type CCTPParser struct { cctpAddress common.Address // consumerFetcher is the Fetcher for sender and timestamp consumerFetcher fetcher.ScribeFetcher + // cctpService is the cctp service for getting token symbol information + cctpService fetcher.CCTPService + // tokenDataService contains the token data service/cache + tokenDataService tokendata.Service // tokenPriceService contains the token price service/cache tokenPriceService tokenprice.Service } @@ -34,12 +41,17 @@ const usdcCoinGeckoID = "usd-coin" const usdcDecimals = 6 // NewCCTPParser creates a new parser for a cctp event. -func NewCCTPParser(consumerDB db.ConsumerDB, cctpAddress common.Address, consumerFetcher fetcher.ScribeFetcher, tokenPriceService tokenprice.Service) (*CCTPParser, error) { +func NewCCTPParser(consumerDB db.ConsumerDB, cctpAddress common.Address, consumerFetcher fetcher.ScribeFetcher, cctpService fetcher.CCTPService, tokenDataService tokendata.Service, tokenPriceService tokenprice.Service) (*CCTPParser, error) { filterer, err := cctp.NewSynapseCCTPFilterer(cctpAddress, nil) if err != nil { return nil, fmt.Errorf("could not create %T: %w", cctp.SynapseCCTPFilterer{}, err) } - return &CCTPParser{consumerDB, filterer, cctpAddress, consumerFetcher, tokenPriceService}, nil + return &CCTPParser{consumerDB, filterer, cctpAddress, consumerFetcher, cctpService, tokenDataService, tokenPriceService}, nil +} + +// ParserType returns the type of parser. +func (c *CCTPParser) ParserType() string { + return "cctp" } // Parse parses the cctp logs. @@ -80,7 +92,7 @@ func (c *CCTPParser) Parse(ctx context.Context, log ethTypes.Log, chainID uint32 } // Populate cctp event type so following operations can mature the event data. - cctpEvent := eventToCCTPEvent(iFace) + cctpEvent := eventToCCTPEvent(iFace, chainID) // Get timestamp from consumer timeStamp, err := c.consumerFetcher.FetchBlockTime(ctx, int(chainID), int(iFace.GetBlockNumber())) @@ -91,8 +103,25 @@ func (c *CCTPParser) Parse(ctx context.Context, log ethTypes.Log, chainID uint32 // If we have a timestamp, populate the following attributes of cctpEvent. timeStampBig := uint64(*timeStamp) cctpEvent.TimeStamp = &timeStampBig + + tokenData, err := c.tokenDataService.GetCCTPTokenData(ctx, chainID, common.HexToAddress(cctpEvent.Token), c.cctpService) + if err != nil { + logger.Errorf("could not get token data: %v", err) + return nil, fmt.Errorf("could not get pool token data: %w", err) + } + decimals := uint8(usdcDecimals) + cctpEvent.TokenSymbol = tokenData.TokenID() + cctpEvent.TokenDecimal = &decimals c.applyPriceData(ctx, &cctpEvent, usdcCoinGeckoID) + // Would store into bridge database with a new goroutine but saw unreliable storage of events w/parent context cancellation. + + bridgeEvent := cctpEventToBridgeEvent(cctpEvent) + err = c.storeBridgeEvent(ctx, bridgeEvent) + if err != nil { + logger.Errorf("could not store cctp event into bridge database: %v", err) + } + return cctpEvent, nil } @@ -105,8 +134,11 @@ func (c *CCTPParser) applyPriceData(ctx context.Context, cctpEvent *model.CCTPEv tokenPrice = &one } - if cctpEvent.SentAmount != nil { - cctpEvent.SentAmountUSD = GetAmountUSD(cctpEvent.SentAmount, usdcDecimals, tokenPrice) + if cctpEvent.Amount != nil { + amountUSD := GetAmountUSD(cctpEvent.Amount, usdcDecimals, tokenPrice) + if amountUSD != nil { + cctpEvent.AmountUSD = *amountUSD + } } if cctpEvent.Fee != nil { cctpEvent.FeeUSD = GetAmountUSD(cctpEvent.Fee, usdcDecimals, tokenPrice) @@ -114,7 +146,7 @@ func (c *CCTPParser) applyPriceData(ctx context.Context, cctpEvent *model.CCTPEv } // eventToCCTPEvent stores a message event. -func eventToCCTPEvent(event cctpTypes.EventLog) model.CCTPEvent { +func eventToCCTPEvent(event cctpTypes.EventLog, chainID uint32) model.CCTPEvent { requestID := event.GetRequestID() var formattedRequest sql.NullString @@ -126,24 +158,94 @@ func eventToCCTPEvent(event cctpTypes.EventLog) model.CCTPEvent { } return model.CCTPEvent{ - InsertTime: uint64(time.Now().UnixNano()), - ContractAddress: event.GetContractAddress().String(), - BlockNumber: event.GetBlockNumber(), - TxHash: event.GetTxHash().String(), - EventType: event.GetEventType().Int(), - RequestID: common.Bytes2Hex(requestID[:]), + InsertTime: uint64(time.Now().UnixNano()), + ChainID: chainID, + TxHash: event.GetTxHash().String(), + ContractAddress: event.GetContractAddress().String(), + BlockNumber: event.GetBlockNumber(), + EventType: event.GetEventType().Int(), + RequestID: common.Bytes2Hex(requestID[:]), + + Token: event.GetToken(), + Amount: event.GetAmount(), + EventIndex: event.GetEventIndex(), OriginChainID: event.GetOriginChainID(), DestinationChainID: event.GetDestinationChainID(), Sender: ToNullString(event.GetSender()), Nonce: ToNullInt64(event.GetNonce()), - BurnToken: ToNullString(event.GetBurnToken()), MintToken: ToNullString(event.GetMintToken()), - SentAmount: event.GetSentAmount(), - ReceivedAmount: event.GetReceivedAmount(), RequestVersion: ToNullInt32(event.GetRequestVersion()), FormattedRequest: formattedRequest, Recipient: ToNullString(event.GetRecipient()), Fee: event.GetFee(), - Token: ToNullString(event.GetToken()), + } +} + +func cctpEventToBridgeEvent(cctpEvent model.CCTPEvent) model.BridgeEvent { + bridgeType := bridgeTypes.CircleRequestSentEvent + + destinationKappa := cctpEvent.RequestID + var kappa *string + if cctpEvent.EventType == cctpTypes.CircleRequestFulfilledEvent.Int() { + bridgeType = bridgeTypes.CircleRequestFulfilledEvent + destinationKappa = "" + kappa = &cctpEvent.RequestID + } + return model.BridgeEvent{ + InsertTime: cctpEvent.InsertTime, + ContractAddress: cctpEvent.ContractAddress, + ChainID: cctpEvent.ChainID, + EventType: bridgeType.Int(), + BlockNumber: cctpEvent.BlockNumber, + TxHash: cctpEvent.TxHash, + Token: cctpEvent.Token, + Amount: cctpEvent.Amount, + EventIndex: cctpEvent.EventIndex, + DestinationKappa: destinationKappa, + Sender: cctpEvent.Sender.String, + + Recipient: cctpEvent.Recipient, + RecipientBytes: sql.NullString{}, + DestinationChainID: cctpEvent.DestinationChainID, + Fee: cctpEvent.Fee, + Kappa: ToNullString(kappa), + TokenIndexFrom: nil, + TokenIndexTo: nil, + MinDy: nil, + Deadline: nil, + + SwapSuccess: nil, + SwapTokenIndex: nil, + SwapMinAmount: nil, + SwapDeadline: nil, + AmountUSD: &cctpEvent.AmountUSD, + FeeUSD: cctpEvent.FeeUSD, + TokenDecimal: cctpEvent.TokenDecimal, + TokenSymbol: ToNullString(&cctpEvent.TokenSymbol), + TimeStamp: cctpEvent.TimeStamp, + } +} + +func (c *CCTPParser) storeBridgeEvent(ctx context.Context, bridgeEvent model.BridgeEvent) error { + b := &backoff.Backoff{ + Factor: 2, + Jitter: true, + Min: 1 * time.Second, + Max: 300 * time.Second, + } + + timeout := time.Duration(0) + for { + select { + case <-ctx.Done(): + return fmt.Errorf("%w while retrying", ctx.Err()) + case <-time.After(timeout): + err := c.consumerDB.StoreEvent(ctx, &bridgeEvent) + if err != nil { + timeout = b.Duration() + continue + } + return nil + } } } diff --git a/services/explorer/consumer/parser/messagebusparser.go b/services/explorer/consumer/parser/messagebusparser.go index 7ae6260d6b..fe7a383711 100644 --- a/services/explorer/consumer/parser/messagebusparser.go +++ b/services/explorer/consumer/parser/messagebusparser.go @@ -81,19 +81,9 @@ func eventToMessageEvent(event messageBusTypes.EventLog, chainID uint32) model.M } } -// ParseAndStore parses the message logs and returns a model that can be stored -// Deprecated: use Parse and store separately. -func (m *MessageBusParser) ParseAndStore(ctx context.Context, log ethTypes.Log, chainID uint32) error { - messageEvent, err := m.Parse(ctx, log, chainID) - if err != nil { - return fmt.Errorf("could not parse event: %w", err) - } - err = m.consumerDB.StoreEvent(ctx, &messageEvent) - - if err != nil { - return fmt.Errorf("could not store event: %w chain: %d address %s", err, chainID, log.Address.String()) - } - return nil +// ParserType returns the type of parser. +func (m *MessageBusParser) ParserType() string { + return "messagebus" } // Parse parses the message logs. diff --git a/services/explorer/consumer/parser/swapparser.go b/services/explorer/consumer/parser/swapparser.go index a6f38a52d7..7ba945363a 100644 --- a/services/explorer/consumer/parser/swapparser.go +++ b/services/explorer/consumer/parser/swapparser.go @@ -179,19 +179,9 @@ func eventToSwapEvent(event swapTypes.EventLog, chainID uint32) model.SwapEvent } } -// ParseAndStore parses the swap logs and returns a model that can be stored -// Deprecated: use Parse and store separately. -func (p *SwapParser) ParseAndStore(ctx context.Context, log ethTypes.Log, chainID uint32) error { - swapEvent, err := p.Parse(ctx, log, chainID) - if err != nil { - return fmt.Errorf("could not parse event: %w", err) - } - err = p.consumerDB.StoreEvent(ctx, &swapEvent) - - if err != nil { - return fmt.Errorf("could not store event: %w chain: %d address %s", err, chainID, log.Address.String()) - } - return nil +// ParserType returns the type of parser. +func (p *SwapParser) ParserType() string { + return "swap" } // Parse parses the swap logs. diff --git a/services/explorer/consumer/parser/tokendata/cache.go b/services/explorer/consumer/parser/tokendata/cache.go index 6d20e3ed90..fd49b29ccb 100644 --- a/services/explorer/consumer/parser/tokendata/cache.go +++ b/services/explorer/consumer/parser/tokendata/cache.go @@ -22,6 +22,8 @@ type Service interface { GetTokenData(ctx context.Context, chainID uint32, token common.Address) (ImmutableTokenData, error) // GetPoolTokenData attempts to get pool token data from the cache otherwise its fetched from the erc20 interface GetPoolTokenData(ctx context.Context, chainID uint32, token common.Address, swapService fetcher.SwapService) (ImmutableTokenData, error) + // GetCCTPTokenData attempts to get the token symbol from the cctp contract + GetCCTPTokenData(ctx context.Context, chainID uint32, token common.Address, cctpService fetcher.CCTPService) (ImmutableTokenData, error) } const cacheSize = 3000 @@ -53,6 +55,7 @@ func NewTokenDataService(service fetcher.Service, tokenSymbolToIDs map[string]st }, nil } +// GetTokenData attempts to get token data from the cache otherwise it is fetched from the bridge config. func (t *tokenDataServiceImpl) GetTokenData(ctx context.Context, chainID uint32, token common.Address) (ImmutableTokenData, error) { key := fmt.Sprintf("token_%d_%s", chainID, token.Hex()) if data, ok := t.tokenCache.Get(key); ok { @@ -69,6 +72,7 @@ func (t *tokenDataServiceImpl) GetTokenData(ctx context.Context, chainID uint32, return tokenData, nil } +// GetPoolTokenData attempts to get pool token data from the cache otherwise it is fetched from the erc20 interface for that token. func (t *tokenDataServiceImpl) GetPoolTokenData(ctx context.Context, chainID uint32, token common.Address, swapService fetcher.SwapService) (ImmutableTokenData, error) { key := fmt.Sprintf("token_%d_%s", chainID, token.Hex()) if data, ok := t.tokenCache.Get(key); ok { @@ -85,6 +89,23 @@ func (t *tokenDataServiceImpl) GetPoolTokenData(ctx context.Context, chainID uin return tokenData, nil } +// GetCCTPTokenData attempts to get cctp token data from the cache otherwise it is fetched using the cctp ref. +func (t *tokenDataServiceImpl) GetCCTPTokenData(ctx context.Context, chainID uint32, token common.Address, cctpService fetcher.CCTPService) (ImmutableTokenData, error) { + key := fmt.Sprintf("token_%d_%s", chainID, token.Hex()) + if data, ok := t.tokenCache.Get(key); ok { + return data, nil + } + + tokenData, err := t.retrieveCCTPTokenData(ctx, token, cctpService) + if err != nil { + return nil, fmt.Errorf("could not get token data: %w", err) + } + + t.tokenCache.Add(key, tokenData) + + return tokenData, nil +} + // retrieveTokenData retrieves the token data from the bridge config contract // this will retry for maxAttemptTime. func (t *tokenDataServiceImpl) retrieveTokenData(parentCtx context.Context, chainID uint32, token common.Address) (ImmutableTokenData, error) { @@ -187,3 +208,29 @@ func (t *tokenDataServiceImpl) retrievePoolTokenData(parentCtx context.Context, return res, nil } + +func (t *tokenDataServiceImpl) retrieveCCTPTokenData(parentCtx context.Context, tokenAddress common.Address, cctpService fetcher.CCTPService) (ImmutableTokenData, error) { + res := immutableTokenImpl{} + + ctx, cancel := context.WithTimeout(parentCtx, maxAttemptTime) + defer cancel() + err := retry.WithBackoff(ctx, func(ctx context.Context) error { + symbol, err := cctpService.GetTokenSymbol(ctx, tokenAddress) + if err != nil { + return fmt.Errorf("could not get cctp token: %w", err) + } + if strings.Contains(strings.ToLower(*symbol), "usdc") { + *symbol = "usdc" + } + res.tokenID = t.tokenSymbolToIDs[strings.ToLower(*symbol)] + res.decimals = 6 // TODO, as cctp bridging matures, retrieve this data from on chain somehow. + + return nil + }, retry.WithMaxAttemptsTime(maxAttemptTime), retry.WithMaxAttempts(maxAttempt)) + if err != nil { + return nil, fmt.Errorf("could not get token data: %w", err) + } + res.tokenAddress = tokenAddress.String() + + return res, nil +} diff --git a/services/explorer/consumer/parser/utils.go b/services/explorer/consumer/parser/utils.go index fd97ce6932..b7da74d1b8 100644 --- a/services/explorer/consumer/parser/utils.go +++ b/services/explorer/consumer/parser/utils.go @@ -4,11 +4,10 @@ import ( "context" "database/sql" "fmt" - "math/big" - "strconv" - ethTypes "github.com/ethereum/go-ethereum/core/types" "gopkg.in/yaml.v2" + "math/big" + "strconv" ) // ErrUnknownTopic is returned when the topic is unknown. @@ -16,11 +15,10 @@ const ErrUnknownTopic = "unknown topic" // Parser parses events and stores them. type Parser interface { - // ParseAndStore parses the logs and stores them in the database. - // Deprecated: use Parse - // ParseAndStore(ctx context.Context, log ethTypes.Log, chainID uint32) error // Parse parses the logs and returns the parsed data. Parse(ctx context.Context, log ethTypes.Log, chainID uint32) (interface{}, error) + // ParserType returns the type of the parser. + ParserType() string } // BoolToUint8 is a helper function to handle bool to uint8 conversion for clickhouse. diff --git a/services/explorer/contracts/cctp/request.go b/services/explorer/contracts/cctp/request.go index e3c30370ca..556811bc29 100644 --- a/services/explorer/contracts/cctp/request.go +++ b/services/explorer/contracts/cctp/request.go @@ -1,10 +1,11 @@ package cctp +import "C" import ( + "github.com/synapsecns/sanguine/services/explorer/types/cctp" "math/big" "github.com/ethereum/go-ethereum/common" - types "github.com/synapsecns/sanguine/services/explorer/types/cctp" ) // GetContractAddress gets the contract address the event occurred on. @@ -23,8 +24,8 @@ func (s SynapseCCTPCircleRequestSent) GetTxHash() common.Hash { } // GetEventType gets the event type for the event. -func (s SynapseCCTPCircleRequestSent) GetEventType() types.EventType { - return types.CircleRequestSentEvent +func (s SynapseCCTPCircleRequestSent) GetEventType() cctp.EventType { + return cctp.CircleRequestSentEvent } // GetRequestID gets the unique identifier of the request. @@ -53,12 +54,6 @@ func (s SynapseCCTPCircleRequestSent) GetNonce() *uint64 { return &s.Nonce } -// GetBurnToken gets the address of the Circle token that was burnt. -func (s SynapseCCTPCircleRequestSent) GetBurnToken() *string { - str := s.Token.String() - return &str -} - // GetMintToken gets the address of the minted Circle token. func (s SynapseCCTPCircleRequestSent) GetMintToken() *string { return nil @@ -69,9 +64,9 @@ func (s SynapseCCTPCircleRequestSent) GetSentAmount() *big.Int { return s.Amount } -// GetReceivedAmount gets the received amount by the recipient. -func (s SynapseCCTPCircleRequestSent) GetReceivedAmount() *big.Int { - return nil +// GetAmount gets the amount from the transfer. +func (s SynapseCCTPCircleRequestSent) GetAmount() *big.Int { + return s.Amount } // GetRequestVersion gets the version of the request format. @@ -95,10 +90,17 @@ func (s SynapseCCTPCircleRequestSent) GetFee() *big.Int { } // GetToken gets the address of the token that the recipient received. -func (s SynapseCCTPCircleRequestSent) GetToken() *string { - return nil +func (s SynapseCCTPCircleRequestSent) GetToken() string { + return s.Token.String() } +// GetEventIndex gets the tx index of the event in the block it was executed in. +func (s SynapseCCTPCircleRequestSent) GetEventIndex() uint64 { + return uint64(s.Raw.TxIndex) +} + +var _ cctp.EventLog = &SynapseCCTPCircleRequestSent{} + // GetContractAddress gets the contract address the event occurred on. func (s SynapseCCTPCircleRequestFulfilled) GetContractAddress() common.Address { return s.Raw.Address @@ -115,18 +117,23 @@ func (s SynapseCCTPCircleRequestFulfilled) GetTxHash() common.Hash { } // GetEventType gets the event type for the event. -func (s SynapseCCTPCircleRequestFulfilled) GetEventType() types.EventType { - return types.CircleRequestFulfilledEvent +func (s SynapseCCTPCircleRequestFulfilled) GetEventType() cctp.EventType { + return cctp.CircleRequestFulfilledEvent } // GetRequestID gets the unique identifier of the request. func (s SynapseCCTPCircleRequestFulfilled) GetRequestID() [32]byte { - return [32]byte{} + return s.RequestID } // GetOriginChainID gets the origin chain ID for the event. func (s SynapseCCTPCircleRequestFulfilled) GetOriginChainID() *big.Int { - return big.NewInt(int64(s.OriginDomain)) + // domain to chain mapping TODO move to static mapping + domainToChain := []int64{1, 43114, 10, 42161} + if s.OriginDomain >= uint32(len(domainToChain)) { // Catch if the domain is not in the mapping (explorer lagging behind addition of new chains) + return big.NewInt(int64(s.OriginDomain)) + } + return big.NewInt(domainToChain[s.OriginDomain]) } // GetDestinationChainID gets the destination chain ID for the event. @@ -144,24 +151,14 @@ func (s SynapseCCTPCircleRequestFulfilled) GetNonce() *uint64 { return nil } -// GetBurnToken gets the address of the Circle token that was burnt. -func (s SynapseCCTPCircleRequestFulfilled) GetBurnToken() *string { - return nil -} - // GetMintToken gets the address of the minted Circle token. func (s SynapseCCTPCircleRequestFulfilled) GetMintToken() *string { str := s.MintToken.String() return &str } -// GetSentAmount gets the amount of Circle tokens burnt. -func (s SynapseCCTPCircleRequestFulfilled) GetSentAmount() *big.Int { - return nil -} - -// GetReceivedAmount gets the received amount by the recipient. -func (s SynapseCCTPCircleRequestFulfilled) GetReceivedAmount() *big.Int { +// GetAmount is the amount from the transfer. +func (s SynapseCCTPCircleRequestFulfilled) GetAmount() *big.Int { return s.Amount } @@ -187,7 +184,13 @@ func (s SynapseCCTPCircleRequestFulfilled) GetFee() *big.Int { } // GetToken gets the address of the token that the recipient received. -func (s SynapseCCTPCircleRequestFulfilled) GetToken() *string { - str := s.Token.String() - return &str +func (s SynapseCCTPCircleRequestFulfilled) GetToken() string { + return s.Token.String() } + +// GetEventIndex gets the tx index of the event in the block it was executed in. +func (s SynapseCCTPCircleRequestFulfilled) GetEventIndex() uint64 { + return uint64(s.Raw.TxIndex) +} + +var _ cctp.EventLog = &SynapseCCTPCircleRequestFulfilled{} diff --git a/services/explorer/db/consumerinterface.go b/services/explorer/db/consumerinterface.go index 4a731ba899..42fd4634a5 100644 --- a/services/explorer/db/consumerinterface.go +++ b/services/explorer/db/consumerinterface.go @@ -15,7 +15,7 @@ type ConsumerDBWriter interface { StoreEvent(ctx context.Context, event interface{}) error // StoreEvents stores a list of events. StoreEvents(ctx context.Context, events []interface{}) error - // StoreLastBlock stores the last block number that has been backfilled for a given chain. + // StoreLastBlock stores the last block number that has been indexed for a given chain. StoreLastBlock(ctx context.Context, chainID uint32, blockNumber uint64, contractAddress string) error // StoreTokenIndex stores the token index data. StoreTokenIndex(ctx context.Context, chainID uint32, tokenIndex uint8, tokenAddress string, contractAddress string) error diff --git a/services/explorer/db/sql/model.go b/services/explorer/db/sql/model.go index 77ccc75951..4dc7a06de0 100644 --- a/services/explorer/db/sql/model.go +++ b/services/explorer/db/sql/model.go @@ -81,6 +81,8 @@ var PageSize = 100 type CCTPEvent struct { // InsertTime is the time the event was inserted into the database. InsertTime uint64 `gorm:"column:insert_time"` + // ChainID is the chain ID of the chain in which the indexed event occurred. + ChainID uint32 `gorm:"column:chain_id"` // TxHash is the transaction hash of the event. TxHash string `gorm:"column:tx_hash"` // ContractAddress is the address of the contract that generated the event. @@ -91,24 +93,25 @@ type CCTPEvent struct { EventType uint8 `gorm:"column:event_type"` // RequestID is the request ID of the CCTP transfer. RequestID string `gorm:"column:request_id"` + + // Token is either the address of the received token on destination or the address of the token burnt on origin. + Token string `gorm:"column:token"` + // Amount is the amount of the CCTP transfer. + Amount *big.Int `gorm:"column:amount;type:UInt256"` + // EventIndex is the index of the log. + EventIndex uint64 `gorm:"column:event_index"` + // AmountUSD is the amount of the CCTP transfer in USD. + AmountUSD float64 `gorm:"column:amount_usd;type:Float64"` // OriginChainID is the chain ID of the CCTP transfer. OriginChainID *big.Int `gorm:"column:origin_chain_id;type:UInt256"` // DestinationChainID is the chain ID of the CCTP transfer. DestinationChainID *big.Int `gorm:"column:destination_chain_id;type:UInt256"` - // Sender is the sender of the CCTP transfer. + // Sender is the address of the sender. Sender sql.NullString `gorm:"column:sender"` // Nonce is the nonce of the CCTP transfer. Nonce sql.NullInt64 `gorm:"column:nonce"` - // BurnToken is the burn token of the CCTP transfer. - BurnToken sql.NullString `gorm:"column:burn_token"` - // MintToken is the mint token of the CCTP transfer. + // MintToken is the address of the minted token on destination MintToken sql.NullString `gorm:"column:mint_token"` - // SentAmount is the sent amount of the CCTP transfer. - SentAmount *big.Int `gorm:"column:sent_amount;type:UInt256"` - // SentAmountUSD is the sent amount of the CCTP transfer in USD terms. - SentAmountUSD *float64 `gorm:"column:sent_amount_usd;type:Float64"` - // ReceivedAmount is the received amount of the CCTP transfer. - ReceivedAmount *big.Int `gorm:"column:received_amount;type:UInt256"` // RequestVersion is the request version of the CCTP transfer. RequestVersion sql.NullInt32 `gorm:"column:request_version"` // FormattedRequest is the formatted request of the CCTP transfer. @@ -119,8 +122,10 @@ type CCTPEvent struct { Fee *big.Int `gorm:"column:fee;type:UInt256"` // FeeUSD is the fee of the CCTP transfer in USD terms. FeeUSD *float64 `gorm:"column:fee_usd;type:Float64"` - // Token is the address of the received token. - Token sql.NullString `gorm:"column:token"` + // TokenDecimal is the token's decimal. + TokenDecimal *uint8 `gorm:"column:token_decimal"` + // TokenSymbol is the token's symbol from coin gecko. + TokenSymbol string `gorm:"column:token_symbol"` // TimeStamp is the timestamp in which the record was inserted. TimeStamp *uint64 `gorm:"column:timestamp"` } diff --git a/services/explorer/go.mod b/services/explorer/go.mod index e072890f13..30badd4e14 100644 --- a/services/explorer/go.mod +++ b/services/explorer/go.mod @@ -15,7 +15,7 @@ replace ( ) require ( - github.com/99designs/gqlgen v0.17.31 + github.com/99designs/gqlgen v0.17.36 github.com/ClickHouse/clickhouse-go/v2 v2.3.0 github.com/Flaque/filet v0.0.0-20201012163910-45f684403088 github.com/MichaelMure/go-term-markdown v0.1.4 @@ -26,7 +26,7 @@ require ( github.com/ethereum/go-ethereum v1.10.26 github.com/friendsofgo/graphiql v0.2.2 github.com/gin-gonic/gin v1.9.1 - github.com/hashicorp/golang-lru/v2 v2.0.1 + github.com/hashicorp/golang-lru/v2 v2.0.3 github.com/integralist/go-findroot v0.0.0-20160518114804-ac90681525dc github.com/ipfs/go-log v1.0.5 github.com/jftuga/ellipsis v1.0.0 @@ -40,8 +40,8 @@ require ( github.com/synapsecns/sanguine/core v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/ethergo v0.0.2 github.com/synapsecns/sanguine/services/scribe v0.0.0-00010101000000-000000000000 - github.com/urfave/cli/v2 v2.24.4 - github.com/vektah/gqlparser/v2 v2.5.1 + github.com/urfave/cli/v2 v2.25.5 + github.com/vektah/gqlparser/v2 v2.5.8 go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel/metric v1.16.0 go.uber.org/atomic v1.10.0 @@ -53,7 +53,6 @@ require ( ) require ( - bitbucket.org/tentontrain/math v0.0.0-20220519191623-a4e86beba92a // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/ClickHouse/ch-go v0.47.3 // indirect github.com/DataDog/appsec-internal-go v1.0.0 // indirect @@ -75,7 +74,6 @@ require ( github.com/agnivade/levenshtein v1.1.1 // indirect github.com/alecthomas/chroma v0.7.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect - github.com/aws/smithy-go v1.13.5 // indirect github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/briandowns/spinner v1.6.1 // indirect @@ -232,7 +230,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -267,10 +265,10 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opentelemetry.io/contrib v1.16.1 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect @@ -289,12 +287,12 @@ require ( golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 // indirect golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/services/explorer/go.sum b/services/explorer/go.sum index 597a4f3670..94972987db 100644 --- a/services/explorer/go.sum +++ b/services/explorer/go.sum @@ -1,5 +1,3 @@ -bitbucket.org/tentontrain/math v0.0.0-20220519191623-a4e86beba92a h1:6QCkYok6wNGonv0ya01Ay5uV8zT412p4wm2stFZsUQM= -bitbucket.org/tentontrain/math v0.0.0-20220519191623-a4e86beba92a/go.mod h1:irIAd6Alw5urzWaCpjWMNWxRfnhP2ABE3s5vM9BlUmw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -59,8 +57,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= -github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158= -github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4= +github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go= +github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= @@ -138,7 +136,6 @@ github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNu github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -191,8 +188,6 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad h1:kXfVkP8xPSJXzicomzjECcw6tv1Wl9h1lNenWBfNKdg= github.com/badoux/checkmail v0.0.0-20181210160741-9661bd69e9ad/go.mod h1:r5ZalvRl3tXevRNJkwIB6DC4DD3DMjIlY9NEU1XGoaQ= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -471,10 +466,8 @@ github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dT github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 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-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -665,8 +658,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= -github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= +github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -750,8 +743,6 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/josephburnett/jd v1.6.1 h1:Uzqhcje4WqvVyp85F3Oj0ezISPTlnhnr/KaLZIy8qh0= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -834,7 +825,6 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/manifoldco/promptui v0.3.0/go.mod h1:zoCNXiJnyM03LlBgTsWv8mq28s7aTC71UgKasqRJHww= github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -1102,8 +1092,8 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1221,16 +1211,16 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVH github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4= -github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= +github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= +github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -1273,14 +1263,14 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib v1.16.1 h1:EpASvVyGx6/ZTlmXzxYfTMZxHROelCeXXa2uLiwltcs= go.opentelemetry.io/contrib v1.16.1/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= @@ -1421,8 +1411,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1725,8 +1715,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/services/explorer/graphql/server/graph/partials.go b/services/explorer/graphql/server/graph/partials.go index 9b994cd36a..cfb2a0be82 100644 --- a/services/explorer/graphql/server/graph/partials.go +++ b/services/explorer/graphql/server/graph/partials.go @@ -397,6 +397,7 @@ const dailyVolumeBridgeMvPt1 = ` results[1666600000] AS harmony, results[7700] AS canto, results[2000] AS dogechain, + results[8453] AS base, arraySum(mapValues(results)) AS total FROM (SELECT date, maxMap(map(chain_id, total)) AS results FROM (SELECT coalesce(toString(b.date), toString(s.date)) AS date, @@ -437,6 +438,7 @@ const dailyVolumeBridge = ` results[1666600000] AS harmony, results[7700] AS canto, results[2000] AS dogechain, + results[8453] AS base, arraySum(mapValues(results)) AS total FROM (SELECT date, maxMap(map(chain_id, total)) AS results FROM (SELECT coalesce(toString(b.date), toString(s.date)) AS date, @@ -529,6 +531,7 @@ SELECT date, results[1666600000] AS harmony, results[7700] AS canto, results[2000] AS dogechain, + results[8453] AS base, arraySum(mapValues(results)) AS total FROM (SELECT date, maxMap(map(chain_id, total)) AS results FROM (SELECT coalesce(toString(b.date), toString(s.date), toString(m.date)) AS date, @@ -627,6 +630,7 @@ SELECT date, results[1666600000] AS harmony, results[7700] AS canto, results[2000] AS dogechain, + results[8453] AS base, arraySum(mapValues(results)) AS total FROM ( SELECT date, @@ -654,6 +658,7 @@ SELECT date, results[1666600000] AS harmony, results[7700] AS canto, results[2000] AS dogechain, + results[8453] AS base, arraySum(mapValues(results)) AS total FROM ( SELECT date, @@ -682,6 +687,7 @@ SELECT date, results[1666600000] AS harmony, results[7700] AS canto, results[2000] AS dogechain, + results[8453] AS base, arraySum(mapValues(results)) AS total FROM ( SELECT date, diff --git a/services/explorer/graphql/server/graph/queries.resolvers.go b/services/explorer/graphql/server/graph/queries.resolvers.go index a08bde5aa1..bb435ca73f 100644 --- a/services/explorer/graphql/server/graph/queries.resolvers.go +++ b/services/explorer/graphql/server/graph/queries.resolvers.go @@ -2,7 +2,7 @@ package graph // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.31 +// Code generated by github.com/99designs/gqlgen version v0.17.36 import ( "context" diff --git a/services/explorer/graphql/server/graph/resolver/server.go b/services/explorer/graphql/server/graph/resolver/server.go index 10f8bf2879..0b408fe36d 100644 --- a/services/explorer/graphql/server/graph/resolver/server.go +++ b/services/explorer/graphql/server/graph/resolver/server.go @@ -9,6 +9,7 @@ import ( "fmt" "strconv" "sync" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -248,7 +249,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -1109,25 +1110,40 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true switch rc.Operation.Operation { case ast.Query: return func(ctx context.Context) *graphql.Response { - if !first { - return nil + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, rc.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { + result := <-ec.deferredResults + atomic.AddInt32(&ec.pendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } } - first = false - ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data := ec._Query(ctx, rc.Operation.SelectionSet) var buf bytes.Buffer data.MarshalGQL(&buf) - - return &graphql.Response{ - Data: buf.Bytes(), + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + + return &response } default: @@ -1138,6 +1154,28 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema + deferred int32 + pendingDeferred int32 + deferredResults chan graphql.DeferredResult +} + +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() } func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { @@ -6150,7 +6188,7 @@ func (ec *executionContext) fieldContext_Query_bridgeTransactions(ctx context.Co ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_bridgeTransactions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6212,7 +6250,7 @@ func (ec *executionContext) fieldContext_Query_messageBusTransactions(ctx contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_messageBusTransactions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6270,7 +6308,7 @@ func (ec *executionContext) fieldContext_Query_countByChainId(ctx context.Contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_countByChainId_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6330,7 +6368,7 @@ func (ec *executionContext) fieldContext_Query_countByTokenAddress(ctx context.C ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_countByTokenAddress_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6388,7 +6426,7 @@ func (ec *executionContext) fieldContext_Query_addressRanking(ctx context.Contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_addressRanking_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6444,7 +6482,7 @@ func (ec *executionContext) fieldContext_Query_amountStatistic(ctx context.Conte ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_amountStatistic_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6538,7 +6576,7 @@ func (ec *executionContext) fieldContext_Query_dailyStatisticsByChain(ctx contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_dailyStatisticsByChain_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6596,7 +6634,7 @@ func (ec *executionContext) fieldContext_Query_rankedChainIDsByVolume(ctx contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_rankedChainIDsByVolume_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6670,7 +6708,7 @@ func (ec *executionContext) fieldContext_Query_addressData(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_addressData_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6736,7 +6774,7 @@ func (ec *executionContext) fieldContext_Query_leaderboard(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_leaderboard_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6798,7 +6836,7 @@ func (ec *executionContext) fieldContext_Query_getOriginBridgeTx(ctx context.Con ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_getOriginBridgeTx_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6860,7 +6898,7 @@ func (ec *executionContext) fieldContext_Query_getDestinationBridgeTx(ctx contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_getDestinationBridgeTx_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6934,7 +6972,7 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -8875,7 +8913,7 @@ func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, fiel ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -9063,7 +9101,7 @@ func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -9276,32 +9314,39 @@ var addressChainRankingImplementors = []string{"AddressChainRanking"} func (ec *executionContext) _AddressChainRanking(ctx context.Context, sel ast.SelectionSet, obj *model.AddressChainRanking) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, addressChainRankingImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("AddressChainRanking") case "chainID": - out.Values[i] = ec._AddressChainRanking_chainID(ctx, field, obj) - case "volumeUsd": - out.Values[i] = ec._AddressChainRanking_volumeUsd(ctx, field, obj) - case "rank": - out.Values[i] = ec._AddressChainRanking_rank(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9309,28 +9354,37 @@ var addressDailyCountImplementors = []string{"AddressDailyCount"} func (ec *executionContext) _AddressDailyCount(ctx context.Context, sel ast.SelectionSet, obj *model.AddressDailyCount) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, addressDailyCountImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("AddressDailyCount") case "date": - out.Values[i] = ec._AddressDailyCount_date(ctx, field, obj) - case "count": - out.Values[i] = ec._AddressDailyCount_count(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9338,60 +9392,53 @@ var addressDataImplementors = []string{"AddressData"} func (ec *executionContext) _AddressData(ctx context.Context, sel ast.SelectionSet, obj *model.AddressData) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, addressDataImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("AddressData") case "bridgeVolume": - out.Values[i] = ec._AddressData_bridgeVolume(ctx, field, obj) - case "bridgeFees": - out.Values[i] = ec._AddressData_bridgeFees(ctx, field, obj) - case "bridgeTxs": - out.Values[i] = ec._AddressData_bridgeTxs(ctx, field, obj) - case "swapVolume": - out.Values[i] = ec._AddressData_swapVolume(ctx, field, obj) - case "swapFees": - out.Values[i] = ec._AddressData_swapFees(ctx, field, obj) - case "swapTxs": - out.Values[i] = ec._AddressData_swapTxs(ctx, field, obj) - case "rank": - out.Values[i] = ec._AddressData_rank(ctx, field, obj) - case "earliestTx": - out.Values[i] = ec._AddressData_earliestTx(ctx, field, obj) - case "chainRanking": - out.Values[i] = ec._AddressData_chainRanking(ctx, field, obj) - case "dailyData": - out.Values[i] = ec._AddressData_dailyData(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9399,28 +9446,37 @@ var addressRankingImplementors = []string{"AddressRanking"} func (ec *executionContext) _AddressRanking(ctx context.Context, sel ast.SelectionSet, obj *model.AddressRanking) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, addressRankingImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("AddressRanking") case "address": - out.Values[i] = ec._AddressRanking_address(ctx, field, obj) - case "count": - out.Values[i] = ec._AddressRanking_count(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9428,40 +9484,43 @@ var bridgeTransactionImplementors = []string{"BridgeTransaction"} func (ec *executionContext) _BridgeTransaction(ctx context.Context, sel ast.SelectionSet, obj *model.BridgeTransaction) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, bridgeTransactionImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("BridgeTransaction") case "fromInfo": - out.Values[i] = ec._BridgeTransaction_fromInfo(ctx, field, obj) - case "toInfo": - out.Values[i] = ec._BridgeTransaction_toInfo(ctx, field, obj) - case "kappa": - out.Values[i] = ec._BridgeTransaction_kappa(ctx, field, obj) - case "pending": - out.Values[i] = ec._BridgeTransaction_pending(ctx, field, obj) - case "swapSuccess": - out.Values[i] = ec._BridgeTransaction_swapSuccess(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9469,36 +9528,41 @@ var bridgeWatcherTxImplementors = []string{"BridgeWatcherTx"} func (ec *executionContext) _BridgeWatcherTx(ctx context.Context, sel ast.SelectionSet, obj *model.BridgeWatcherTx) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, bridgeWatcherTxImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("BridgeWatcherTx") case "bridgeTx": - out.Values[i] = ec._BridgeWatcherTx_bridgeTx(ctx, field, obj) - case "pending": - out.Values[i] = ec._BridgeWatcherTx_pending(ctx, field, obj) - case "type": - out.Values[i] = ec._BridgeWatcherTx_type(ctx, field, obj) - case "kappa": - out.Values[i] = ec._BridgeWatcherTx_kappa(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9506,28 +9570,37 @@ var dateResultImplementors = []string{"DateResult"} func (ec *executionContext) _DateResult(ctx context.Context, sel ast.SelectionSet, obj *model.DateResult) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, dateResultImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("DateResult") case "date": - out.Values[i] = ec._DateResult_date(ctx, field, obj) - case "total": - out.Values[i] = ec._DateResult_total(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9535,100 +9608,73 @@ var dateResultByChainImplementors = []string{"DateResultByChain"} func (ec *executionContext) _DateResultByChain(ctx context.Context, sel ast.SelectionSet, obj *model.DateResultByChain) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, dateResultByChainImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("DateResultByChain") case "date": - out.Values[i] = ec._DateResultByChain_date(ctx, field, obj) - case "ethereum": - out.Values[i] = ec._DateResultByChain_ethereum(ctx, field, obj) - case "optimism": - out.Values[i] = ec._DateResultByChain_optimism(ctx, field, obj) - case "cronos": - out.Values[i] = ec._DateResultByChain_cronos(ctx, field, obj) - case "bsc": - out.Values[i] = ec._DateResultByChain_bsc(ctx, field, obj) - case "polygon": - out.Values[i] = ec._DateResultByChain_polygon(ctx, field, obj) - case "fantom": - out.Values[i] = ec._DateResultByChain_fantom(ctx, field, obj) - case "boba": - out.Values[i] = ec._DateResultByChain_boba(ctx, field, obj) - case "metis": - out.Values[i] = ec._DateResultByChain_metis(ctx, field, obj) - case "moonbeam": - out.Values[i] = ec._DateResultByChain_moonbeam(ctx, field, obj) - case "moonriver": - out.Values[i] = ec._DateResultByChain_moonriver(ctx, field, obj) - case "klaytn": - out.Values[i] = ec._DateResultByChain_klaytn(ctx, field, obj) - case "arbitrum": - out.Values[i] = ec._DateResultByChain_arbitrum(ctx, field, obj) - case "avalanche": - out.Values[i] = ec._DateResultByChain_avalanche(ctx, field, obj) - case "dfk": - out.Values[i] = ec._DateResultByChain_dfk(ctx, field, obj) - case "aurora": - out.Values[i] = ec._DateResultByChain_aurora(ctx, field, obj) - case "harmony": - out.Values[i] = ec._DateResultByChain_harmony(ctx, field, obj) - case "canto": - out.Values[i] = ec._DateResultByChain_canto(ctx, field, obj) - case "dogechain": - out.Values[i] = ec._DateResultByChain_dogechain(ctx, field, obj) - case "total": - out.Values[i] = ec._DateResultByChain_total(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9636,34 +9682,43 @@ var heroTypeImplementors = []string{"HeroType", "MessageType"} func (ec *executionContext) _HeroType(ctx context.Context, sel ast.SelectionSet, obj *model.HeroType) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, heroTypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("HeroType") case "recipient": - out.Values[i] = ec._HeroType_recipient(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "heroID": - out.Values[i] = ec._HeroType_heroID(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9671,32 +9726,39 @@ var historicalResultImplementors = []string{"HistoricalResult"} func (ec *executionContext) _HistoricalResult(ctx context.Context, sel ast.SelectionSet, obj *model.HistoricalResult) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, historicalResultImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("HistoricalResult") case "total": - out.Values[i] = ec._HistoricalResult_total(ctx, field, obj) - case "dateResults": - out.Values[i] = ec._HistoricalResult_dateResults(ctx, field, obj) - case "type": - out.Values[i] = ec._HistoricalResult_type(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9704,44 +9766,45 @@ var leaderboardImplementors = []string{"Leaderboard"} func (ec *executionContext) _Leaderboard(ctx context.Context, sel ast.SelectionSet, obj *model.Leaderboard) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, leaderboardImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Leaderboard") case "address": - out.Values[i] = ec._Leaderboard_address(ctx, field, obj) - case "volumeUSD": - out.Values[i] = ec._Leaderboard_volumeUSD(ctx, field, obj) - case "fees": - out.Values[i] = ec._Leaderboard_fees(ctx, field, obj) - case "txs": - out.Values[i] = ec._Leaderboard_txs(ctx, field, obj) - case "rank": - out.Values[i] = ec._Leaderboard_rank(ctx, field, obj) - case "avgVolumeUSD": - out.Values[i] = ec._Leaderboard_avgVolumeUSD(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9749,36 +9812,41 @@ var messageBusTransactionImplementors = []string{"MessageBusTransaction"} func (ec *executionContext) _MessageBusTransaction(ctx context.Context, sel ast.SelectionSet, obj *model.MessageBusTransaction) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, messageBusTransactionImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("MessageBusTransaction") case "fromInfo": - out.Values[i] = ec._MessageBusTransaction_fromInfo(ctx, field, obj) - case "toInfo": - out.Values[i] = ec._MessageBusTransaction_toInfo(ctx, field, obj) - case "pending": - out.Values[i] = ec._MessageBusTransaction_pending(ctx, field, obj) - case "messageID": - out.Values[i] = ec._MessageBusTransaction_messageID(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9786,68 +9854,57 @@ var partialInfoImplementors = []string{"PartialInfo"} func (ec *executionContext) _PartialInfo(ctx context.Context, sel ast.SelectionSet, obj *model.PartialInfo) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, partialInfoImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("PartialInfo") case "chainID": - out.Values[i] = ec._PartialInfo_chainID(ctx, field, obj) - case "destinationChainID": - out.Values[i] = ec._PartialInfo_destinationChainID(ctx, field, obj) - case "address": - out.Values[i] = ec._PartialInfo_address(ctx, field, obj) - case "txnHash": - out.Values[i] = ec._PartialInfo_txnHash(ctx, field, obj) - case "value": - out.Values[i] = ec._PartialInfo_value(ctx, field, obj) - case "formattedValue": - out.Values[i] = ec._PartialInfo_formattedValue(ctx, field, obj) - case "USDValue": - out.Values[i] = ec._PartialInfo_USDValue(ctx, field, obj) - case "tokenAddress": - out.Values[i] = ec._PartialInfo_tokenAddress(ctx, field, obj) - case "tokenSymbol": - out.Values[i] = ec._PartialInfo_tokenSymbol(ctx, field, obj) - case "blockNumber": - out.Values[i] = ec._PartialInfo_blockNumber(ctx, field, obj) - case "time": - out.Values[i] = ec._PartialInfo_time(ctx, field, obj) - case "formattedTime": - out.Values[i] = ec._PartialInfo_formattedTime(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9855,68 +9912,57 @@ var partialMessageBusInfoImplementors = []string{"PartialMessageBusInfo"} func (ec *executionContext) _PartialMessageBusInfo(ctx context.Context, sel ast.SelectionSet, obj *model.PartialMessageBusInfo) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, partialMessageBusInfoImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("PartialMessageBusInfo") case "chainID": - out.Values[i] = ec._PartialMessageBusInfo_chainID(ctx, field, obj) - case "chainName": - out.Values[i] = ec._PartialMessageBusInfo_chainName(ctx, field, obj) - case "destinationChainID": - out.Values[i] = ec._PartialMessageBusInfo_destinationChainID(ctx, field, obj) - case "destinationChainName": - out.Values[i] = ec._PartialMessageBusInfo_destinationChainName(ctx, field, obj) - case "contractAddress": - out.Values[i] = ec._PartialMessageBusInfo_contractAddress(ctx, field, obj) - case "txnHash": - out.Values[i] = ec._PartialMessageBusInfo_txnHash(ctx, field, obj) - case "message": - out.Values[i] = ec._PartialMessageBusInfo_message(ctx, field, obj) - case "messageType": - out.Values[i] = ec._PartialMessageBusInfo_messageType(ctx, field, obj) - case "blockNumber": - out.Values[i] = ec._PartialMessageBusInfo_blockNumber(ctx, field, obj) - case "time": - out.Values[i] = ec._PartialMessageBusInfo_time(ctx, field, obj) - case "formattedTime": - out.Values[i] = ec._PartialMessageBusInfo_formattedTime(ctx, field, obj) - case "revertedReason": - out.Values[i] = ec._PartialMessageBusInfo_revertedReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9924,41 +9970,48 @@ var petTypeImplementors = []string{"PetType", "MessageType"} func (ec *executionContext) _PetType(ctx context.Context, sel ast.SelectionSet, obj *model.PetType) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, petTypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("PetType") case "recipient": - out.Values[i] = ec._PetType_recipient(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "petID": - out.Values[i] = ec._PetType_petID(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "name": - out.Values[i] = ec._PetType_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -9971,7 +10024,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr }) out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ Object: field.Name, @@ -9984,7 +10037,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr case "bridgeTransactions": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -9995,16 +10048,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "messageBusTransactions": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10015,16 +10067,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "countByChainId": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10035,16 +10086,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "countByTokenAddress": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10055,16 +10105,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "addressRanking": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10075,16 +10124,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "amountStatistic": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10095,16 +10143,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "dailyStatisticsByChain": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10115,16 +10162,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "rankedChainIDsByVolume": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10135,16 +10181,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "addressData": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10155,16 +10200,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "leaderboard": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10175,16 +10219,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "getOriginBridgeTx": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10195,16 +10238,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "getDestinationBridgeTx": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -10215,32 +10257,39 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___type(ctx, field) }) - case "__schema": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___schema(ctx, field) }) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10248,34 +10297,43 @@ var tearTypeImplementors = []string{"TearType", "MessageType"} func (ec *executionContext) _TearType(ctx context.Context, sel ast.SelectionSet, obj *model.TearType) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, tearTypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("TearType") case "recipient": - out.Values[i] = ec._TearType_recipient(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "amount": - out.Values[i] = ec._TearType_amount(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10283,32 +10341,39 @@ var tokenCountResultImplementors = []string{"TokenCountResult"} func (ec *executionContext) _TokenCountResult(ctx context.Context, sel ast.SelectionSet, obj *model.TokenCountResult) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, tokenCountResultImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("TokenCountResult") case "chainID": - out.Values[i] = ec._TokenCountResult_chainID(ctx, field, obj) - case "tokenAddress": - out.Values[i] = ec._TokenCountResult_tokenAddress(ctx, field, obj) - case "count": - out.Values[i] = ec._TokenCountResult_count(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10316,28 +10381,37 @@ var transactionCountResultImplementors = []string{"TransactionCountResult"} func (ec *executionContext) _TransactionCountResult(ctx context.Context, sel ast.SelectionSet, obj *model.TransactionCountResult) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, transactionCountResultImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("TransactionCountResult") case "chainID": - out.Values[i] = ec._TransactionCountResult_chainID(ctx, field, obj) - case "count": - out.Values[i] = ec._TransactionCountResult_count(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10345,27 +10419,38 @@ var unknownTypeImplementors = []string{"UnknownType", "MessageType"} func (ec *executionContext) _UnknownType(ctx context.Context, sel ast.SelectionSet, obj *model.UnknownType) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, unknownTypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("UnknownType") case "known": - out.Values[i] = ec._UnknownType_known(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10373,24 +10458,35 @@ var valueResultImplementors = []string{"ValueResult"} func (ec *executionContext) _ValueResult(ctx context.Context, sel ast.SelectionSet, obj *model.ValueResult) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, valueResultImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("ValueResult") case "value": - out.Values[i] = ec._ValueResult_value(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10398,28 +10494,37 @@ var volumeByChainIDImplementors = []string{"VolumeByChainID"} func (ec *executionContext) _VolumeByChainID(ctx context.Context, sel ast.SelectionSet, obj *model.VolumeByChainID) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, volumeByChainIDImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("VolumeByChainID") case "chainID": - out.Values[i] = ec._VolumeByChainID_chainID(ctx, field, obj) - case "total": - out.Values[i] = ec._VolumeByChainID_total(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10427,52 +10532,55 @@ var __DirectiveImplementors = []string{"__Directive"} func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Directive") case "name": - out.Values[i] = ec.___Directive_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Directive_description(ctx, field, obj) - case "locations": - out.Values[i] = ec.___Directive_locations(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "args": - out.Values[i] = ec.___Directive_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10480,42 +10588,47 @@ var __EnumValueImplementors = []string{"__EnumValue"} func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__EnumValue") case "name": - out.Values[i] = ec.___EnumValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___EnumValue_description(ctx, field, obj) - case "isDeprecated": - out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10523,56 +10636,57 @@ var __FieldImplementors = []string{"__Field"} func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Field") case "name": - out.Values[i] = ec.___Field_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Field_description(ctx, field, obj) - case "args": - out.Values[i] = ec.___Field_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "type": - out.Values[i] = ec.___Field_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isDeprecated": - out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10580,42 +10694,47 @@ var __InputValueImplementors = []string{"__InputValue"} func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__InputValue") case "name": - out.Values[i] = ec.___InputValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___InputValue_description(ctx, field, obj) - case "type": - out.Values[i] = ec.___InputValue_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "defaultValue": - out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10623,53 +10742,54 @@ var __SchemaImplementors = []string{"__Schema"} func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Schema") case "description": - out.Values[i] = ec.___Schema_description(ctx, field, obj) - case "types": - out.Values[i] = ec.___Schema_types(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "queryType": - out.Values[i] = ec.___Schema_queryType(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "mutationType": - out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) - case "subscriptionType": - out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) - case "directives": - out.Values[i] = ec.___Schema_directives(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -10677,63 +10797,56 @@ var __TypeImplementors = []string{"__Type"} func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Type") case "kind": - out.Values[i] = ec.___Type_kind(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "name": - out.Values[i] = ec.___Type_name(ctx, field, obj) - case "description": - out.Values[i] = ec.___Type_description(ctx, field, obj) - case "fields": - out.Values[i] = ec.___Type_fields(ctx, field, obj) - case "interfaces": - out.Values[i] = ec.___Type_interfaces(ctx, field, obj) - case "possibleTypes": - out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) - case "enumValues": - out.Values[i] = ec.___Type_enumValues(ctx, field, obj) - case "inputFields": - out.Values[i] = ec.___Type_inputFields(ctx, field, obj) - case "ofType": - out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } diff --git a/services/explorer/graphql/server/server.go b/services/explorer/graphql/server/server.go index f8002581bc..a0b4f6992d 100644 --- a/services/explorer/graphql/server/server.go +++ b/services/explorer/graphql/server/server.go @@ -7,15 +7,14 @@ import ( ) func graphqlHandler(server *handler.Server) gin.HandlerFunc { - return func(c *gin.Context) { - server.ServeHTTP(c.Writer, c.Request) - } + return gin.WrapH(server) } func graphiqlHandler() gin.HandlerFunc { - h, _ := graphiql.NewGraphiqlHandler(GraphqlEndpoint) - - return func(c *gin.Context) { - h.ServeHTTP(c.Writer, c.Request) + h, err := graphiql.NewGraphiqlHandler(GraphqlEndpoint) + if err != nil { + panic(err) } + + return gin.WrapH(h) } diff --git a/services/explorer/node/explorer.go b/services/explorer/node/explorer.go index 7a5b73aff3..d148dd28f2 100644 --- a/services/explorer/node/explorer.go +++ b/services/explorer/node/explorer.go @@ -125,6 +125,8 @@ func getChainBackfiller(consumerDB db.ConsumerDB, chainConfig config.ChainConfig var messageBusParser *parser.MessageBusParser var cctpParser *parser.CCTPParser var swapService fetcherpkg.SwapService + var cctpService fetcherpkg.CCTPService + swapParsers := make(map[common.Address]*parser.SwapParser) for i := range chainConfig.Contracts { @@ -164,7 +166,11 @@ func getChainBackfiller(consumerDB db.ConsumerDB, chainConfig config.ChainConfig return nil, fmt.Errorf("could not create message bus parser: %w", err) } case "cctp": - cctpParser, err = parser.NewCCTPParser(consumerDB, common.HexToAddress(chainConfig.Contracts[i].Address), fetcher, priceDataService) + cctpService, err = fetcherpkg.NewCCTPFetcher(common.HexToAddress(chainConfig.Contracts[i].Address), client) + if err != nil || swapService == nil { + return nil, fmt.Errorf("could not create cctpService: %w", err) + } + cctpParser, err = parser.NewCCTPParser(consumerDB, common.HexToAddress(chainConfig.Contracts[i].Address), fetcher, cctpService, tokenDataService, priceDataService) if err != nil || cctpParser == nil { return nil, fmt.Errorf("could not create message bus parser: %w", err) } diff --git a/services/explorer/node/explorer_test.go b/services/explorer/node/explorer_test.go index 96b897ba30..2d18b2e4a2 100644 --- a/services/explorer/node/explorer_test.go +++ b/services/explorer/node/explorer_test.go @@ -93,7 +93,7 @@ func (c NodeSuite) TestLive() { // go through each contract and save the end height in scribe for i := range contracts { // the last block store per contract - err := c.eventDB.StoreLastIndexed(c.GetTestContext(), common.HexToAddress(contracts[i].Address), k, 12) + err := c.eventDB.StoreLastIndexed(c.GetTestContext(), common.HexToAddress(contracts[i].Address), k, 12, false) Nil(c.T(), err) } c.fillBlocks(bridgeRef, swapRefA, swapRefB, transactOpts, k) diff --git a/services/explorer/static/chainIDs.yaml b/services/explorer/static/chainIDs.yaml index c7961aa118..8508b46383 100644 --- a/services/explorer/static/chainIDs.yaml +++ b/services/explorer/static/chainIDs.yaml @@ -15,3 +15,4 @@ 1313161554: 'Aurora' 1666600000: 'Harmony' 7700: 'Canto' +8453: 'Base' diff --git a/services/explorer/types/bridge/eventtype.go b/services/explorer/types/bridge/eventtype.go index 8262609791..b421670e8a 100644 --- a/services/explorer/types/bridge/eventtype.go +++ b/services/explorer/types/bridge/eventtype.go @@ -13,32 +13,36 @@ type EventType uint8 const ( // DepositEvent is the token deposit event. - DepositEvent EventType = iota + DepositEvent EventType = iota // Origin // RedeemEvent is the token redeem event. - RedeemEvent + RedeemEvent // Origin // WithdrawEvent is the token withdraw event. - WithdrawEvent + WithdrawEvent // Destination // MintEvent is the token mint event. - MintEvent + MintEvent // Destination // DepositAndSwapEvent is the token deposit and swap event. - DepositAndSwapEvent + DepositAndSwapEvent // Origin // MintAndSwapEvent is the token mint and swap event. - MintAndSwapEvent + MintAndSwapEvent // Destination // RedeemAndSwapEvent is the token redeem and swap event. - RedeemAndSwapEvent + RedeemAndSwapEvent // Origin // RedeemAndRemoveEvent is the token redeem and remove event. - RedeemAndRemoveEvent + RedeemAndRemoveEvent // Origin // WithdrawAndRemoveEvent is the token withdraw and remove event. - WithdrawAndRemoveEvent + WithdrawAndRemoveEvent // Destination // RedeemV2Event is the token redeem v2 event. - RedeemV2Event + RedeemV2Event // Origin + // CircleRequestSentEvent is emitted when the origin bridge event is executed using the cctp contract. + CircleRequestSentEvent // Origin + // CircleRequestFulfilledEvent is emitted when the destination bridge event is executed using the cctp contract. + CircleRequestFulfilledEvent // Destination ) // AllEventTypes is a list of the event types. func AllEventTypes() []EventType { return []EventType{DepositEvent, RedeemEvent, WithdrawEvent, MintEvent, DepositAndSwapEvent, MintAndSwapEvent, RedeemAndSwapEvent, RedeemAndRemoveEvent, - WithdrawAndRemoveEvent, RedeemV2Event} + WithdrawAndRemoveEvent, RedeemV2Event, CircleRequestSentEvent, CircleRequestFulfilledEvent} } // Int gets the int value of the event type. @@ -46,18 +50,6 @@ func (i EventType) Int() uint8 { return uint8(i) } -// BridgeInitiated determines whether or not the event type is initiated by the bridge -// (as opposed to the user). -func (i EventType) BridgeInitiated() bool { - switch i { - case DepositEvent, RedeemEvent, RedeemAndRemoveEvent, DepositAndSwapEvent, RedeemAndSwapEvent, RedeemV2Event: - return false - case WithdrawEvent, MintEvent, MintAndSwapEvent, WithdrawAndRemoveEvent: - return true - } - panic("unknown event") -} - // GormDataType gets the data type to use for gorm. func (i EventType) GormDataType() string { return dbcommon.EnumDataType diff --git a/services/explorer/types/bridge/eventtype_string.go b/services/explorer/types/bridge/eventtype_string.go index f39e99e95e..940310b78d 100644 --- a/services/explorer/types/bridge/eventtype_string.go +++ b/services/explorer/types/bridge/eventtype_string.go @@ -18,11 +18,13 @@ func _() { _ = x[RedeemAndRemoveEvent-7] _ = x[WithdrawAndRemoveEvent-8] _ = x[RedeemV2Event-9] + _ = x[CircleRequestSentEvent-10] + _ = x[CircleRequestFulfilledEvent-11] } -const _EventType_name = "DepositEventRedeemEventWithdrawEventMintEventDepositAndSwapEventMintAndSwapEventRedeemAndSwapEventRedeemAndRemoveEventWithdrawAndRemoveEventRedeemV2Event" +const _EventType_name = "DepositEventRedeemEventWithdrawEventMintEventDepositAndSwapEventMintAndSwapEventRedeemAndSwapEventRedeemAndRemoveEventWithdrawAndRemoveEventRedeemV2EventCircleRequestSentEventCircleRequestFulfilledEvent" -var _EventType_index = [...]uint8{0, 12, 23, 36, 45, 64, 80, 98, 118, 140, 153} +var _EventType_index = [...]uint8{0, 12, 23, 36, 45, 64, 80, 98, 118, 140, 153, 175, 202} func (i EventType) String() string { if i >= EventType(len(_EventType_index)-1) { diff --git a/services/explorer/types/cctp/event.go b/services/explorer/types/cctp/event.go index 3519913e5a..158dfff952 100644 --- a/services/explorer/types/cctp/event.go +++ b/services/explorer/types/cctp/event.go @@ -10,16 +10,22 @@ import ( // //nolint:interfacebloat type EventLog interface { + // GetTxHash returns the transaction hash of the log. + GetTxHash() common.Hash // GetContractAddress returns the contract address of the log. GetContractAddress() common.Address // GetBlockNumber returns the block number of the log. GetBlockNumber() uint64 - // GetTxHash returns the transaction hash of the log. - GetTxHash() common.Hash // GetEventType returns the event type of the log. GetEventType() EventType + // GetEventIndex returns the index of the log. + GetEventIndex() uint64 // GetRequestID returns the request id of the CCTP transfer. GetRequestID() [32]byte + // GetToken returns the address of the received token. + GetToken() string + // GetAmount returns the amount of the CCTP transfer. + GetAmount() *big.Int // GetOriginChainID returns the chain id of the CCTP transfer. GetOriginChainID() *big.Int // GetDestinationChainID returns the chain id of the CCTP transfer. @@ -28,14 +34,9 @@ type EventLog interface { GetSender() *string // GetNonce returns the nonce of the CCTP transfer. GetNonce() *uint64 - // GetBurnToken returns the burn token of the CCTP transfer. - GetBurnToken() *string // GetMintToken returns the mint token of the CCTP transfer. GetMintToken() *string - // GetSentAmount returns the sent amount of the CCTP transfer. - GetSentAmount() *big.Int - // GetReceivedAmount returns the received amount of the CCTP transfer. - GetReceivedAmount() *big.Int + // GetRequestVersion returns the request version of the CCTP transfer. GetRequestVersion() *uint32 // GetFormattedRequest returns the formatted request of the CCTP transfer. @@ -44,6 +45,4 @@ type EventLog interface { GetRecipient() *string // GetFee returns the fee of the CCTP transfer. GetFee() *big.Int - // GetToken returns the address of the received token. - GetToken() *string } diff --git a/services/explorer/types/cctp/eventtype.go b/services/explorer/types/cctp/eventtype.go index e65514ca3e..fa0ac59105 100644 --- a/services/explorer/types/cctp/eventtype.go +++ b/services/explorer/types/cctp/eventtype.go @@ -6,7 +6,7 @@ package cctp type EventType uint8 const ( - // CircleRequestSentEvent is emitted when a Circle token is sent with an attached action request.. + // CircleRequestSentEvent is emitted when a Circle token is sent with an attached action request. CircleRequestSentEvent EventType = iota // CircleRequestFulfilledEvent is emitted when a Circle token is received with an attached action request. CircleRequestFulfilledEvent diff --git a/services/omnirpc/chainmanager/manager.go b/services/omnirpc/chainmanager/manager.go index 4399119f8c..44ccf0cd4e 100644 --- a/services/omnirpc/chainmanager/manager.go +++ b/services/omnirpc/chainmanager/manager.go @@ -2,13 +2,20 @@ package chainmanager import ( "context" + "fmt" + "github.com/ipfs/go-log" + "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/services/omnirpc/config" "github.com/synapsecns/sanguine/services/omnirpc/rpcinfo" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "sort" "sync" "time" ) +var logger = log.Logger("chainmanager") + // rpcTimeout is how long to wait for a response. const rpcTimeout = time.Second * 5 @@ -25,19 +32,22 @@ type ChainManager interface { } // NewChainManager creates a new chain manager. -func NewChainManager() ChainManager { +func NewChainManager(handler metrics.Handler) ChainManager { return &chainManager{ chainList: make(map[uint32]*chain), // mux is used to prevent parallel manipulations to the map mux: sync.RWMutex{}, + // handler is the metrics handler + handler: handler, } } // NewChainManagerFromConfig creates a new chain manager. -func NewChainManagerFromConfig(configuration config.Config) ChainManager { +func NewChainManagerFromConfig(configuration config.Config, handler metrics.Handler) ChainManager { cm := &chainManager{ chainList: make(map[uint32]*chain), mux: sync.RWMutex{}, + handler: handler, } for chainID, chn := range configuration.Chains { @@ -64,6 +74,11 @@ func NewChainManagerFromConfig(configuration config.Config) ChainManager { } } + err := cm.setupMetrics() + if err != nil { + logger.Errorf("could not setup metrics: %v", err) + } + return cm } @@ -71,6 +86,7 @@ func NewChainManagerFromConfig(configuration config.Config) ChainManager { type chainManager struct { chainList map[uint32]*chain mux sync.RWMutex + handler metrics.Handler } func (c *chainManager) GetChain(chainID uint32) Chain { @@ -129,13 +145,68 @@ func (c *chainManager) RefreshRPCInfo(ctx context.Context, chainID uint32) { } rpcURLS := chainList.URLs() - rpcInfoList := sortInfoList(rpcinfo.GetRPCLatency(ctx, rpcTimeout, rpcURLS)) + rpcInfoList := sortInfoList(rpcinfo.GetRPCLatency(ctx, rpcTimeout, rpcURLS, c.handler)) c.mux.Lock() c.chainList[chainID].rpcs = rpcInfoList c.mux.Unlock() } +const ( + meter = "github.com/synapsecns/sanguine/services/omnirpc/chainmanager" + blockNumberMetric = "block_number" + latencyMetric = "latency" + blockAgeMetric = "block_age" +) + +// records metrics for various rpcs. Should only be called once. +// +// note: because of missing support for https://github.com/open-telemetry/opentelemetry-specification/issues/2318 +// this is done from the struct rather than recorded at refresh time. +// +// in a future version, this should be a synchronous gauge. +func (c *chainManager) setupMetrics() error { + meterMaid := c.handler.Meter(meter) + blockGauge, err := meterMaid.Int64ObservableGauge(blockNumberMetric) + if err != nil { + return fmt.Errorf("could not create histogram: %w", err) + } + + latencyGauge, err := meterMaid.Float64ObservableGauge(latencyMetric, metric.WithUnit("seconds")) + if err != nil { + return fmt.Errorf("could not create histogram: %w", err) + } + + ageGauge, err := meterMaid.Float64ObservableGauge(blockAgeMetric, metric.WithUnit("seconds")) + if err != nil { + return fmt.Errorf("could not create histogram: %w", err) + } + + if _, err := meterMaid.RegisterCallback(func(parentCtx context.Context, o metric.Observer) (err error) { + c.mux.RLock() + defer c.mux.RUnlock() + + for chainID, chainInfo := range c.chainList { + for _, rpc := range chainInfo.rpcs { + attributeSet := attribute.NewSet(attribute.Int64(metrics.ChainID, int64(chainID)), attribute.String("rpc_url", rpc.URL)) + + if rpc.HasError { + continue + } + + o.ObserveInt64(blockGauge, int64(rpc.BlockNumber), metric.WithAttributeSet(attributeSet)) + o.ObserveFloat64(latencyGauge, rpc.Latency.Seconds(), metric.WithAttributeSet(attributeSet)) + o.ObserveFloat64(ageGauge, rpc.BlockAge.Seconds(), metric.WithAttributeSet(attributeSet)) + } + } + + return nil + }, blockGauge, latencyGauge, ageGauge); err != nil { + return fmt.Errorf("could not register callback for gauges: %w", err) + } + return nil +} + func sortInfoList(rpcInfoList []rpcinfo.Result) []rpcinfo.Result { sort.Slice(rpcInfoList, func(i, j int) bool { // ignore latencies with an error diff --git a/services/omnirpc/chainmanager/manager_test.go b/services/omnirpc/chainmanager/manager_test.go index 99a458e034..bc3998392a 100644 --- a/services/omnirpc/chainmanager/manager_test.go +++ b/services/omnirpc/chainmanager/manager_test.go @@ -6,7 +6,9 @@ import ( "github.com/brianvoe/gofakeit/v6" "github.com/richardwilkes/toolbox/collection" . "github.com/stretchr/testify/assert" + "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/services/omnirpc/chainmanager" + "github.com/synapsecns/sanguine/services/omnirpc/metadata" "github.com/synapsecns/sanguine/services/omnirpc/rpcinfo" "sort" "testing" @@ -14,7 +16,10 @@ import ( ) func TestRefreshRPCInfoNil(t *testing.T) { - cm := chainmanager.NewChainManager() + nullHandler, err := metrics.NewByType(context.Background(), metadata.BuildInfo(), metrics.Null) + NoError(t, err) + + cm := chainmanager.NewChainManager(nullHandler) // make sure we don't panic if the chain is not nil NotPanics(t, func() { @@ -60,7 +65,10 @@ func TestSortInfoList(t *testing.T) { } func TestGetChainIDs(t *testing.T) { - cm := chainmanager.NewChainManager() + nullHandler, err := metrics.NewByType(context.Background(), metadata.BuildInfo(), metrics.Null) + NoError(t, err) + + cm := chainmanager.NewChainManager(nullHandler) chainIDs := collection.Set[uint32]{} diff --git a/services/omnirpc/client/suite_test.go b/services/omnirpc/client/suite_test.go index c3678ce658..6e321b47c4 100644 --- a/services/omnirpc/client/suite_test.go +++ b/services/omnirpc/client/suite_test.go @@ -10,7 +10,7 @@ import ( "github.com/synapsecns/sanguine/ethergo/backends" "github.com/synapsecns/sanguine/ethergo/backends/geth" "github.com/synapsecns/sanguine/services/omnirpc/client" - "github.com/synapsecns/sanguine/services/omnirpc/cmd" + "github.com/synapsecns/sanguine/services/omnirpc/metadata" "github.com/synapsecns/sanguine/services/omnirpc/testhelper" "golang.org/x/sync/errgroup" "math/big" @@ -69,7 +69,7 @@ func (s *TestClientSuite) SetupJaeger() { localmetrics.SetupTestJaeger(s.GetSuiteContext(), s.T()) var err error - s.metrics, err = metrics.NewByType(s.GetSuiteContext(), cmd.BuildInfo(), metrics.Jaeger) + s.metrics, err = metrics.NewByType(s.GetSuiteContext(), metadata.BuildInfo(), metrics.Jaeger) s.Require().Nil(err) } diff --git a/services/omnirpc/cmd/commands.go b/services/omnirpc/cmd/commands.go index 49742bc940..c6f20a19e0 100644 --- a/services/omnirpc/cmd/commands.go +++ b/services/omnirpc/cmd/commands.go @@ -30,7 +30,7 @@ var latencyCommand = &cli.Command{ return fmt.Errorf("could not get chain config for chain %d", c.Int(chainIDFlag.Name)) } - res := rpcinfo.GetRPCLatency(c.Context, time.Second*5, chainConfig.RPCs) + res := rpcinfo.GetRPCLatency(c.Context, time.Second*5, chainConfig.RPCs, metrics.Get()) DisplayLatency(res) return nil diff --git a/services/omnirpc/go.mod b/services/omnirpc/go.mod index 2ad78533e2..8fedd8ef99 100644 --- a/services/omnirpc/go.mod +++ b/services/omnirpc/go.mod @@ -37,9 +37,10 @@ require ( github.com/synapsecns/fasthttp-http2 v1.0.0 github.com/synapsecns/sanguine/core v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/ethergo v0.0.2 - github.com/urfave/cli/v2 v2.24.4 + github.com/urfave/cli/v2 v2.25.5 github.com/valyala/fasthttp v1.41.0 go.opentelemetry.io/otel v1.16.0 + go.opentelemetry.io/otel/metric v1.16.0 go.opentelemetry.io/otel/trace v1.16.0 go.uber.org/automaxprocs v1.5.1 golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 @@ -212,7 +213,7 @@ require ( github.com/rung/go-safecast v1.0.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.8.1 // indirect @@ -246,16 +247,15 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.16.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect - go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/sdk v1.16.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect @@ -266,13 +266,13 @@ require ( go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/services/omnirpc/go.sum b/services/omnirpc/go.sum index da45f86a36..ed9e7213a1 100644 --- a/services/omnirpc/go.sum +++ b/services/omnirpc/go.sum @@ -1042,8 +1042,8 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1155,8 +1155,8 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVH github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= @@ -1208,12 +1208,12 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= @@ -1350,8 +1350,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1652,8 +1652,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/services/omnirpc/main.go b/services/omnirpc/main.go index b4a8c551ad..4916c3a820 100644 --- a/services/omnirpc/main.go +++ b/services/omnirpc/main.go @@ -3,10 +3,11 @@ package main import ( "github.com/synapsecns/sanguine/services/omnirpc/cmd" + "github.com/synapsecns/sanguine/services/omnirpc/metadata" _ "go.uber.org/automaxprocs" "os" ) func main() { - cmd.Start(os.Args, cmd.BuildInfo()) + cmd.Start(os.Args, metadata.BuildInfo()) } diff --git a/services/omnirpc/cmd/buildinfo.go b/services/omnirpc/metadata/buildinfo.go similarity index 94% rename from services/omnirpc/cmd/buildinfo.go rename to services/omnirpc/metadata/buildinfo.go index fe4005f6df..3441bc1c6b 100644 --- a/services/omnirpc/cmd/buildinfo.go +++ b/services/omnirpc/metadata/buildinfo.go @@ -1,4 +1,4 @@ -package cmd +package metadata import "github.com/synapsecns/sanguine/core/config" diff --git a/services/omnirpc/metadata/doc.go b/services/omnirpc/metadata/doc.go new file mode 100644 index 0000000000..ba80ffa6ef --- /dev/null +++ b/services/omnirpc/metadata/doc.go @@ -0,0 +1,2 @@ +// Package metadata is the entry point for the omnirpc service. +package metadata diff --git a/services/omnirpc/proxy/server.go b/services/omnirpc/proxy/server.go index e19e2511c4..74207bd090 100644 --- a/services/omnirpc/proxy/server.go +++ b/services/omnirpc/proxy/server.go @@ -45,7 +45,7 @@ func NewProxy(config config.Config, handler metrics.Handler) *RPCProxy { } return &RPCProxy{ - chainManager: chainmanager.NewChainManagerFromConfig(config), + chainManager: chainmanager.NewChainManagerFromConfig(config, handler), refreshInterval: time.Second * time.Duration(config.RefreshInterval), port: config.Port, client: omniHTTP.NewClient(omniHTTP.ClientTypeFromString(config.ClientType)), @@ -60,7 +60,6 @@ func (r *RPCProxy) Run(ctx context.Context) { router := ginhelper.New(logger) router.Use(r.handler.Gin()) - router.GET(ginhelper.MetricsEndpoint, gin.WrapH(r.handler.Handler())) router.POST("/rpc/:id", func(c *gin.Context) { chainID, err := strconv.Atoi(c.Param("id")) @@ -121,23 +120,33 @@ func (r *RPCProxy) startProxyLoop(ctx context.Context) { case <-ctx.Done(): return case <-time.After(waitTime): - var wg sync.WaitGroup + r.benchmarkProxies(ctx) - for _, chainID := range r.chainManager.GetChainIDs() { - wg.Add(1) + waitTime = scanInterval + } + } +} - go func(chainID uint32) { - r.chainManager.RefreshRPCInfo(ctx, chainID) +// benchmarkProxies benchmarks all proxies. +func (r *RPCProxy) benchmarkProxies(parentCtx context.Context) { + ctx, span := r.handler.Tracer().Start(parentCtx, "benchmarkProxies") + defer func() { + span.End() + }() - wg.Done() - }(chainID) - } + var wg sync.WaitGroup - wg.Wait() + for _, chainID := range r.chainManager.GetChainIDs() { + wg.Add(1) - waitTime = scanInterval - } + go func(ctx context.Context, chainID uint32) { + r.chainManager.RefreshRPCInfo(ctx, chainID) + + wg.Done() + }(ctx, chainID) } + + wg.Wait() } // Port gets the port the proxy is running on. diff --git a/services/omnirpc/proxy/suite_test.go b/services/omnirpc/proxy/suite_test.go index dbdad563ce..7aac3584f1 100644 --- a/services/omnirpc/proxy/suite_test.go +++ b/services/omnirpc/proxy/suite_test.go @@ -6,7 +6,7 @@ import ( "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/core/metrics/localmetrics" "github.com/synapsecns/sanguine/core/testsuite" - "github.com/synapsecns/sanguine/services/omnirpc/cmd" + "github.com/synapsecns/sanguine/services/omnirpc/metadata" "testing" ) @@ -29,7 +29,7 @@ func (p *ProxySuite) SetupSuite() { localmetrics.SetupTestJaeger(p.GetSuiteContext(), p.T()) var err error - p.metrics, err = metrics.NewByType(p.GetSuiteContext(), cmd.BuildInfo(), metrics.Jaeger) + p.metrics, err = metrics.NewByType(p.GetSuiteContext(), metadata.BuildInfo(), metrics.Jaeger) assert.Nil(p.T(), err) } diff --git a/services/omnirpc/rpcinfo/export_test.go b/services/omnirpc/rpcinfo/export_test.go index 2d8c861e78..9e6da51f33 100644 --- a/services/omnirpc/rpcinfo/export_test.go +++ b/services/omnirpc/rpcinfo/export_test.go @@ -1,8 +1,11 @@ package rpcinfo -import "context" +import ( + "context" + "github.com/synapsecns/sanguine/core/metrics" +) // GetLatency gets the latency on a chain. func GetLatency(ctx context.Context, rpcURL string) (l Result) { - return getLatency(ctx, rpcURL) + return getLatency(ctx, rpcURL, metrics.NewNullHandler()) } diff --git a/services/omnirpc/rpcinfo/latency.go b/services/omnirpc/rpcinfo/latency.go index 0b4c960d2c..b8526816f3 100644 --- a/services/omnirpc/rpcinfo/latency.go +++ b/services/omnirpc/rpcinfo/latency.go @@ -4,7 +4,12 @@ import ( "context" "errors" "fmt" - "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/core/types" + "github.com/lmittmann/w3/module/eth" + "github.com/synapsecns/sanguine/core/metrics" + ethClient "github.com/synapsecns/sanguine/ethergo/client" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "golang.org/x/exp/slices" "golang.org/x/sync/errgroup" "net/url" @@ -20,6 +25,8 @@ type Result struct { Latency time.Duration // BlockAge is the age of the block BlockAge time.Duration + // BlockNumber is the block number + BlockNumber uint64 // HasError is wether or not the result has an error HasError bool // Error is the error recevied when trying to establish latency @@ -27,18 +34,23 @@ type Result struct { } // GetRPCLatency gets latency from a list of rpcs. -func GetRPCLatency(parentCtx context.Context, timeout time.Duration, rpcList []string) (latSlice []Result) { +func GetRPCLatency(parentCtx context.Context, timeout time.Duration, rpcList []string, handler metrics.Handler) (latSlice []Result) { var mux sync.Mutex timeCtx, cancel := context.WithTimeout(parentCtx, timeout) - defer cancel() - g, ctx := errgroup.WithContext(timeCtx) + traceCtx, span := handler.Tracer().Start(timeCtx, "rpcinfo.GetRPCLatency", trace.WithAttributes(attribute.StringSlice("rpcList", rpcList))) + defer func() { + metrics.EndSpan(span) + cancel() + }() + + g, ctx := errgroup.WithContext(traceCtx) for _, rpcURL := range rpcList { // capture func literal rpcURL := rpcURL g.Go(func() error { - latency := getLatency(ctx, rpcURL) + latency := getLatency(ctx, rpcURL, handler) mux.Lock() latSlice = append(latSlice, latency) @@ -53,7 +65,7 @@ func GetRPCLatency(parentCtx context.Context, timeout time.Duration, rpcList []s return latSlice } -func getLatency(ctx context.Context, rpcURL string) (l Result) { +func getLatency(ctx context.Context, rpcURL string, handler metrics.Handler) (l Result) { l = Result{URL: rpcURL, HasError: true} parsedURL, err := url.Parse(rpcURL) @@ -70,15 +82,23 @@ func getLatency(ctx context.Context, rpcURL string) (l Result) { startTime := time.Now() - client, err := ethclient.DialContext(ctx, rpcURL) + client, err := ethClient.DialBackend(ctx, rpcURL, handler) if err != nil { - l.Error = fmt.Errorf("could not connect to %s: %w", rpcURL, err) + l.Error = fmt.Errorf("could not create client: %w", err) return l } - latestHeader, err := client.HeaderByNumber(ctx, nil) + var chainID uint64 + var latestHeader types.Header + + err = client.BatchWithContext(ctx, + eth.ChainID().Returns(&chainID), + eth.HeaderByNumber(nil).Returns(&latestHeader), + ) + if err != nil { - l.Error = fmt.Errorf("could not get header from %s: %w", rpcURL, err) + l.Error = err + l.HasError = true return l } @@ -87,7 +107,9 @@ func getLatency(ctx context.Context, rpcURL string) (l Result) { l.Latency = endTime.Sub(startTime) l.BlockAge = endTime.Sub(time.Unix(int64(latestHeader.Time), 0)) + l.BlockNumber = latestHeader.Number.Uint64() l.HasError = false + return l } diff --git a/services/omnirpc/rpcinfo/latency_test.go b/services/omnirpc/rpcinfo/latency_test.go index 8e234871f4..46e6f8ff76 100644 --- a/services/omnirpc/rpcinfo/latency_test.go +++ b/services/omnirpc/rpcinfo/latency_test.go @@ -3,6 +3,7 @@ package rpcinfo_test import ( "context" . "github.com/stretchr/testify/assert" + "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/ethergo/backends/geth" "github.com/synapsecns/sanguine/ethergo/backends/preset" "github.com/synapsecns/sanguine/services/omnirpc/rpcinfo" @@ -26,7 +27,7 @@ func (r *LatencySuite) TestRPCLatency() { }) Nil(r.T(), g.Wait()) - latencySlice := rpcinfo.GetRPCLatency(r.GetTestContext(), time.Second*3, []string{bsc.HTTPEndpoint(), avalanche.HTTPEndpoint()}) + latencySlice := rpcinfo.GetRPCLatency(r.GetTestContext(), time.Second*3, []string{bsc.HTTPEndpoint(), avalanche.HTTPEndpoint()}, metrics.NewNullHandler()) NotEqual(r.T(), latencySlice[0].URL, latencySlice[1].URL) for _, latencyData := range latencySlice { False(r.T(), latencyData.HasError) diff --git a/services/omnirpc/testhelper/server.go b/services/omnirpc/testhelper/server.go index 1237a48c71..3e1895f13f 100644 --- a/services/omnirpc/testhelper/server.go +++ b/services/omnirpc/testhelper/server.go @@ -10,9 +10,9 @@ import ( "github.com/synapsecns/sanguine/core/metrics/localmetrics" "github.com/synapsecns/sanguine/core/testsuite" "github.com/synapsecns/sanguine/ethergo/backends" - "github.com/synapsecns/sanguine/services/omnirpc/cmd" "github.com/synapsecns/sanguine/services/omnirpc/config" omniHTTP "github.com/synapsecns/sanguine/services/omnirpc/http" + "github.com/synapsecns/sanguine/services/omnirpc/metadata" "github.com/synapsecns/sanguine/services/omnirpc/proxy" "net/http" "testing" @@ -46,7 +46,7 @@ func NewOmnirpcServer(ctx context.Context, tb testing.TB, backends ...backends.S localmetrics.SetupTestJaeger(ctx, tb) - handler, err := metrics.NewByType(ctx, cmd.BuildInfo(), metrics.Jaeger) + handler, err := metrics.NewByType(ctx, metadata.BuildInfo(), metrics.Jaeger) assert.Nil(tb, err) server := proxy.NewProxy(makeConfig(backends, omniHTTP.FastHTTP), handler) diff --git a/services/scribe/api/data_test.go b/services/scribe/api/data_test.go index cd9947e036..1012c691c0 100644 --- a/services/scribe/api/data_test.go +++ b/services/scribe/api/data_test.go @@ -1,8 +1,6 @@ package api_test import ( - "math/big" - "github.com/brianvoe/gofakeit/v6" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -10,6 +8,8 @@ import ( "github.com/synapsecns/sanguine/services/scribe/db" "github.com/synapsecns/sanguine/services/scribe/graphql" "github.com/synapsecns/sanguine/services/scribe/grpc/client/rest" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + "math/big" ) func (g APISuite) TestRetrieveData() { @@ -200,9 +200,9 @@ func (g APISuite) TestTransactionDataEquality() { func (g APISuite) TestBlockTimeDataEquality() { // create data for storing a block time - chainID := gofakeit.Uint32() - blockNumber := uint64(gofakeit.Uint32()) - blockTime := uint64(gofakeit.Uint32()) + chainID := uint32(1) + blockNumber := uint64(1000000) + blockTime := uint64(1455404053) // store block time err := g.db.StoreBlockTime(g.GetTestContext(), chainID, blockNumber, blockTime) @@ -283,7 +283,7 @@ func (g APISuite) TestLastContractIndexed() { contractAddress := common.BigToAddress(big.NewInt(gofakeit.Int64())) // store last indexed - err := g.db.StoreLastIndexed(g.GetTestContext(), contractAddress, chainID, blockNumber) + err := g.db.StoreLastIndexed(g.GetTestContext(), contractAddress, chainID, blockNumber, scribeTypes.IndexingConfirmed) Nil(g.T(), err) // retrieve last indexed diff --git a/services/scribe/api/server.go b/services/scribe/api/server.go index 05718326e8..a264324f9f 100644 --- a/services/scribe/api/server.go +++ b/services/scribe/api/server.go @@ -44,13 +44,17 @@ var logger = log.Logger("scribe-api") // Start starts the api server. func Start(ctx context.Context, cfg Config, handler metrics.Handler) error { + logger.Warnf("starting api server") router := ginhelper.New(logger) + // wrap gin with metrics + router.GET(ginhelper.MetricsEndpoint, gin.WrapH(handler.Handler())) eventDB, err := InitDB(ctx, cfg.Database, cfg.Path, handler, cfg.SkipMigrations) if err != nil { return fmt.Errorf("could not initialize database: %w", err) } + router.Use(handler.Gin()) gqlServer.EnableGraphql(router, eventDB, cfg.OmniRPCURL, handler) grpcServer, err := server.SetupGRPCServer(ctx, router, eventDB, handler) if err != nil { @@ -104,6 +108,7 @@ func Start(ctx context.Context, cfg Config, handler metrics.Handler) error { <-ctx.Done() grpcServer.Stop() m.Close() + logger.Errorf("grpc server stopped") return nil }) @@ -119,6 +124,8 @@ func Start(ctx context.Context, cfg Config, handler metrics.Handler) error { // InitDB initializes a database given a database type and path. // TODO: use enum for database type. func InitDB(ctx context.Context, databaseType string, path string, metrics metrics.Handler, skipMigrations bool) (db.EventDB, error) { + logger.Warnf("Starting database connection from api") + switch { case databaseType == "sqlite": sqliteStore, err := sqlite.NewSqliteStore(ctx, path, metrics, skipMigrations) diff --git a/services/scribe/api/suite_test.go b/services/scribe/api/suite_test.go index 91ba88efc3..1494b3297a 100644 --- a/services/scribe/api/suite_test.go +++ b/services/scribe/api/suite_test.go @@ -66,6 +66,7 @@ func (g *APISuite) TearDownSuite() { func (g *APISuite) SetupTest() { g.TestSuite.SetupTest() g.dbPath = filet.TmpDir(g.T(), "") + g.SetTestTimeout(time.Minute * 3) sqliteStore, err := sqlite.NewSqliteStore(g.GetTestContext(), g.dbPath, g.metrics, false) Nil(g.T(), err) @@ -78,10 +79,11 @@ func (g *APISuite) SetupTest() { go func() { Nil(g.T(), api.Start(g.GetSuiteContext(), api.Config{ - Port: uint16(port), - Database: "sqlite", - Path: g.dbPath, - OmniRPCURL: "https://rpc.interoperability.institute/confirmations/1/rpc", + Port: uint16(port), + Database: "sqlite", + Path: g.dbPath, + OmniRPCURL: "https://rpc.omnirpc.io/confirmations/1/rpc", + SkipMigrations: true, }, g.metrics)) }() diff --git a/services/scribe/backfill/backend.go b/services/scribe/backend/backend.go similarity index 95% rename from services/scribe/backfill/backend.go rename to services/scribe/backend/backend.go index 5742cf8376..fd5c27fef2 100644 --- a/services/scribe/backfill/backend.go +++ b/services/scribe/backend/backend.go @@ -1,8 +1,10 @@ -package backfill +package backend import ( "context" "fmt" + "math/big" + "github.com/benbjohnson/immutable" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -13,7 +15,6 @@ import ( "github.com/synapsecns/sanguine/ethergo/client" "github.com/synapsecns/sanguine/ethergo/util" "golang.org/x/exp/constraints" - "math/big" ) // ScribeBackend is the set of functions that the scribe needs from a client. @@ -36,7 +37,7 @@ func DialBackend(ctx context.Context, url string, handler metrics.Handler) (Scri // GetLogsInRange gets all logs in a range with a single batch request // in successful cases an immutable list is returned, otherwise an error is returned. -func GetLogsInRange(ctx context.Context, backend ScribeBackend, contractAddress common.Address, expectedChainID uint64, chunks []*util.Chunk) (*immutable.List[*[]types.Log], error) { +func GetLogsInRange(ctx context.Context, backend ScribeBackend, contractAddresses []common.Address, expectedChainID uint64, chunks []*util.Chunk) (*immutable.List[*[]types.Log], error) { calls := make([]w3types.Caller, len(chunks)+2) results := make([][]types.Log, len(chunks)) chainID := new(uint64) @@ -44,12 +45,11 @@ func GetLogsInRange(ctx context.Context, backend ScribeBackend, contractAddress maxHeight := new(big.Int) calls[1] = eth.BlockNumber().Returns(maxHeight) - for i := 0; i < len(chunks); i++ { filter := ethereum.FilterQuery{ FromBlock: chunks[i].StartBlock, ToBlock: chunks[i].EndBlock, - Addresses: []common.Address{contractAddress}, + Addresses: contractAddresses, } calls[i+2] = eth.Logs(filter).Returns(&results[i]) } diff --git a/services/scribe/backend/backend_test.go b/services/scribe/backend/backend_test.go new file mode 100644 index 0000000000..0018b55619 --- /dev/null +++ b/services/scribe/backend/backend_test.go @@ -0,0 +1,200 @@ +package backend_test + +import ( + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/testutil" + + "math/big" + "sync" + "testing" + + . "github.com/stretchr/testify/assert" + "github.com/synapsecns/sanguine/ethergo/backends/geth" + "github.com/synapsecns/sanguine/ethergo/util" + "k8s.io/apimachinery/pkg/util/sets" +) + +func (b *BackendSuite) TestLogsInRange() { + const desiredBlockHeight = 10 + + var testChainHandler *testutil.TestChainHandler + var err error + var wg sync.WaitGroup + + wg.Add(2) + testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) + go func() { + defer wg.Done() + testChainHandler, err = testutil.PopulateWithLogs(b.GetTestContext(), b.T(), testBackend, desiredBlockHeight, []*testutil.DeployManager{b.manager}) + Nil(b.T(), err) + }() + + var host string + go func() { + defer wg.Done() + host = testutil.StartOmnirpcServer(b.GetTestContext(), b.T(), testBackend) + }() + + wg.Wait() + + scribeBackend, err := backend.DialBackend(b.GetTestContext(), host, b.metrics) + Nil(b.T(), err) + + chainID, err := scribeBackend.ChainID(b.GetTestContext()) + Nil(b.T(), err) + + lastBlock, err := testBackend.BlockNumber(b.GetTestContext()) + Nil(b.T(), err) + + iterator := util.NewChunkIterator(big.NewInt(int64(1)), big.NewInt(int64(lastBlock)), 1, true) + + var blockRanges []*util.Chunk + blockRange := iterator.NextChunk() + + for blockRange != nil { + blockRanges = append(blockRanges, blockRange) + blockRange = iterator.NextChunk() + } + + res, err := backend.GetLogsInRange(b.GetTestContext(), scribeBackend, testChainHandler.Addresses, chainID.Uint64(), blockRanges) + Nil(b.T(), err) + + // use to make sure we don't double use values + intSet := sets.NewInt64() + + itr := res.Iterator() + + numLogs := 0 + for !itr.Done() { + index, chunk := itr.Next() + + Falsef(b.T(), intSet.Has(int64(index)), "%d appears at least twice", index) + intSet.Insert(int64(index)) + NotNil(b.T(), chunk) + for range *chunk { + numLogs++ + } + } + Equal(b.T(), int(testChainHandler.EventsEmitted[testChainHandler.Addresses[0]]), numLogs) +} + +func (b *BackendSuite) TestLogsInRangeWithMultipleContracts() { + const desiredBlockHeight = 10 + + var testChainHandler *testutil.TestChainHandler + var err error + var wg sync.WaitGroup + + wg.Add(2) + testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) + + managerB := testutil.NewDeployManager(b.T()) + managerC := testutil.NewDeployManager(b.T()) + managers := []*testutil.DeployManager{b.manager, managerB, managerC} + + go func() { + defer wg.Done() + testChainHandler, err = testutil.PopulateWithLogs(b.GetTestContext(), b.T(), testBackend, desiredBlockHeight, managers) + Nil(b.T(), err) + }() + + var host string + go func() { + defer wg.Done() + host = testutil.StartOmnirpcServer(b.GetTestContext(), b.T(), testBackend) + }() + + wg.Wait() + + scribeBackend, err := backend.DialBackend(b.GetTestContext(), host, b.metrics) + Nil(b.T(), err) + + chainID, err := scribeBackend.ChainID(b.GetTestContext()) + Nil(b.T(), err) + iterator := util.NewChunkIterator(big.NewInt(int64(1)), big.NewInt(int64(desiredBlockHeight)), 1, true) + + var blockRanges []*util.Chunk + blockRange := iterator.NextChunk() + + for blockRange != nil { + blockRanges = append(blockRanges, blockRange) + blockRange = iterator.NextChunk() + } + res, err := backend.GetLogsInRange(b.GetTestContext(), scribeBackend, testChainHandler.Addresses, chainID.Uint64(), blockRanges) + Nil(b.T(), err) + + // use to make sure we don't double use values + intSet := sets.NewInt64() + + itr := res.Iterator() + + numLogs := 0 + logs := make(map[string]int) + for !itr.Done() { + index, chunk := itr.Next() + + Falsef(b.T(), intSet.Has(int64(index)), "%d appears at least twice", index) + intSet.Insert(int64(index)) + NotNil(b.T(), chunk) + for i := range *chunk { + logAddr := (*chunk)[i].Address.String() + logs[logAddr]++ + numLogs++ + } + } + Equal(b.T(), len(managers), numLogs) + + // Check if there's a log for each of the contracts + for i := range testChainHandler.Addresses { + Equal(b.T(), int(testChainHandler.EventsEmitted[testChainHandler.Addresses[i]]), logs[testChainHandler.Addresses[i].String()]) + } +} + +func TestMakeRange(t *testing.T) { + testIntRange := backend.MakeRange(0, 4) + Equal(t, []int{0, 1, 2, 3, 4}, testIntRange) + + testUintRange := backend.MakeRange(uint16(10), uint16(12)) + Equal(t, testUintRange, []uint16{10, 11, 12}) +} + +func (b *BackendSuite) TestBlockHashesInRange() { + testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) + + // start an omnirpc proxy and run 10 test tranactions so we can batch call blocks + // 1-10 + var wg sync.WaitGroup + wg.Add(2) + + const desiredBlockHeight = 10 + + go func() { + defer wg.Done() + err := testutil.ReachBlockHeight(b.GetTestContext(), b.T(), testBackend, desiredBlockHeight) + Nil(b.T(), err) + }() + + var host string + go func() { + defer wg.Done() + host = testutil.StartOmnirpcServer(b.GetTestContext(), b.T(), testBackend) + }() + + wg.Wait() + + scribeBackend, err := backend.DialBackend(b.GetTestContext(), host, b.metrics) + Nil(b.T(), err) + + res, err := backend.BlockHashesInRange(b.GetTestContext(), scribeBackend, 1, 10) + Nil(b.T(), err) + + // use to make sure we don't double use values + intSet := sets.NewInt64() + + itr := res.Iterator() + + for !itr.Done() { + index, _, _ := itr.Next() + Falsef(b.T(), intSet.Has(int64(index)), "%d appears at least twice", index) + } +} diff --git a/services/scribe/backend/doc.go b/services/scribe/backend/doc.go new file mode 100644 index 0000000000..edc44cb076 --- /dev/null +++ b/services/scribe/backend/doc.go @@ -0,0 +1,4 @@ +// Package backend handles the scribe backend. +package backend + +// TODO move backend to ethergo/backends diff --git a/services/scribe/backfill/suite_test.go b/services/scribe/backend/suite_test.go similarity index 79% rename from services/scribe/backfill/suite_test.go rename to services/scribe/backend/suite_test.go index 08a2e3087f..3eb8503c04 100644 --- a/services/scribe/backfill/suite_test.go +++ b/services/scribe/backend/suite_test.go @@ -1,4 +1,4 @@ -package backfill_test +package backend_test import ( "github.com/synapsecns/sanguine/core/metrics" @@ -18,7 +18,7 @@ import ( "github.com/synapsecns/sanguine/services/scribe/testutil" ) -type BackfillSuite struct { +type BackendSuite struct { *testsuite.TestSuite testDB db.EventDB manager *testutil.DeployManager @@ -27,16 +27,16 @@ type BackfillSuite struct { metrics metrics.Handler } -// NewBackfillSuite creates a new backfill test suite. -func NewBackfillSuite(tb testing.TB) *BackfillSuite { +// NewBackendSuite creates a new backfill test suite. +func NewBackendSuite(tb testing.TB) *BackendSuite { tb.Helper() - return &BackfillSuite{ + return &BackendSuite{ TestSuite: testsuite.NewTestSuite(tb), } } // SetupTest sets up the test suite. -func (b *BackfillSuite) SetupTest() { +func (b *BackendSuite) SetupTest() { b.TestSuite.SetupTest() b.SetTestTimeout(time.Minute * 3) sqliteStore, err := sqlite.NewSqliteStore(b.GetTestContext(), filet.TmpDir(b.T(), ""), b.metrics, false) @@ -48,7 +48,7 @@ func (b *BackfillSuite) SetupTest() { b.signer = localsigner.NewSigner(b.wallet.PrivateKey()) } -func (b *BackfillSuite) SetupSuite() { +func (b *BackendSuite) SetupSuite() { b.TestSuite.SetupSuite() localmetrics.SetupTestJaeger(b.GetSuiteContext(), b.T()) @@ -57,7 +57,7 @@ func (b *BackfillSuite) SetupSuite() { Nil(b.T(), err) } -// TestBackfillSuite tests the backfill suite. -func TestBackfillSuite(t *testing.T) { - suite.Run(t, NewBackfillSuite(t)) +// TestBackendSuite tests the backfill suite. +func TestBackendSuite(t *testing.T) { + suite.Run(t, NewBackendSuite(t)) } diff --git a/services/scribe/backfill/backend_test.go b/services/scribe/backfill/backend_test.go deleted file mode 100644 index 947769aeb8..0000000000 --- a/services/scribe/backfill/backend_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package backfill_test - -import ( - "context" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/params" - . "github.com/stretchr/testify/assert" - "github.com/synapsecns/sanguine/ethergo/backends" - "github.com/synapsecns/sanguine/ethergo/backends/geth" - "github.com/synapsecns/sanguine/ethergo/util" - "github.com/synapsecns/sanguine/services/omnirpc/testhelper" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "k8s.io/apimachinery/pkg/util/sets" - "math/big" - "sync" - "testing" -) - -// startOmnirpcServer boots an omnirpc server for an rpc address. -// the url for this rpc is returned. -func (b *BackfillSuite) startOmnirpcServer(ctx context.Context, backend backends.SimulatedTestBackend) string { - baseHost := testhelper.NewOmnirpcServer(ctx, b.T(), backend) - return testhelper.GetURL(baseHost, backend) -} - -// ReachBlockHeight reaches a block height on a backend. -func (b *BackfillSuite) ReachBlockHeight(ctx context.Context, backend backends.SimulatedTestBackend, desiredBlockHeight uint64) { - i := 0 - for { - select { - case <-ctx.Done(): - b.T().Log(ctx.Err()) - return - default: - // continue - } - i++ - backend.FundAccount(ctx, common.BigToAddress(big.NewInt(int64(i))), *big.NewInt(params.Wei)) - - latestBlock, err := backend.BlockNumber(ctx) - Nil(b.T(), err) - - if latestBlock >= desiredBlockHeight { - return - } - } -} - -// ReachBlockHeight reaches a block height on a backend. -func (b *BackfillSuite) PopuluateWithLogs(ctx context.Context, backend backends.SimulatedTestBackend, desiredBlockHeight uint64) common.Address { - i := 0 - var address common.Address - for { - select { - case <-ctx.Done(): - b.T().Log(ctx.Err()) - return address - default: - // continue - } - i++ - backend.FundAccount(ctx, common.BigToAddress(big.NewInt(int64(i))), *big.NewInt(params.Wei)) - testContract, testRef := b.manager.GetTestContract(b.GetTestContext(), backend) - address = testContract.Address() - transactOpts := backend.GetTxContext(b.GetTestContext(), nil) - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - backend.WaitForConfirmation(b.GetTestContext(), tx) - - latestBlock, err := backend.BlockNumber(ctx) - Nil(b.T(), err) - - if latestBlock >= desiredBlockHeight { - return address - } - } -} - -func (b *BackfillSuite) TestLogsInRange() { - testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) - // start an omnirpc proxy and run 10 test transactions so we can batch call blocks 1-10 - var wg sync.WaitGroup - wg.Add(2) - - const desiredBlockHeight = 10 - - var commonAddress common.Address - go func() { - defer wg.Done() - commonAddress = b.PopuluateWithLogs(b.GetTestContext(), testBackend, desiredBlockHeight) - }() - - var host string - go func() { - defer wg.Done() - host = b.startOmnirpcServer(b.GetTestContext(), testBackend) - }() - - wg.Wait() - - scribeBackend, err := backfill.DialBackend(b.GetTestContext(), host, b.metrics) - Nil(b.T(), err) - - chainID, err := scribeBackend.ChainID(b.GetTestContext()) - Nil(b.T(), err) - iterator := util.NewChunkIterator(big.NewInt(int64(1)), big.NewInt(int64(desiredBlockHeight)), 1, true) - - var blockRanges []*util.Chunk - blockRange := iterator.NextChunk() - - for blockRange != nil { - blockRanges = append(blockRanges, blockRange) - blockRange = iterator.NextChunk() - } - res, err := backfill.GetLogsInRange(b.GetTestContext(), scribeBackend, commonAddress, chainID.Uint64(), blockRanges) - Nil(b.T(), err) - - // use to make sure we don't double use values - intSet := sets.NewInt64() - - itr := res.Iterator() - - numLogs := 0 - for !itr.Done() { - numLogs++ - index, _ := itr.Next() - - Falsef(b.T(), intSet.Has(int64(index)), "%d appears at least twice", index) - intSet.Insert(int64(index)) - numLogs++ - } -} - -func TestMakeRange(t *testing.T) { - testIntRange := backfill.MakeRange(0, 4) - Equal(t, []int{0, 1, 2, 3, 4}, testIntRange) - - testUintRange := backfill.MakeRange(uint16(10), uint16(12)) - Equal(t, testUintRange, []uint16{10, 11, 12}) -} - -func (b *BackfillSuite) TestBlockHashesInRange() { - testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) - - // start an omnirpc proxy and run 10 test tranactions so we can batch call blocks - // 1-10 - var wg sync.WaitGroup - wg.Add(2) - - const desiredBlockHeight = 10 - - go func() { - defer wg.Done() - b.ReachBlockHeight(b.GetTestContext(), testBackend, desiredBlockHeight) - }() - - var host string - go func() { - defer wg.Done() - host = b.startOmnirpcServer(b.GetTestContext(), testBackend) - }() - - wg.Wait() - - scribeBackend, err := backfill.DialBackend(b.GetTestContext(), host, b.metrics) - Nil(b.T(), err) - - res, err := backfill.BlockHashesInRange(b.GetTestContext(), scribeBackend, 1, 10) - Nil(b.T(), err) - - // use to make sure we don't double use values - intSet := sets.NewInt64() - - itr := res.Iterator() - - for !itr.Done() { - index, _, _ := itr.Next() - - Falsef(b.T(), intSet.Has(int64(index)), "%d appears at least twice", index) - } -} diff --git a/services/scribe/backfill/chain.go b/services/scribe/backfill/chain.go deleted file mode 100644 index e168441c5e..0000000000 --- a/services/scribe/backfill/chain.go +++ /dev/null @@ -1,215 +0,0 @@ -package backfill - -import ( - "context" - "fmt" - "math" - "time" - - "github.com/synapsecns/sanguine/core/metrics" - - "github.com/jpillora/backoff" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "golang.org/x/sync/errgroup" -) - -// ChainBackfiller is a backfiller that fetches logs for a chain. It aggregates logs -// from a slice of ContractBackfillers. -type ChainBackfiller struct { - // chainID is the chain ID of the chain. - chainID uint32 - // eventDB is the database to store event data in. - eventDB db.EventDB - // client contains the clients used for backfilling. - client []ScribeBackend - // contractBackfillers is the list of contract backfillers. - contractBackfillers []*ContractBackfiller - // startHeights is a map from address -> start height. - startHeights map[string]uint64 - // minBlockHeight is the minimum block height to store block time for. - minBlockHeight uint64 - // chainConfig is the config for the backfiller. - chainConfig config.ChainConfig - // refreshRate is the rate at which the backfiller will refresh when livefilling. - refreshRate int - // handler is the metrics handler for the scribe. - handler metrics.Handler -} - -// Used for handling logging of various context types. -type contextKey int - -const ( - chainContextKey contextKey = iota -) - -// NewChainBackfiller creates a new backfiller for a chain. This is done by passing through all the function parameters -// into the ChainBackfiller struct, as well as iterating through all the contracts in the chain config & creating -// ContractBackfillers for each contract. -func NewChainBackfiller(eventDB db.EventDB, client []ScribeBackend, chainConfig config.ChainConfig, refreshRate int, handler metrics.Handler) (*ChainBackfiller, error) { - var contractBackfillers []*ContractBackfiller - - startHeights := make(map[string]uint64) - - if chainConfig.GetLogsRange == 0 { - chainConfig.GetLogsRange = 600 - } - - if chainConfig.GetLogsBatchAmount == 0 { - chainConfig.GetLogsBatchAmount = 2 - } - - if chainConfig.StoreConcurrency == 0 { - chainConfig.StoreConcurrency = 20 - } - - if refreshRate == 0 { - refreshRate = 1 - } - - if chainConfig.ConcurrencyThreshold == 0 { - chainConfig.ConcurrencyThreshold = 50000 - } - minBlockHeight := uint64(math.MaxUint64) - - for _, contract := range chainConfig.Contracts { - blockHeightMeter, err := handler.Metrics().NewHistogram(fmt.Sprintf("scribe_block_meter_%d_%s", chainConfig.ChainID, contract.Address), "block_histogram", "a block height meter", "blocks") - if err != nil { - return nil, fmt.Errorf("error creating otel histogram %w", err) - } - contractBackfiller, err := NewContractBackfiller(chainConfig, contract, eventDB, client, handler, blockHeightMeter) - if err != nil { - return nil, fmt.Errorf("could not create contract backfiller: %w", err) - } - contractBackfillers = append(contractBackfillers, contractBackfiller) - startHeights[contract.Address] = contract.StartBlock - - if minBlockHeight > contract.StartBlock { - minBlockHeight = contract.StartBlock - } - } - - return &ChainBackfiller{ - chainID: chainConfig.ChainID, - eventDB: eventDB, - client: client, - contractBackfillers: contractBackfillers, - startHeights: startHeights, - minBlockHeight: minBlockHeight, - chainConfig: chainConfig, - refreshRate: refreshRate, - handler: handler, - }, nil -} - -// Backfill iterates over each contract backfiller and calls Backfill concurrently on each one. -// If `onlyOneBlock` is true, the backfiller will only backfill the block at `currentBlock`. -// -//nolint:gocognit,cyclop -func (c ChainBackfiller) Backfill(ctx context.Context, onlyOneBlock *uint64, livefill bool) error { - // Create a new context for the chain so all chains don't halt when backfilling is completed. - chainCtx := context.WithValue(ctx, chainContextKey, fmt.Sprintf("%d-%d", c.chainID, c.minBlockHeight)) - backfillGroup, backfillCtx := errgroup.WithContext(chainCtx) - - b := &backoff.Backoff{ - Factor: 2, - Jitter: true, - Min: 1 * time.Second, - Max: 3 * time.Second, - } - - timeout := time.Duration(0) - startTime := time.Now() - - for i := range c.contractBackfillers { - contractBackfiller := c.contractBackfillers[i] - startHeight := c.startHeights[contractBackfiller.contractConfig.Address] - - LogEvent(InfoLevel, "Starting livefilling contracts", LogData{"cid": c.chainID}) - backfillGroup.Go(func() error { - timeout = time.Duration(0) - for { - select { - case <-backfillCtx.Done(): - LogEvent(ErrorLevel, "Couldn't livefill contract, context canceled", LogData{"cid": c.chainID, "ca": contractBackfiller.contractConfig.Address, "sh": startHeight, "bd": b.Duration(), "a": b.Attempt(), "e": backfillCtx.Err()}) - - return fmt.Errorf("%s chain context canceled: %w", backfillCtx.Value(chainContextKey), backfillCtx.Err()) - case <-time.After(timeout): - var latestBlock *uint64 - var err error - - // onlyOneBlock is used for amending single blocks with a blockhash discrepancies or for testing. - if onlyOneBlock != nil { - startHeight = *onlyOneBlock - latestBlock = onlyOneBlock - } else { - latestBlock, err = c.getLatestBlock(backfillCtx) - if err != nil { - return fmt.Errorf("could not get current block number while backfilling: %w", err) - } - } - - err = contractBackfiller.Backfill(backfillCtx, startHeight, *latestBlock) - if err != nil { - timeout = b.Duration() - - // If the contract has been given a specific refresh rate, then use that refresh rate for error handling. - if contractBackfiller.contractConfig.RefreshRate > 1 { - timeout = time.Duration(contractBackfiller.contractConfig.RefreshRate) * time.Second - } - LogEvent(WarnLevel, "Could not backfill contract, retrying", LogData{"cid": c.chainID, "ca": contractBackfiller.contractConfig.Address, "sh": startHeight, "bd": b.Duration(), "a": b.Attempt(), "e": err.Error()}) - - continue - } - - if !livefill { - return nil - } - - timeout = time.Duration(contractBackfiller.contractConfig.RefreshRate) * time.Second - LogEvent(InfoLevel, "Continuing to livefill contract", LogData{"t": timeout, "cid": c.chainID, "ca": contractBackfiller.contractConfig.Address, "sh": startHeight, "bd": b.Duration(), "a": b.Attempt()}) - } - } - }) - } - - if err := backfillGroup.Wait(); err != nil { - LogEvent(ErrorLevel, "Could not backfill with error group", LogData{"cid": c.chainID, "bd": b.Duration(), "a": b.Attempt(), "e": err.Error(), "bt": true}) - - return fmt.Errorf("could not backfill: %w", err) - } - LogEvent(WarnLevel, "Finished backfilling blocktimes and contracts", LogData{"cid": c.chainID, "t": time.Since(startTime).Hours()}) - - return nil -} - -func (c *ChainBackfiller) getLatestBlock(ctx context.Context) (*uint64, error) { - var currentBlock uint64 - var err error - b := &backoff.Backoff{ - Factor: 2, - Jitter: true, - Min: 1 * time.Second, - Max: 10 * time.Second, - } - - timeout := time.Duration(0) - for { - select { - case <-ctx.Done(): - - return nil, fmt.Errorf("%s context canceled: %w", ctx.Value(chainContextKey), ctx.Err()) - case <-time.After(timeout): - currentBlock, err = c.client[0].BlockNumber(ctx) - - if err != nil { - timeout = b.Duration() - LogEvent(InfoLevel, "Could not get block number, bad connection to rpc likely", LogData{"cid": c.chainID, "e": err.Error()}) - continue - } - } - - return ¤tBlock, nil - } -} diff --git a/services/scribe/backfill/chain_test.go b/services/scribe/backfill/chain_test.go deleted file mode 100644 index 310b811cbc..0000000000 --- a/services/scribe/backfill/chain_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package backfill_test - -import ( - "github.com/synapsecns/sanguine/ethergo/backends" - "github.com/synapsecns/sanguine/ethergo/backends/geth" - "math/big" - - "github.com/brianvoe/gofakeit/v6" - "github.com/ethereum/go-ethereum/params" - . "github.com/stretchr/testify/assert" - "github.com/synapsecns/sanguine/ethergo/contracts" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "github.com/synapsecns/sanguine/services/scribe/testutil" - "github.com/synapsecns/sanguine/services/scribe/testutil/testcontract" -) - -// TestChainBackfill tests that the ChainBackfiller can backfill events from a chain. -func (b BackfillSuite) TestChainBackfill() { - // We need to set up multiple deploy managers, one for each contract. We will use - // b.manager for the first contract, and create a new ones for the next two. - managerB := testutil.NewDeployManager(b.T()) - managerC := testutil.NewDeployManager(b.T()) - - // Get simulated blockchain, deploy three test contracts, and set up test variables. - chainID := gofakeit.Uint32() - - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetTestContext(), b.T(), big.NewInt(int64(chainID))) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - - simulatedChain.FundAccount(b.GetTestContext(), b.wallet.Address(), *big.NewInt(params.Ether)) - testContractA, testRefA := b.manager.GetTestContract(b.GetTestContext(), simulatedChain) - testContractB, testRefB := managerB.GetTestContract(b.GetTestContext(), simulatedChain) - testContractC, testRefC := managerC.GetTestContract(b.GetTestContext(), simulatedChain) - - contracts := []contracts.DeployedContract{testContractA, testContractB, testContractC} - testRefs := []*testcontract.TestContractRef{testRefA, testRefB, testRefC} - startBlocks := make([]uint64, len(contracts)) - - for i, contract := range contracts { - deployTxHash := contract.DeployTx().Hash() - receipt, err := simulatedChain.TransactionReceipt(b.GetTestContext(), deployTxHash) - Nil(b.T(), err) - startBlocks[i] = receipt.BlockNumber.Uint64() - } - - contractConfigs := config.ContractConfigs{} - - for i, contract := range contracts { - contractConfigs = append(contractConfigs, config.ContractConfig{ - Address: contract.Address().String(), - StartBlock: startBlocks[i], - }) - } - - chainConfig := config.ChainConfig{ - ChainID: chainID, - Contracts: contractConfigs, - } - simulatedChainArr := []backfill.ScribeBackend{simulatedClient, simulatedClient} - chainBackfiller, err := backfill.NewChainBackfiller(b.testDB, simulatedChainArr, chainConfig, 1, b.metrics) - Nil(b.T(), err) - b.EmitEventsForAChain(contracts, testRefs, simulatedChain, chainBackfiller, chainConfig, true) -} - -// EmitEventsForAChain emits events for a chain. If `backfill` is set to true, the function will store the events -// whilst checking their existence in the database. -func (b BackfillSuite) EmitEventsForAChain(contracts []contracts.DeployedContract, testRefs []*testcontract.TestContractRef, simulatedChain backends.SimulatedTestBackend, chainBackfiller *backfill.ChainBackfiller, chainConfig config.ChainConfig, backfill bool) { - transactOpts := simulatedChain.GetTxContext(b.GetTestContext(), nil) - - // Emit events from each contract. - for _, testRef := range testRefs { - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - } - - if backfill { - err := chainBackfiller.Backfill(b.GetTestContext(), nil, false) - Nil(b.T(), err) - - for _, contract := range contracts { - logFilter := db.LogFilter{ - ChainID: chainConfig.ChainID, - ContractAddress: contract.Address().String(), - } - logs, err := b.testDB.RetrieveLogsWithFilter(b.GetTestContext(), logFilter, 1) - Nil(b.T(), err) - - // There should be 4 logs. One from `EmitEventA`, one from `EmitEventB`, and two from `EmitEventAandB`. - Equal(b.T(), 4, len(logs)) - } - - receiptFilter := db.ReceiptFilter{ - ChainID: chainConfig.ChainID, - } - receipts, err := b.testDB.RetrieveReceiptsWithFilter(b.GetTestContext(), receiptFilter, 1) - Nil(b.T(), err) - - // There should be 9 receipts from `EmitEventA`,`EmitEventB`, and `EmitEventAandB` for each contract. - Equal(b.T(), 9, len(receipts)) - totalBlockTimes := uint64(0) - currBlock, err := simulatedChain.BlockNumber(b.GetTestContext()) - Nil(b.T(), err) - firstBlock, err := b.testDB.RetrieveFirstBlockStored(b.GetTestContext(), chainBackfiller.ChainID()) - Nil(b.T(), err) - - for blockNum := firstBlock; blockNum <= currBlock; blockNum++ { - _, err := b.testDB.RetrieveBlockTime(b.GetTestContext(), chainBackfiller.ChainID(), blockNum) - if err == nil { - totalBlockTimes++ - } - } - - // There are `currBlock` - `firstBlock`+1 block times stored (checking after contract gets deployed). - Equal(b.T(), currBlock-firstBlock+uint64(1), totalBlockTimes) - } -} diff --git a/services/scribe/backfill/contract.go b/services/scribe/backfill/contract.go deleted file mode 100644 index 452d66218d..0000000000 --- a/services/scribe/backfill/contract.go +++ /dev/null @@ -1,473 +0,0 @@ -package backfill - -import ( - "context" - "errors" - "fmt" - "math/big" - "time" - - "github.com/lmittmann/w3" - "github.com/lmittmann/w3/module/eth" - "github.com/lmittmann/w3/w3types" - "github.com/synapsecns/sanguine/core/mapmutex" - "github.com/synapsecns/sanguine/core/metrics" - "go.opentelemetry.io/otel/attribute" - otelMetrics "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/trace" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - lru "github.com/hashicorp/golang-lru" - "github.com/jpillora/backoff" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "golang.org/x/sync/errgroup" -) - -// ContractBackfiller is a backfiller that fetches logs for a specific contract. -type ContractBackfiller struct { - // chainConfig is the chain config for the chain that the contract is on. - contractConfig config.ContractConfig - // address is the contract address to get logs for. - chainConfig config.ChainConfig - // eventDB is the database to store event data in. - eventDB db.EventDB - // client is the client for filtering. - client []ScribeBackend - // cache is a cache for txHashes. - cache *lru.Cache - // mux is the mutex used to prevent double inserting logs from the same tx - mux mapmutex.StringerMapMutex - // handler is the metrics handler for the scribe. - handler metrics.Handler - // blockMeter is an otel historgram for doing metrics on block heights by chain - blockMeter otelMetrics.Int64Histogram -} - -// retryTolerance is the number of times to retry a failed operation before rerunning the entire Backfill function. -const retryTolerance = 20 - -// txNotSupportedError is for handling the legacy Arbitrum tx type. -const txNotSupportedError = "transaction type not supported" - -// invalidTxVRSError is for handling Aurora VRS error. -const invalidTxVRSError = "invalid transaction v, r, s values" - -// txNotFoundError is for handling omniRPC errors for BSC. -const txNotFoundError = "not found" - -// txData returns the transaction data for a given transaction hash. -type txData struct { - receipt types.Receipt - transaction types.Transaction - blockHeader types.Header - success bool -} - -var errNoContinue = errors.New("encountered unreconcilable error, will not attempt to store tx") - -// errNoTx indicates a tx cannot be parsed, this is only returned when the tx doesn't match our data model. -var errNoTx = errors.New("tx is not supported by the client") - -// NewContractBackfiller creates a new backfiller for a contract. -func NewContractBackfiller(chainConfig config.ChainConfig, contractConfig config.ContractConfig, eventDB db.EventDB, client []ScribeBackend, handler metrics.Handler, blockMeter otelMetrics.Int64Histogram) (*ContractBackfiller, error) { - cache, err := lru.New(500) - if err != nil { - return nil, fmt.Errorf("could not initialize cache: %w", err) - } - - // Default refresh rate is instant - if contractConfig.RefreshRate == 0 { - contractConfig.RefreshRate = 1 - } - return &ContractBackfiller{ - chainConfig: chainConfig, - contractConfig: contractConfig, - eventDB: eventDB, - client: client, - cache: cache, - mux: mapmutex.NewStringerMapMutex(), - handler: handler, - blockMeter: blockMeter, - }, nil -} - -// Backfill retrieves logs, receipts, and transactions for a contract from a given range and does so in the following manner. -// 1. Get logs for the contract in chunks of batch requests. -// 2. Iterate through each log's Tx Hash and performs the following -// - Get the receipt for each log and store it and all of its logs. -// - Get the transaction for each log and store it. -// -//nolint:gocognit, cyclop -func (c *ContractBackfiller) Backfill(parentCtx context.Context, givenStart uint64, endHeight uint64) (err error) { - ctx, span := c.handler.Tracer().Start(parentCtx, "contract.Backfill", trace.WithAttributes( - attribute.Int("chain", int(c.chainConfig.ChainID)), - attribute.String("address", c.contractConfig.Address), - attribute.Int("start", int(givenStart)), - attribute.Int("end", int(endHeight)), - )) - - defer func() { - metrics.EndSpanWithErr(span, err) - }() - - g, groupCtx := errgroup.WithContext(ctx) - startHeight := givenStart - lastBlockIndexed, err := c.eventDB.RetrieveLastIndexed(groupCtx, common.HexToAddress(c.contractConfig.Address), c.chainConfig.ChainID) - if err != nil { - LogEvent(WarnLevel, "Could not get last indexed", LogData{"cid": c.chainConfig.ChainID, "sh": startHeight, "eh": endHeight, "e": err.Error()}) - - return fmt.Errorf("could not get last indexed: %w", err) - } - if lastBlockIndexed > startHeight { - startHeight = lastBlockIndexed + 1 - } - - // logsChain and errChan are used to pass logs from rangeFilter onto the next stage of the backfill process. - logsChan, errChan := c.getLogs(groupCtx, startHeight, endHeight) - - // Reads from the local logsChan and stores the logs and associated receipts / txs. - g.Go(func() error { - concurrentCalls := 0 - gS, storeCtx := errgroup.WithContext(ctx) - // could change this to for - range - for { - select { - case <-groupCtx.Done(): - LogEvent(ErrorLevel, "Context canceled while storing and retrieving logs", LogData{"cid": c.chainConfig.ChainID, "ca": c.contractConfig.Address}) - - return fmt.Errorf("context canceled while storing and retrieving logs: %w", groupCtx.Err()) - case log, ok := <-logsChan: - if !ok { - return nil - } - concurrentCalls++ - gS.Go(func() error { - // another goroutine is already storing this receipt - locker, ok := c.mux.TryLock(log.TxHash) - if !ok { - return nil - } - defer locker.Unlock() - - // Check if the txHash has already been stored in the cache. - if _, ok := c.cache.Get(log.TxHash); ok { - return nil - } - - err := c.store(storeCtx, log) - if err != nil { - LogEvent(ErrorLevel, "Could not store log", LogData{"cid": c.chainConfig.ChainID, "ca": c.contractConfig.Address, "e": err.Error()}) - - return fmt.Errorf("could not store log: %w", err) - } - - return nil - }) - - // Stop spawning store threads and wait - if concurrentCalls >= c.chainConfig.StoreConcurrency || endHeight-log.BlockNumber < c.chainConfig.ConcurrencyThreshold { - if err = gS.Wait(); err != nil { - return fmt.Errorf("error waiting for go routines: %w", err) - } - - // Reset context TODO make this better - gS, storeCtx = errgroup.WithContext(ctx) - concurrentCalls = 0 - err = c.eventDB.StoreLastIndexed(ctx, common.HexToAddress(c.contractConfig.Address), c.chainConfig.ChainID, log.BlockNumber) - if err != nil { - LogEvent(ErrorLevel, "Could not store last indexed block", LogData{"cid": c.chainConfig.ChainID, "bn": log.BlockNumber, "tx": log.TxHash.Hex(), "la": log.Address.String(), "ca": c.contractConfig.Address, "e": err.Error()}) - - return fmt.Errorf("could not store last indexed block: %w", err) - } - - c.blockMeter.Record(ctx, int64(log.BlockNumber), otelMetrics.WithAttributeSet( - attribute.NewSet(attribute.Int64("start_block", int64(startHeight)), attribute.Int64("chain_id", int64(c.chainConfig.ChainID)))), - ) - } - - case errFromChan := <-errChan: - LogEvent(ErrorLevel, "Received errChan", LogData{"cid": c.chainConfig.ChainID, "ca": c.contractConfig.Address, "err": errFromChan}) - - return fmt.Errorf("errChan returned an err %s", errFromChan) - } - } - }) - - err = g.Wait() - - if err != nil { - return fmt.Errorf("could not backfill contract: %w \nChain: %d\nLog 's Contract Address: %s\nContract Address: %s", err, c.chainConfig.ChainID, c.contractConfig.Address, c.contractConfig.Address) - } - - err = c.eventDB.StoreLastIndexed(ctx, common.HexToAddress(c.contractConfig.Address), c.chainConfig.ChainID, endHeight) - if err != nil { - return fmt.Errorf("could not store last indexed block: %w", err) - } - c.blockMeter.Record(ctx, int64(endHeight), otelMetrics.WithAttributeSet( - attribute.NewSet(attribute.Int64("start_block", int64(startHeight)), attribute.Int64("chain_id", int64(c.chainConfig.ChainID)))), - ) - LogEvent(InfoLevel, "Finished backfilling contract", LogData{"cid": c.chainConfig.ChainID, "ca": c.contractConfig.Address}) - - return nil -} - -// TODO split two goroutines into sep functions for maintainability -// store stores the logs, receipts, and transactions for a tx hash. -// -//nolint:cyclop,gocognit,maintidx -func (c *ContractBackfiller) store(parentCtx context.Context, log types.Log) (err error) { - ctx, span := c.handler.Tracer().Start(parentCtx, "store", trace.WithAttributes( - attribute.String("contract", c.contractConfig.Address), - attribute.String("tx", log.TxHash.Hex()), - attribute.String("block", fmt.Sprintf("%d", log.BlockNumber)), - )) - - defer func() { - metrics.EndSpanWithErr(span, err) - }() - - startTime := time.Now() - - b := &backoff.Backoff{ - Factor: 2, - Jitter: true, - Min: 3 * time.Millisecond, - Max: 2 * time.Second, - } - - timeout := time.Duration(0) - tryCount := 0 - - var tx *txData - hasTX := true - -OUTER: - for { - select { - case <-ctx.Done(): - LogEvent(ErrorLevel, "Context canceled while storing logs/receipts", LogData{"cid": c.chainConfig.ChainID, "bn": log.BlockNumber, "tx": log.TxHash.Hex(), "la": log.Address.String(), "ca": c.contractConfig.Address, "e": ctx.Err()}) - - return fmt.Errorf("context canceled while storing logs/receipts: %w", ctx.Err()) - case <-time.After(timeout): - tryCount++ - - tx, err = c.fetchEventData(ctx, log.TxHash, log.BlockNumber) - if err != nil { - if errors.Is(err, errNoContinue) { - return nil - } - - if errors.Is(err, errNoTx) { - hasTX = false - break OUTER - } - - if tryCount > retryTolerance { - return fmt.Errorf("retry tolerance exceeded: %w", err) - } - - timeout = b.Duration() - continue - } - - break OUTER - } - } - - g, groupCtx := errgroup.WithContext(ctx) - - g.Go(func() error { - // Store receipt in the EventDB. - err = c.eventDB.StoreReceipt(groupCtx, c.chainConfig.ChainID, tx.receipt) - if err != nil { - LogEvent(ErrorLevel, "Could not store receipt, retrying", LogData{"cid": c.chainConfig.ChainID, "bn": log.BlockNumber, "tx": log.TxHash.Hex(), "la": log.Address.String(), "ca": c.contractConfig.Address, "e": err.Error()}) - - return fmt.Errorf("could not store receipt: %w", err) - } - return nil - }) - - if hasTX { - g.Go(func() error { - err = c.eventDB.StoreEthTx(groupCtx, &tx.transaction, c.chainConfig.ChainID, log.BlockHash, log.BlockNumber, uint64(log.TxIndex)) - if err != nil { - return fmt.Errorf("could not store tx: %w", err) - } - return nil - }) - } - - g.Go(func() error { - logs, err := c.prunedReceiptLogs(tx.receipt) - if err != nil { - return err - } - - err = c.eventDB.StoreLogs(groupCtx, c.chainConfig.ChainID, logs...) - if err != nil { - return fmt.Errorf("could not store receipt logs: %w", err) - } - - return nil - }) - - g.Go(func() error { - err := c.eventDB.StoreBlockTime(groupCtx, c.chainConfig.ChainID, tx.blockHeader.Number.Uint64(), tx.blockHeader.Time) - if err != nil { - return fmt.Errorf("could not store receipt logs: %w", err) - } - return nil - }) - - err = g.Wait() - if err != nil { - LogEvent(ErrorLevel, "Could not store data", LogData{"cid": c.chainConfig.ChainID, "bn": log.BlockNumber, "tx": log.TxHash.Hex(), "la": log.Address.String(), "ca": c.contractConfig.Address, "e": err.Error()}) - - return fmt.Errorf("could not store data: %w\n%s on chain %d from %d to %s", err, c.contractConfig.Address, c.chainConfig.ChainID, log.BlockNumber, log.TxHash.String()) - } - - c.cache.Add(log.TxHash, true) - LogEvent(InfoLevel, "Log, Receipt, and Tx stored", LogData{"cid": c.chainConfig.ChainID, "bn": log.BlockNumber, "tx": log.TxHash.Hex(), "la": log.Address.String(), "ca": c.contractConfig.Address, "ts": time.Since(startTime).Seconds()}) - - return nil -} -func (c *ContractBackfiller) getLogs(parentCtx context.Context, startHeight, endHeight uint64) (<-chan types.Log, <-chan string) { - ctx, span := c.handler.Tracer().Start(parentCtx, "getLogs") - defer metrics.EndSpan(span) - - logFetcher := NewLogFetcher(common.HexToAddress(c.contractConfig.Address), c.client[0], big.NewInt(int64(startHeight)), big.NewInt(int64(endHeight)), &c.chainConfig) - logsChan, errChan := make(chan types.Log), make(chan string) - - go c.runFetcher(ctx, logFetcher, errChan) - go c.processLogs(ctx, logFetcher, logsChan, errChan) - - return logsChan, errChan -} - -func (c *ContractBackfiller) runFetcher(ctx context.Context, logFetcher *LogFetcher, errChan chan<- string) { - if err := logFetcher.Start(ctx); err != nil { - select { - case <-ctx.Done(): - errChan <- fmt.Sprintf("context canceled while appending log to channel %v", ctx.Err()) - return - case errChan <- err.Error(): - return - } - } -} - -func (c *ContractBackfiller) processLogs(ctx context.Context, logFetcher *LogFetcher, logsChan chan<- types.Log, errChan chan<- string) { - for { - select { - case <-ctx.Done(): - errChan <- fmt.Sprintf("context canceled %v", ctx.Err()) - return - case logChunks, ok := <-logFetcher.fetchedLogsChan: - if !ok { - close(logsChan) - return - } - for _, log := range logChunks { - select { - case <-ctx.Done(): - errChan <- fmt.Sprintf("context canceled while loading log chunks to log %v", ctx.Err()) - return - case logsChan <- log: - } - } - } - } -} - -// prunedReceiptLogs gets all logs from a receipt and prunes null logs. -func (c *ContractBackfiller) prunedReceiptLogs(receipt types.Receipt) (logs []types.Log, err error) { - for i := range receipt.Logs { - log := receipt.Logs[i] - if log == nil { - LogEvent(ErrorLevel, "log is nil", LogData{"cid": c.chainConfig.ChainID, "bn": log.BlockNumber, "tx": log.TxHash.Hex(), "la": log.Address.String(), "ca": c.contractConfig.Address}) - - return nil, fmt.Errorf("log is nil\nChain: %d\nTxHash: %s\nLog BlockNumber: %d\nLog 's Contract Address: %s\nContract Address: %s", c.chainConfig.ChainID, log.TxHash.String(), log.BlockNumber, log.Address.String(), c.contractConfig.Address) - } - logs = append(logs, *log) - } - return logs, nil -} - -// fetchEventData tries to fetch a transaction from the cache, if it's not there it tries to fetch it from the database. -// nolint: cyclop -func (c *ContractBackfiller) fetchEventData(parentCtx context.Context, txhash common.Hash, blockNumber uint64) (tx *txData, err error) { - ctx, span := c.handler.Tracer().Start(parentCtx, "fetchEventData", trace.WithAttributes( - attribute.String("tx", txhash.Hex()), - attribute.String("block", fmt.Sprintf("%d", blockNumber)), - )) - - defer func() { - metrics.EndSpanWithErr(span, err) - }() - -OUTER: - // increasing this across more clients puts too much load on the server, results in failed requests. TODO investigate - for i := range c.client[0:1] { - tx = &txData{} - - calls := make([]w3types.Caller, 3) - - // setup referencable indexes so we can access errors from the calls - const ( - receiptIndex = 0 - txIndex = 1 - headerIndex = 2 - ) - - // get the transaction receipt - calls[receiptIndex] = eth.TxReceipt(txhash).Returns(&tx.receipt) - - // get the raw transaction - calls[txIndex] = eth.Tx(txhash).Returns(&tx.transaction) - - // get the block number - calls[headerIndex] = eth.HeaderByNumber(new(big.Int).SetUint64(blockNumber)).Returns(&tx.blockHeader) - - //nolint: nestif - if err := c.client[i].BatchWithContext(ctx, calls...); err != nil { - //nolint: errorlint - callErr, ok := err.(w3.CallErrors) - if !ok { - return nil, fmt.Errorf("could not parse errors: %w", err) - } - - if callErr[receiptIndex] != nil { - if callErr[receiptIndex].Error() == txNotFoundError { - LogEvent(InfoLevel, "Could not get tx for txHash, attempting with additional confirmations", LogData{"cid": c.chainConfig.ChainID, "tx": txhash, "ca": c.contractConfig.Address, "e": err.Error()}) - continue OUTER - } - } - - if callErr[txIndex] != nil { - switch callErr[txIndex].Error() { - case txNotSupportedError: - LogEvent(InfoLevel, "Invalid tx", LogData{"cid": c.chainConfig.ChainID, "tx": txhash, "ca": c.contractConfig.Address, "e": err.Error()}) - return tx, errNoTx - case invalidTxVRSError: - LogEvent(InfoLevel, "Could not get tx for txHash, attempting with additional confirmations", LogData{"cid": c.chainConfig.ChainID, "tx": txhash, "ca": c.contractConfig.Address, "e": err.Error()}) - return tx, errNoTx - case txNotFoundError: - LogEvent(InfoLevel, "Could not get tx for txHash, attempting with additional confirmations", LogData{"cid": c.chainConfig.ChainID, "tx": txhash, "ca": c.contractConfig.Address, "e": err.Error()}) - continue OUTER - } - } - - return nil, fmt.Errorf("could not get tx receipt: %w", err) - } - - tx.success = true - } - - if tx == nil || !tx.success { - return nil, fmt.Errorf("could not get tx data: %w", err) - } - - return tx, nil -} diff --git a/services/scribe/backfill/contract_test.go b/services/scribe/backfill/contract_test.go deleted file mode 100644 index 257a11a793..0000000000 --- a/services/scribe/backfill/contract_test.go +++ /dev/null @@ -1,529 +0,0 @@ -package backfill_test - -import ( - "context" - "fmt" - "github.com/brianvoe/gofakeit/v6" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" - . "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/synapsecns/sanguine/ethergo/backends" - "github.com/synapsecns/sanguine/ethergo/backends/geth" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "github.com/synapsecns/sanguine/services/scribe/db/mocks" - "os" - "sync" - - "math/big" -) - -// TestFailedStore tests that the ChainBackfiller continues backfilling after a failed store. - -func (b BackfillSuite) TestFailedStore() { - mockDB := new(mocks.EventDB) - mockDB. - // on a store receipt call - On("StoreReceipt", mock.Anything, mock.Anything, mock.Anything). - Return(fmt.Errorf("failed to store receipt")) - mockDB. - // on a store transaction call - On("StoreEthTx", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(fmt.Errorf("failed to store transaction")) - mockDB. - // on a store log call - On("StoreLogs", mock.Anything, mock.Anything, mock.Anything). - Return(fmt.Errorf("failed to store log")) - mockDB. - // on retrieve last indexed call - On("RetrieveLastIndexed", mock.Anything, mock.Anything, mock.Anything). - Return(uint64(0), nil) - - mockDB.On("StoreBlockTime", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - - chainID := gofakeit.Uint32() - - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetTestContext(), b.T(), big.NewInt(int64(chainID))) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - - simulatedChain.FundAccount(b.GetTestContext(), b.wallet.Address(), *big.NewInt(params.Ether)) - testContract, testRef := b.manager.GetTestContract(b.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(b.GetTestContext(), nil) - contractConfig := config.ContractConfig{ - Address: testContract.Address().String(), - StartBlock: 0, - } - simulatedChainArr := []backfill.ScribeBackend{simulatedClient, simulatedClient} - chainConfig := config.ChainConfig{ - ChainID: chainID, - GetLogsBatchAmount: 1, - StoreConcurrency: 1, - GetLogsRange: 1, - } - blockHeightMeter, err := b.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") - Nil(b.T(), err) - - backfiller, err := backfill.NewContractBackfiller(chainConfig, contractConfig, mockDB, simulatedChainArr, b.metrics, blockHeightMeter) - Nil(b.T(), err) - - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Get the block that the last transaction was executed in. - txBlockNumber, err := b.getTxBlockNumber(simulatedChain, tx) - Nil(b.T(), err) - err = backfiller.Backfill(b.GetTestContext(), contractConfig.StartBlock, txBlockNumber) - NotNil(b.T(), err) - - // Check to ensure that StoreLastIndexed was never called. - mockDB.AssertNotCalled(b.T(), "StoreLastIndexed", mock.Anything, mock.Anything, mock.Anything, mock.Anything) -} - -// TestGetLogsSimulated tests the GetLogs function using a simulated blockchain. -// -//nolint:cyclop -func (b BackfillSuite) TestGetLogsSimulated() { - // Get simulated blockchain, deploy the test contract, and set up test variables. - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetSuiteContext(), b.T(), big.NewInt(3)) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - - simulatedChain.FundAccount(b.GetTestContext(), b.wallet.Address(), *big.NewInt(params.Ether)) - testContract, testRef := b.manager.GetTestContract(b.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(b.GetTestContext(), nil) - contractConfig := config.ContractConfig{ - Address: testContract.Address().String(), - StartBlock: 0, - } - simulatedChainArr := []backfill.ScribeBackend{simulatedClient, simulatedClient} - chainConfig := config.ChainConfig{ - ChainID: 3, - GetLogsBatchAmount: 1, - StoreConcurrency: 1, - GetLogsRange: 1, - } - blockHeightMeter, err := b.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") - Nil(b.T(), err) - - backfiller, err := backfill.NewContractBackfiller(chainConfig, contractConfig, b.testDB, simulatedChainArr, b.metrics, blockHeightMeter) - Nil(b.T(), err) - - // Emit five events, and then fetch them with GetLogs. The first two will be fetched first, - // then the last three after. - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Get the block that the second transaction was executed in. - txBlockNumberA, err := b.getTxBlockNumber(simulatedChain, tx) - Nil(b.T(), err) - - tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{10}, big.NewInt(11), big.NewInt(12)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(13), big.NewInt(14), big.NewInt(15)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Get the block that the last transaction was executed in. - txBlockNumberB, err := b.getTxBlockNumber(simulatedChain, tx) - Nil(b.T(), err) - - // Get the logs for the first two events. - collectedLogs := []types.Log{} - logs, errChan := backfiller.GetLogs(b.GetTestContext(), contractConfig.StartBlock, txBlockNumberA) - - for { - select { - case <-b.GetTestContext().Done(): - b.T().Error("test timed out") - case log, ok := <-logs: - if !ok { - goto Done - } - collectedLogs = append(collectedLogs, log) - case errorFromChan := <-errChan: - Nil(b.T(), errorFromChan) - } - } -Done: - // Check to see if 2 logs were collected. - Equal(b.T(), 2, len(collectedLogs)) - - // Get the logs for the last three events. - collectedLogs = []types.Log{} - logs, errChan = backfiller.GetLogs(b.GetTestContext(), txBlockNumberA+1, txBlockNumberB) - - for { - select { - case <-b.GetTestContext().Done(): - b.T().Error("test timed out") - case log, ok := <-logs: - if !ok { - goto Done2 - } - collectedLogs = append(collectedLogs, log) - case errorFromChan := <-errChan: - Nil(b.T(), errorFromChan) - } - } -Done2: - - // Check to see if 3 logs were collected. - Equal(b.T(), 3, len(collectedLogs)) -} - -// TestContractBackfill tests using a contractBackfiller for recording receipts and logs in a database. - -func (b BackfillSuite) TestContractBackfill() { - // Get simulated blockchain, deploy the test contract, and set up test variables. - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetSuiteContext(), b.T(), big.NewInt(142)) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - - simulatedChain.FundAccount(b.GetTestContext(), b.wallet.Address(), *big.NewInt(params.Ether)) - testContract, testRef := b.manager.GetTestContract(b.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(b.GetTestContext(), nil) - - // Set config. - contractConfig := config.ContractConfig{ - Address: testContract.Address().String(), - StartBlock: 0, - } - - simulatedChainArr := []backfill.ScribeBackend{simulatedClient, simulatedClient} - chainConfig := config.ChainConfig{ - ChainID: 142, - GetLogsBatchAmount: 1, - StoreConcurrency: 1, - GetLogsRange: 1, - } - blockHeightMeter, err := b.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") - Nil(b.T(), err) - backfiller, err := backfill.NewContractBackfiller(chainConfig, contractConfig, b.testDB, simulatedChainArr, b.metrics, blockHeightMeter) - b.Require().NoError(err) - - // Emit events for the backfiller to read. - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Emit two logs in one receipt. - tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Get the block that the last transaction was executed in. - txBlockNumber, err := b.getTxBlockNumber(simulatedChain, tx) - Nil(b.T(), err) - - // Backfill the events. The `0` will be replaced with the startBlock from the config. - err = backfiller.Backfill(b.GetTestContext(), contractConfig.StartBlock, txBlockNumber) - Nil(b.T(), err) - - // Get all receipts. - receipts, err := b.testDB.RetrieveReceiptsWithFilter(b.GetTestContext(), db.ReceiptFilter{}, 1) - Nil(b.T(), err) - - // Check to see if 3 receipts were collected. - Equal(b.T(), 4, len(receipts)) - - // Get all logs. - logs, err := b.testDB.RetrieveLogsWithFilter(b.GetTestContext(), db.LogFilter{}, 1) - Nil(b.T(), err) - - // Check to see if 4 logs were collected. - Equal(b.T(), 5, len(logs)) - - // Check to see if the last receipt has two logs. - Equal(b.T(), 2, len(receipts[0].Logs)) - - // Ensure last indexed block is correct. - lastIndexed, err := b.testDB.RetrieveLastIndexed(b.GetTestContext(), testContract.Address(), uint32(testContract.ChainID().Uint64())) - Nil(b.T(), err) - Equal(b.T(), txBlockNumber, lastIndexed) -} - -// TestTxTypeNotSupported tests how the contract backfiller handles a transaction type that is not supported. -// -// nolint:dupl -func (b BackfillSuite) TestTxTypeNotSupported() { - if os.Getenv("CI") != "" { - b.T().Skip("Network test flake") - } - - var backendClient backfill.ScribeBackend - omnirpcURL := "https://rpc.interoperability.institute/confirmations/1/rpc/42161" - backendClient, err := backfill.DialBackend(b.GetTestContext(), omnirpcURL, b.metrics) - Nil(b.T(), err) - - // This config is using this block https://arbiscan.io/block/6262099 - // and this tx https://arbiscan.io/tx/0x8800222adf9578fb576db0bd7fb4860fe89932549be084a3313939c03e4d279d - // with a unique Arbitrum type to verify that anomalous tx type is handled correctly. - contractConfig := config.ContractConfig{ - Address: "0xA67b7147DcE20D6F25Fd9ABfBCB1c3cA74E11f0B", - StartBlock: 6262099, - } - - chainConfig := config.ChainConfig{ - ChainID: 42161, - Contracts: []config.ContractConfig{contractConfig}, - } - backendClientArr := []backfill.ScribeBackend{backendClient, backendClient} - chainBackfiller, err := backfill.NewChainBackfiller(b.testDB, backendClientArr, chainConfig, 1, b.metrics) - Nil(b.T(), err) - err = chainBackfiller.Backfill(b.GetTestContext(), &contractConfig.StartBlock, false) - Nil(b.T(), err) - - logs, err := b.testDB.RetrieveLogsWithFilter(b.GetTestContext(), db.LogFilter{}, 1) - Nil(b.T(), err) - Equal(b.T(), 4, len(logs)) - receipts, err := b.testDB.RetrieveReceiptsWithFilter(b.GetTestContext(), db.ReceiptFilter{}, 1) - Nil(b.T(), err) - Equal(b.T(), 1, len(receipts)) -} - -// TestTxTypeNotSupported tests how the contract backfiller handles a transaction type that is not supported. -// -// nolint:dupl -func (b BackfillSuite) TestInvalidTxVRS() { - if os.Getenv("CI") != "" { - b.T().Skip("Network test flake") - } - - var backendClient backfill.ScribeBackend - omnirpcURL := "https://rpc.interoperability.institute/confirmations/1/rpc/1313161554" - backendClient, err := backfill.DialBackend(b.GetTestContext(), omnirpcURL, b.metrics) - Nil(b.T(), err) - - // This config is using this block https://aurorascan.dev/block/58621373 - // and this tx https://aurorascan.dev/tx/0x687282d7bd6c3d591f9ad79784e0983afabcac2a9074d368b7ca3d7caf4edee5 - // to test handling of the v,r,s tx not found error. - contractConfig := config.ContractConfig{ - Address: "0xaeD5b25BE1c3163c907a471082640450F928DDFE", - StartBlock: 58621373, - } - - chainConfig := config.ChainConfig{ - ChainID: 1313161554, - Contracts: []config.ContractConfig{contractConfig}, - } - backendClientArr := []backfill.ScribeBackend{backendClient, backendClient} - chainBackfiller, err := backfill.NewChainBackfiller(b.testDB, backendClientArr, chainConfig, 1, b.metrics) - Nil(b.T(), err) - - err = chainBackfiller.Backfill(b.GetTestContext(), &contractConfig.StartBlock, false) - Nil(b.T(), err) - - logs, err := b.testDB.RetrieveLogsWithFilter(b.GetTestContext(), db.LogFilter{}, 1) - Nil(b.T(), err) - Equal(b.T(), 9, len(logs)) - receipts, err := b.testDB.RetrieveReceiptsWithFilter(b.GetTestContext(), db.ReceiptFilter{}, 1) - Nil(b.T(), err) - Equal(b.T(), 1, len(receipts)) -} -func (b BackfillSuite) getTxBlockNumber(chain backends.SimulatedTestBackend, tx *types.Transaction) (uint64, error) { - receipt, err := chain.TransactionReceipt(b.GetTestContext(), tx.Hash()) - if err != nil { - return 0, fmt.Errorf("error getting receipt for tx: %w", err) - } - return receipt.BlockNumber.Uint64(), nil -} - -// TestContractBackfill tests using a contractBackfiller for recording receipts and logs in a database. -func (b BackfillSuite) TestContractBackfillFromPreIndexed() { - // Get simulated blockchain, deploy the test contract, and set up test variables. - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetSuiteContext(), b.T(), big.NewInt(142)) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - - simulatedChain.FundAccount(b.GetTestContext(), b.wallet.Address(), *big.NewInt(params.Ether)) - testContract, testRef := b.manager.GetTestContract(b.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(b.GetTestContext(), nil) - - // Set config. - contractConfig := config.ContractConfig{ - Address: testContract.Address().String(), - StartBlock: 0, - } - - simulatedChainArr := []backfill.ScribeBackend{simulatedClient, simulatedClient} - chainConfig := config.ChainConfig{ - ChainID: 142, - GetLogsBatchAmount: 1, - StoreConcurrency: 1, - GetLogsRange: 1, - } - blockHeightMeter, err := b.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") - Nil(b.T(), err) - backfiller, err := backfill.NewContractBackfiller(chainConfig, contractConfig, b.testDB, simulatedChainArr, b.metrics, blockHeightMeter) - Nil(b.T(), err) - - // Emit events for the backfiller to read. - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Emit two logs in one receipt. - tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Get the block that the last transaction was executed in. - txBlockNumber, err := b.getTxBlockNumber(simulatedChain, tx) - Nil(b.T(), err) - - err = b.testDB.StoreLastIndexed(b.GetTestContext(), common.HexToAddress(contractConfig.Address), chainConfig.ChainID, txBlockNumber) - Nil(b.T(), err) - - tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(10), big.NewInt(11), big.NewInt(12)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{13}, big.NewInt(14), big.NewInt(15)) - Nil(b.T(), err) - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Emit two logs in one receipt. - tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(16), big.NewInt(17), big.NewInt(18)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Emit two logs in one receipt. - tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(19), big.NewInt(20), big.NewInt(21)) - Nil(b.T(), err) - - simulatedChain.WaitForConfirmation(b.GetTestContext(), tx) - - // Get the block that the last transaction was executed in. - txBlockNumber, err = b.getTxBlockNumber(simulatedChain, tx) - Nil(b.T(), err) - - err = backfiller.Backfill(b.GetTestContext(), contractConfig.StartBlock, txBlockNumber) - Nil(b.T(), err) - - // Get all receipts. - receipts, err := b.testDB.RetrieveReceiptsWithFilter(b.GetTestContext(), db.ReceiptFilter{}, 1) - Nil(b.T(), err) - - // Check to see if 3 receipts were collected. - Equal(b.T(), 4, len(receipts)) - - // Get all logs. - logs, err := b.testDB.RetrieveLogsWithFilter(b.GetTestContext(), db.LogFilter{}, 1) - Nil(b.T(), err) - - // Check to see if 4 logs were collected. - Equal(b.T(), 6, len(logs)) - - // Check to see if the last receipt has two logs. - Equal(b.T(), 2, len(receipts[0].Logs)) - - // Ensure last indexed block is correct. - lastIndexed, err := b.testDB.RetrieveLastIndexed(b.GetTestContext(), testContract.Address(), uint32(testContract.ChainID().Uint64())) - Nil(b.T(), err) - Equal(b.T(), txBlockNumber, lastIndexed) -} - -func (b BackfillSuite) TestGetLogs() { - testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) - var wg sync.WaitGroup - wg.Add(2) - - const desiredBlockHeight = 10 - var contractAddress common.Address - go func() { - defer wg.Done() - contractAddress = b.PopuluateWithLogs(b.GetTestContext(), testBackend, desiredBlockHeight) - }() - - var host string - go func() { - defer wg.Done() - host = b.startOmnirpcServer(b.GetTestContext(), testBackend) - }() - - wg.Wait() - - scribeBackend, err := backfill.DialBackend(b.GetTestContext(), host, b.metrics) - Nil(b.T(), err) - simulatedChainArr := []backfill.ScribeBackend{scribeBackend, scribeBackend} - - chainID, err := scribeBackend.ChainID(b.GetTestContext()) - Nil(b.T(), err) - - contractConfig := &config.ContractConfig{ - Address: contractAddress.Hex(), - } - chainConfig := config.ChainConfig{ - ChainID: uint32(chainID.Uint64()), - GetLogsBatchAmount: 1, - StoreConcurrency: 1, - GetLogsRange: 1, - } - blockHeightMeter, err := b.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") - Nil(b.T(), err) - contractBackfiller, err := backfill.NewContractBackfiller(chainConfig, *contractConfig, b.testDB, simulatedChainArr, b.metrics, blockHeightMeter) - Nil(b.T(), err) - - startHeight, endHeight := uint64(1), uint64(10) - logsChan, errChan := contractBackfiller.GetLogs(b.GetTestContext(), startHeight, endHeight) - - var logs []types.Log - var errs []string -loop: - for { - select { - case log, ok := <-logsChan: - if !ok { - break loop - } - logs = append(logs, log) - case err, ok := <-errChan: - if !ok { - break loop - } - errs = append(errs, err) - } - } - - Equal(b.T(), 2, len(logs)) - Equal(b.T(), 0, len(errs)) - - cancelCtx, cancel := context.WithCancel(b.GetTestContext()) - cancel() - - _, errChan = contractBackfiller.GetLogs(cancelCtx, startHeight, endHeight) -loop2: - for { - errStr := <-errChan - Contains(b.T(), errStr, "context canceled") - break loop2 - } -} diff --git a/services/scribe/backfill/doc.go b/services/scribe/backfill/doc.go deleted file mode 100644 index a31f65eaaa..0000000000 --- a/services/scribe/backfill/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package backfill is used to get logs from previous blocks -package backfill diff --git a/services/scribe/backfill/err.go b/services/scribe/backfill/err.go deleted file mode 100644 index fc372d0edb..0000000000 --- a/services/scribe/backfill/err.go +++ /dev/null @@ -1 +0,0 @@ -package backfill diff --git a/services/scribe/backfill/export_test.go b/services/scribe/backfill/export_test.go deleted file mode 100644 index cb5c73a960..0000000000 --- a/services/scribe/backfill/export_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package backfill - -import ( - "context" - "github.com/ethereum/go-ethereum/core/types" -) - -// GetLogs exports logs for testing. -func (c ContractBackfiller) GetLogs(ctx context.Context, startHeight, endHeight uint64) (<-chan types.Log, <-chan string) { - return c.getLogs(ctx, startHeight, endHeight) -} - -// Clients exports clients for testing. -func (s *ScribeBackfiller) Clients() map[uint32][]ScribeBackend { - return s.clients -} - -// ChainID exports chainID for testing. -func (c ChainBackfiller) ChainID() uint32 { - return c.chainID -} diff --git a/services/scribe/backfill/fetcher_test.go b/services/scribe/backfill/fetcher_test.go deleted file mode 100644 index 374b4a197e..0000000000 --- a/services/scribe/backfill/fetcher_test.go +++ /dev/null @@ -1,185 +0,0 @@ -package backfill_test - -import ( - "context" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - . "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/synapsecns/sanguine/ethergo/backends/geth" - "github.com/synapsecns/sanguine/ethergo/chain/client/mocks" - etherMocks "github.com/synapsecns/sanguine/ethergo/mocks" - "github.com/synapsecns/sanguine/ethergo/util" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "github.com/synapsecns/sanguine/services/scribe/config" - "math/big" - "sync" -) - -// TestFilterLogsMaxAttempts ensures after the maximum number of attempts, an error is returned. -func (b BackfillSuite) TestFilterLogsMaxAttempts() { - b.T().Skip("flake") - chainID := big.NewInt(int64(1)) - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetTestContext(), b.T(), chainID) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - mockFilterer := new(mocks.EVMClient) - contractAddress := etherMocks.MockAddress() - config := &config.ChainConfig{ - ChainID: 1, - GetLogsBatchAmount: 1, - GetLogsRange: 1, - } - - rangeFilter := backfill.NewLogFetcher(contractAddress, simulatedClient, big.NewInt(1), big.NewInt(10), config) - - // Use the range filterer created above to create a mock log filter. - mockFilterer. - On("FilterLogs", mock.Anything, mock.Anything). - Return(nil, errors.New("I'm a test error")) - chunks := []*util.Chunk{{ - StartBlock: big.NewInt(1), - EndBlock: big.NewInt(10), - }} - logInfo, err := rangeFilter.FetchLogs(b.GetTestContext(), chunks) - Nil(b.T(), logInfo) - NotNil(b.T(), err) -} - -// TestGetChunkArr ensures that the batching orchestration function (collecting block range chunks into arrays) works properly. -func (b BackfillSuite) TestGetChunkArr() { - chainID := big.NewInt(int64(1)) - simulatedChain := geth.NewEmbeddedBackendForChainID(b.GetTestContext(), b.T(), chainID) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedChain.RPCAddress(), b.metrics) - Nil(b.T(), err) - contractAddress := etherMocks.MockAddress() - config := &config.ChainConfig{ - ChainID: 1, - ConcurrencyThreshold: 1, - GetLogsBatchAmount: 1, - GetLogsRange: 1, - } - - startBlock := int64(1) - endBlock := int64(10) - - rangeFilter := backfill.NewLogFetcher(contractAddress, simulatedClient, big.NewInt(startBlock), big.NewInt(endBlock), config) - - numberOfRequests := int64(0) - for i := int64(0); i < endBlock; i++ { - chunks := rangeFilter.GetChunkArr() - if len(chunks) == 0 { - break - } - Equal(b.T(), len(chunks), int(config.GetLogsBatchAmount)) - numberOfRequests++ - } - Equal(b.T(), numberOfRequests, endBlock) - - // Test with a larger batch size - config.GetLogsBatchAmount = 4 - rangeFilter = backfill.NewLogFetcher(contractAddress, simulatedClient, big.NewInt(1), big.NewInt(10), config) - numberOfRequests = int64(0) - loopCount := endBlock/int64(config.GetLogsBatchAmount) + 1 - for i := int64(0); i < loopCount; i++ { - chunks := rangeFilter.GetChunkArr() - if len(chunks) == 0 { - break - } - if i < loopCount-1 { - Equal(b.T(), len(chunks), int(config.GetLogsBatchAmount)) - } else { - Equal(b.T(), len(chunks), int(endBlock%int64(config.GetLogsBatchAmount))) - } - numberOfRequests++ - } - Equal(b.T(), numberOfRequests, loopCount) - - // Test with a larger range size - config.GetLogsRange = 2 - rangeFilter = backfill.NewLogFetcher(contractAddress, simulatedClient, big.NewInt(1), big.NewInt(10), config) - numberOfRequests = int64(0) - loopCount = endBlock/int64(config.GetLogsBatchAmount*config.GetLogsRange) + 1 - for i := int64(0); i < loopCount; i++ { - chunks := rangeFilter.GetChunkArr() - if len(chunks) == 0 { - break - } - if i < loopCount-1 { - Equal(b.T(), len(chunks), int(config.GetLogsBatchAmount)) - } else { - Equal(b.T(), len(chunks), 1) - } - numberOfRequests++ - } - Equal(b.T(), numberOfRequests, loopCount) -} - -// TestGetChunkArr ensures that the batching orchestration function (collecting block range chunks into arrays) works properly. -func (b BackfillSuite) TestFetchLogs() { - testBackend := geth.NewEmbeddedBackend(b.GetTestContext(), b.T()) - // start an omnirpc proxy and run 10 test transactions so we can batch call blocks 1-10 - var wg sync.WaitGroup - wg.Add(2) - - const desiredBlockHeight = 10 - - var contractAddress common.Address - go func() { - defer wg.Done() - contractAddress = b.PopuluateWithLogs(b.GetTestContext(), testBackend, desiredBlockHeight) - }() - - var host string - go func() { - defer wg.Done() - host = b.startOmnirpcServer(b.GetTestContext(), testBackend) - }() - - wg.Wait() - - scribeBackend, err := backfill.DialBackend(b.GetTestContext(), host, b.metrics) - Nil(b.T(), err) - - chunks := []*util.Chunk{ - { - StartBlock: big.NewInt(1), - EndBlock: big.NewInt(2), - }, - { - StartBlock: big.NewInt(3), - EndBlock: big.NewInt(4), - }, - { - StartBlock: big.NewInt(5), - EndBlock: big.NewInt(6), - }, - { - StartBlock: big.NewInt(7), - EndBlock: big.NewInt(8), - }, - { - StartBlock: big.NewInt(9), - EndBlock: big.NewInt(10), - }, - } - chainID, err := scribeBackend.ChainID(b.GetTestContext()) - Nil(b.T(), err) - config := &config.ChainConfig{ - ChainID: uint32(chainID.Uint64()), - ConcurrencyThreshold: 1, - GetLogsBatchAmount: 1, - GetLogsRange: 2, - } - rangeFilter := backfill.NewLogFetcher(contractAddress, scribeBackend, big.NewInt(1), big.NewInt(desiredBlockHeight), config) - logs, err := rangeFilter.FetchLogs(b.GetTestContext(), chunks) - Nil(b.T(), err) - Equal(b.T(), 2, len(logs)) - - cancelCtx, cancel := context.WithCancel(b.GetTestContext()) - cancel() - - _, err = rangeFilter.FetchLogs(cancelCtx, chunks) - NotNil(b.T(), err) - Contains(b.T(), err.Error(), "context was canceled") -} diff --git a/services/scribe/backfill/logger.go b/services/scribe/backfill/logger.go deleted file mode 100644 index b77089c2e4..0000000000 --- a/services/scribe/backfill/logger.go +++ /dev/null @@ -1,67 +0,0 @@ -package backfill - -import ( - "fmt" - "github.com/ipfs/go-log" -) - -// LogData holds all the data passed to LogEvent to be logged. -type LogData map[string]interface{} - -type logLevel int - -const ( - // InfoLevel prints a log at the info level. - InfoLevel logLevel = iota - // WarnLevel prints a log at the warn level. - WarnLevel - // ErrorLevel prints a log at the error level. - ErrorLevel -) - -var logger = log.Logger("scribe-backfiller") -var keyToTitle = map[string]string{ - "cid": "ChainID", - "bn": "Block Number", - "tx": "TX Hash", - "la": "Log Address", - "ca": "Contract Address", - "sh": "Start Height", - "eh": "End Height", - "lc": "Logs Chan", - "bt": "BlockTime Log", - "bd": "Backoff Duration", - "lb": "Last Block Stored", - "a": "Backoff Attempt", - "t": "Time Elapsed", - "ts": "Time Elapsed (Seconds)", - "cn": "Client Number", - "e": "Error"} - -// LogEvent formats and logs an event. -func LogEvent(level logLevel, msg string, logData LogData) { - switch level { - case InfoLevel: - logger.Infof("Message: %s%s", msg, generateLog(logData)) - case WarnLevel: - logger.Warnf("Message: %s%s", msg, generateLog(logData)) - case ErrorLevel: - logger.Errorf("Message: %s%s", msg, generateLog(logData)) - default: - logger.Infof("Message: %s%s", msg, generateLog(logData)) - } -} - -func generateLog(logData LogData) string { - var logString string - - for k, v := range logData { - title, ok := keyToTitle[k] - if !ok { - title = k - } - logString += "\n" + title + ": " + fmt.Sprintf("%v", v) - } - - return logString -} diff --git a/services/scribe/backfill/scribe.go b/services/scribe/backfill/scribe.go deleted file mode 100644 index 7251a05985..0000000000 --- a/services/scribe/backfill/scribe.go +++ /dev/null @@ -1,70 +0,0 @@ -package backfill - -import ( - "context" - "fmt" - "github.com/synapsecns/sanguine/core/metrics" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "golang.org/x/sync/errgroup" -) - -// ScribeBackfiller is a backfiller that aggregates all backfilling from ChainBackfillers. -type ScribeBackfiller struct { - // eventDB is the database to store event data in. - eventDB db.EventDB - // clients is a mapping of chain IDs -> clients. - clients map[uint32][]ScribeBackend - // ChainBackfillers is a mapping of chain IDs -> chain backfillers. - ChainBackfillers map[uint32]*ChainBackfiller - // config is the config for the backfiller. - config config.Config - // handler is the metrics handler for the scribe. - handler metrics.Handler -} - -// NewScribeBackfiller creates a new backfiller for the scribe. -func NewScribeBackfiller(eventDB db.EventDB, clientsMap map[uint32][]ScribeBackend, config config.Config, handler metrics.Handler) (*ScribeBackfiller, error) { - chainBackfillers := map[uint32]*ChainBackfiller{} - - for _, chainConfig := range config.Chains { - chainBackfiller, err := NewChainBackfiller(eventDB, clientsMap[chainConfig.ChainID], chainConfig, 1, handler) - if err != nil { - return nil, fmt.Errorf("could not create chain backfiller: %w", err) - } - - chainBackfillers[chainConfig.ChainID] = chainBackfiller - } - - return &ScribeBackfiller{ - eventDB: eventDB, - clients: clientsMap, - ChainBackfillers: chainBackfillers, - config: config, - handler: handler, - }, nil -} - -// Backfill iterates over each chain backfiller and calls Backfill concurrently on each one. -func (s ScribeBackfiller) Backfill(ctx context.Context) error { - g, groupCtx := errgroup.WithContext(ctx) - - for i := range s.ChainBackfillers { - chainBackfiller := s.ChainBackfillers[i] - g.Go(func() error { - LogEvent(InfoLevel, "Scribe backfilling chain", LogData{"cid": chainBackfiller.chainID}) - err := chainBackfiller.Backfill(groupCtx, nil, false) - if err != nil { - return fmt.Errorf("could not backfill chain: %w", err) - } - - return nil - }) - } - - if err := g.Wait(); err != nil { - return fmt.Errorf("could not backfill: %w", err) - } - - return nil -} diff --git a/services/scribe/backfill/scribe_test.go b/services/scribe/backfill/scribe_test.go deleted file mode 100644 index 83f5567f32..0000000000 --- a/services/scribe/backfill/scribe_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package backfill_test - -import ( - "github.com/synapsecns/sanguine/ethergo/backends/geth" - "math/big" - "sync" - - "github.com/brianvoe/gofakeit/v6" - . "github.com/stretchr/testify/assert" - "github.com/synapsecns/sanguine/ethergo/contracts" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "github.com/synapsecns/sanguine/services/scribe/testutil" - "github.com/synapsecns/sanguine/services/scribe/testutil/testcontract" -) - -// TestScribeBackfill tests backfilling data from all chains. -// -//nolint:cyclop -func (b BackfillSuite) TestScribeBackfill() { - // Set up 3 chains, and the simulated backends for each. - chainA := gofakeit.Uint32() - chainB := chainA + 1 - chainC := chainB + 1 - chains := []uint32{chainA, chainB, chainC} - - simulatedBackends := make([]*geth.Backend, len(chains)) - simulatedClients := make([]backfill.ScribeBackend, len(chains)) - - var wg sync.WaitGroup - var mux sync.Mutex - - for i, chain := range chains { - // capture func literals - chain := chain - i := i - - wg.Add(1) - - go func() { - defer wg.Done() - simulatedBackend := geth.NewEmbeddedBackendForChainID(b.GetTestContext(), b.T(), big.NewInt(int64(chain))) - simulatedClient, err := backfill.DialBackend(b.GetTestContext(), simulatedBackend.RPCAddress(), b.metrics) - Nil(b.T(), err) - - mux.Lock() - defer mux.Unlock() - simulatedBackends[i] = simulatedBackend - simulatedClients[i] = simulatedClient - }() - } - wg.Wait() - - type deployedContracts []contracts.DeployedContract - type contractRefs []*testcontract.TestContractRef - type startBlocks []uint64 - var allDeployedContracts []deployedContracts - var allContractRefs []contractRefs - var allStartBlocks []startBlocks - // Deploy test contracts to each chain. - for _, backend := range simulatedBackends { - // We need to set up multiple deploy managers, one for each contract. We will use - // b.manager for the first contract, and create a new ones for the next two. - managerB := testutil.NewDeployManager(b.T()) - managerC := testutil.NewDeployManager(b.T()) - // Set the contracts and contract refs for each chain. - testContractA, testRefA := b.manager.GetTestContract(b.GetTestContext(), backend) - testContractB, testRefB := managerB.GetTestContract(b.GetTestContext(), backend) - testContractC, testRefC := managerC.GetTestContract(b.GetTestContext(), backend) - testContracts := []contracts.DeployedContract{testContractA, testContractB, testContractC} - testRefs := []*testcontract.TestContractRef{testRefA, testRefB, testRefC} - // Set the start blocks for each chain. - var startBlocks startBlocks - for _, contract := range testContracts { - deployTxHash := contract.DeployTx().Hash() - receipt, err := backend.TransactionReceipt(b.GetTestContext(), deployTxHash) - Nil(b.T(), err) - startBlocks = append(startBlocks, receipt.BlockNumber.Uint64()) - } - allStartBlocks = append(allStartBlocks, startBlocks) - - // Add the contracts and contract refs to the list of all contracts and contract refs. - allDeployedContracts = append(allDeployedContracts, testContracts) - allContractRefs = append(allContractRefs, testRefs) - } - - // Set up the config for the scribe. - allContractConfigs := []config.ContractConfigs{} - for i, deployedContracts := range allDeployedContracts { - var contractConfig config.ContractConfigs - for j, deployedContract := range deployedContracts { - contractConfig = append(contractConfig, config.ContractConfig{ - Address: deployedContract.Address().String(), - StartBlock: allStartBlocks[i][j], - }) - } - allContractConfigs = append(allContractConfigs, contractConfig) - } - allChainConfigs := []config.ChainConfig{} - for i, chain := range chains { - chainConfig := config.ChainConfig{ - ChainID: chain, - Contracts: allContractConfigs[i], - } - allChainConfigs = append(allChainConfigs, chainConfig) - } - scribeConfig := config.Config{ - Chains: allChainConfigs, - } - - // Set up all chain backfillers. - chainBackfillers := []*backfill.ChainBackfiller{} - for i, chainConfig := range allChainConfigs { - simulatedChainArr := []backfill.ScribeBackend{simulatedClients[i], simulatedClients[i]} - chainBackfiller, err := backfill.NewChainBackfiller(b.testDB, simulatedChainArr, chainConfig, 1, b.metrics) - Nil(b.T(), err) - chainBackfillers = append(chainBackfillers, chainBackfiller) - } - - scribeBackends := make(map[uint32][]backfill.ScribeBackend) - for i := range simulatedBackends { - client := simulatedClients[i] - backend := simulatedBackends[i] - - simulatedChainArr := []backfill.ScribeBackend{client, client} - scribeBackends[uint32(backend.GetChainID())] = simulatedChainArr - } - - // Set up the scribe backfiller. - scribeBackfiller, err := backfill.NewScribeBackfiller(b.testDB, scribeBackends, scribeConfig, b.metrics) - Nil(b.T(), err) - - // Run the backfill test for each chain. - for i, chainBackfiller := range chainBackfillers { - b.EmitEventsForAChain(allDeployedContracts[i], allContractRefs[i], simulatedBackends[i], chainBackfiller, allChainConfigs[i], false) - } - - // Run the scribe's backfill. - err = scribeBackfiller.Backfill(b.GetTestContext()) - Nil(b.T(), err) - - // Check that the data was added to the database. - logs, err := b.testDB.RetrieveLogsWithFilter(b.GetTestContext(), db.LogFilter{}, 1) - Nil(b.T(), err) - // There are 4 logs per contract, and 3 contracts per chain. Since there are 3 chains, 4*3*3 = 36 logs. - Equal(b.T(), 36, len(logs)) - receipts, err := b.testDB.RetrieveReceiptsWithFilter(b.GetTestContext(), db.ReceiptFilter{}, 1) - Nil(b.T(), err) - // There are 9 receipts per chain. Since there are 3 chains, 9*3 = 27 receipts. - Equal(b.T(), 27, len(receipts)) - - for _, chainBackfiller := range chainBackfillers { - totalBlockTimes := uint64(0) - currBlock, err := scribeBackfiller.Clients()[chainBackfiller.ChainID()][0].BlockNumber(b.GetTestContext()) - Nil(b.T(), err) - firstBlock, err := b.testDB.RetrieveFirstBlockStored(b.GetTestContext(), chainBackfiller.ChainID()) - Nil(b.T(), err) - for blockNum := firstBlock; blockNum <= currBlock; blockNum++ { - _, err := b.testDB.RetrieveBlockTime(b.GetTestContext(), chainBackfiller.ChainID(), blockNum) - if err == nil { - totalBlockTimes++ - } - } - // There are `currBlock` - `firstBlock`+1 block times stored. events don't get emitted until the contract gets deployed. - Equal(b.T(), currBlock-firstBlock+uint64(1), totalBlockTimes) - } -} diff --git a/services/scribe/cmd/cmd.md b/services/scribe/cmd/cmd.md index d3e04bfaa8..890138bd43 100644 --- a/services/scribe/cmd/cmd.md +++ b/services/scribe/cmd/cmd.md @@ -1,74 +1,166 @@ # Scribe - [![Go Reference](https://pkg.go.dev/badge/github.com/synapsecns/sanguine/services/scribe.svg)](https://pkg.go.dev/github.com/synapsecns/sanguine/services/scribe) [![Go Report Card](https://goreportcard.com/badge/github.com/synapsecns/sanguine/services/scribe)](https://goreportcard.com/report/github.com/synapsecns/sanguine/services/scribe) +Scribe is a multi-chain indexing service. Scribe is designed to take a list of contracts specified by chain id and store logs, receipts, and txs for every event, past to present, in a mysql database. + +Use cases +- Analytics for on chain events +- Stream events occurring across chains +- Monitor activity on your contracts + + +Scribe is currently indexing over 900 contracts across 18 different chains. + + + +## Core Features +### Scribe Server +Scribe comes with a graphql/iql endpoint with various queries to make interacting with the collected data easier. Here are some basic queries +- `lastIndexed(chain_id, contract_address)` +- `logsRange(chain_id, contract_address, start_block, end_block, page)` +- `blockTime(chain_id, block_number)` +- `txSender(tx_hash, chain_id)` + + +A full list can be found at graphql/server/graph/schema/queries.graphql + + +### Scribe Indexer +Scribe indexer supports indexing on any number of contracts on any chain. For each contract Scribe indexes from the +specified start of the contract from the config. Scribe stores every log, receipt, tx, and timestamp for every event until +it reaches the specified livefill threshold where it will then continue to index events as they occur in real time. +For reorg protection, Scribe does not store any events that are more than the specified number of confirmations from the chain tip +into its primary tables. Scribe stores "unconfirmed" events near the tip of the chain in separate and transient tables. +The Scribe server has built in queries to query both the primary (confirmed) and transient (unconfirmed) tables at the same time if +realtime data needed for querying or streaming. -Scribe is a service that goes through all chains and contracts specified in a config file and creates a database with logs, receipts, and transactions. ## Usage -Run the following command to start the Scribe: +Run the following command to start Scribe: ```bash $ go run main.go ``` -Then the Scribe command line will be exposed. You can use the following commands: +Then Scribe command line will be exposed. You can use the following commands: ```bash -# Start the Scribe -$ scribe --config --db --path -# Call a single backfill with the scribe -$ backfill --config --db --path -# Start the Scribe server +# Start Scribe indexer +$ Scribe --config --db --path +# Start Scribe server $ server --port --db --path ``` -## Configuration +### Deploy +See /charts/scribe for the deployment helm chart for this service + +### Configuration +There are many ways to augment Scribe's behavior. Before running Scribe, please take a look at the different parameters. +``` +rpc_url: The url of the rpc aggregator +chains: A list of chains to index + chain_id: The ID of the chain + get_logs_range: is the number of blocks to request in a single getLogs request. + get_logs_batch_amount: is the number of getLogs requests to include in a batch request. + store_concurrency: is the number of goroutines to use when storing data. + concurrency_threshold: is the max number of block from head in which concurrent operations (store, getlogs) is allowed. + livefill_threshold: number of blocks away from the head to start livefilling (see "Understanding Scribe Indexer" to understand this better) + livefill_range: range in whcih the getLogs request for the livefill contracts will be requesting. + livefill_flush_interval: the interval in which the unconfirmed livefill table will be flushed. + confirmations: the number of blocks from head that the livefiller will livefill up to (and where the unconfirmed livefill indexer will begin) + contracts: stores all the contract information for the chain + address: address of the contract + start_block: block to start indexing the contract from (block with the first tx) +``` + + +#### Example Config +```yaml +rpc_url: +chains: +- chain_id: 1 + get_logs_range: 500 + get_block_batch_amount: 1 + store_concurrency: 100 + concurrency_threshold: 50000 + livefill_threshold: 300 + livefill_range: 200 + livefill_flush_interval: 10000 + confirmations: 200 + contracts: + - address: 0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b + start_block: 18646320 +``` -chain_id: The ID of the chain -required_confirmations: the number of confirmations required for a block to be finalized -contracts: stores all the contract information for the chain. -get_logs_range: is the number of blocks to request in a single getLogs request. -get_logs_batch_amount: is the number of getLogs requests to include in a single batch request. -store_concurrency: is the number of goroutines to use when storing data. -concurrency_threshold: is the max number of block from head in which concurrent operations (store, getlogs) is allowed. +## Understanding the Scribe Indexer +The Scribe indexer is composed of three components +1. `Fetcher`: Takes a list of contracts, a block range, and fetches and feeds logs into a channel to be consumed by an indexer. +2. `Indexer`: Takes a list of contracts, a block range, and a config and stores logs, receipts, and txs for all events in that range. +3. `ChainIndexer`: Runs 2+ indexers per chain. + 1. 1 indexer for each contract behind the specified livefill threshold (backfill) + 2. 1 indexer for every contract within the specified livefill threshold (livefill) + 3. 1 indexer for every contract within the specified "unconfirmed" range at the chain tip. (unconfirmed livefill) + + +### Chain level flow +1. Scribe initializes with config +2. Each chain has a `ChainIndexer` spun up and runs it in a go routine +3. The `ChainIndexer` checks all contracts for their `lastIndexed` block. Contracts without a `lastIndexed` or with a `lastIndexed` block outside the +specified livefill block range are put into individual indexers (backfill). All other contracts are collected into a single indexer (livefill). +4. A contract in an individual indexer (backfill) reaches the livefill threshold, it is passed into a channel where it will be picked up by the go routine running the +indexer for the livefill contracts. +5. While contracts are being livefilled, there is another indexer with all contracts listed on the given chain. This indexer is used to livefill the unconfirmed range at the chain tip. This range is set by the config +and stores data in separate tables than the other indexers. This table has stale rows (old rows) deleted every few hours (set in config). -## Directory Structure +### Indexer level flow +1. `Indexer` is initialized with config and a list of contracts +2. `Indexer` is started with a block range +3. The indexer creates a `Fetcher` that feeds logs into a channel that is then consumed by the indexer. The fetcher retrieves logs in chunks specified in the config +(`get_logs_range` and `get_logs_batch_amount`). +4. When a new log is retrieved the indexer gets the tx, block header (timestamp), and receipt (and all of its logs) for that log and the stores them in the database. Depending on the concurrency +settings, Scribe will spin up multiple concurrent processes for retrieving this data and storing. It is recommended that concurrency (`concurrency_threshold`) is set to at least a couple hours +from the head to ensure that data is inserted into the database in order (if streaming). +5. The indexer will continue to fetch and store data until it reaches the end of the block range. + + +### Directory Structure
-scribe
+Scribe
 ├── api: Contains the Swagger API definition and GQL Client tests.
-├── backfill: Used to fetch logs, receipts, and transactions to store in the database
-├── cmd: The command line interface functions for running the Scribe and GraphQL server
-├── config: Configuration files for the Scribe
+├── backend: The backend implementation for Scribe
+├── client: Client implementation for Scribe (embedded/remote)
+├── cmd: The command line interface functions for running Scribe and GraphQL server
+├── config: Configuration files for Scribe
 ├── db: The database schema and functions for interacting with the database
-├── graphql: GraphQL implementation for the Scribe's recorded data
+├── graphql: GraphQL implementation for Scribe's recorded data
 │   ├── client: The client interface for the GraphQL server
-│   ├── contrib: The GraphQL generators for the Scribe
+│   ├── contrib: The GraphQL generators for Scribe
 │   └── server: The server implementation for GraphQL
 │       └── graph: The server's models, resolvers, and schemas
-├── grpc: The gRPC client implementation for the Scribe
-├── internal: Internal packages for the Scribe
-├── node: The new block listener that calls backfill
+├── grpc: The gRPC client implementation for Scribe
+├── internal: Internal packages for Scribe
+├── logger: Handles logging for various events in Scribe.
+├── metadata: Provides metadata for building .
 ├── scripts: Scripts for Scribe
-└── testutil: Test utilities for the Scribe
+├── service: Service holds Scribe indexer code (Fetcher, Indexer, ChainIndexer)
+├── testhelper: Assists testing in downstream services.
+├── testutil: Test utilities suite for Scribe
+└── types: Holds various custom types for Scribe
 
+ + +### Schema +The schema for each table in the database can be found at db/datastore/sql/base + ## Regenerating protobuf definitions: `make generate` -## Flow -1. Scribe initializes with config -2. A go routine is started for each chain in the config -3. Each go routine starts a concurrent backfill for each contract in the config -4. Each backfill fetches (eth_getLogs) for each contract in chunks. Chunk size is set in the config (GetLogsBatchAmount * GetLogsRange). The fetching flow is blocked by the channel size (15). -5. As the fetching channel is filled, the backfiller starts loads logs into another channel for processing. -6. As logs are taken from the processing channel, scribe does a eth_getTransactionReceipt for each log and a eth_getTransaction for each receipt. -7. Scribe stores the receipt, tx, blocktime for that tx, and all logs from the receipt. diff --git a/services/scribe/cmd/commands.go b/services/scribe/cmd/commands.go index 02072cd4e0..7e826a27ec 100644 --- a/services/scribe/cmd/commands.go +++ b/services/scribe/cmd/commands.go @@ -1,19 +1,21 @@ package cmd +// TODO update this to match new commands + migrate flags to config. import ( "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/service" // used to embed markdown. _ "embed" "fmt" + markdown "github.com/MichaelMure/go-term-markdown" "github.com/hashicorp/consul/sdk/freeport" "github.com/jftuga/termsize" "github.com/synapsecns/sanguine/core" "github.com/synapsecns/sanguine/services/scribe/api" - "github.com/synapsecns/sanguine/services/scribe/backfill" "github.com/synapsecns/sanguine/services/scribe/config" "github.com/synapsecns/sanguine/services/scribe/db" - "github.com/synapsecns/sanguine/services/scribe/node" "github.com/urfave/cli/v2" ) @@ -60,7 +62,7 @@ var pathFlag = &cli.StringFlag{ Required: true, } -func createScribeParameters(c *cli.Context) (eventDB db.EventDB, clients map[uint32][]backfill.ScribeBackend, scribeConfig config.Config, err error) { +func createScribeParameters(c *cli.Context) (eventDB db.EventDB, clients map[uint32][]backend.ScribeBackend, scribeConfig config.Config, err error) { scribeConfig, err = config.DecodeConfig(core.ExpandOrReturnPath(c.String(configFlag.Name))) if err != nil { return nil, nil, scribeConfig, fmt.Errorf("could not decode config: %w", err) @@ -71,10 +73,10 @@ func createScribeParameters(c *cli.Context) (eventDB db.EventDB, clients map[uin return nil, nil, scribeConfig, fmt.Errorf("could not initialize database: %w", err) } - clients = make(map[uint32][]backfill.ScribeBackend) + clients = make(map[uint32][]backend.ScribeBackend) for _, client := range scribeConfig.Chains { for confNum := 1; confNum <= MaxConfirmations; confNum++ { - backendClient, err := backfill.DialBackend(c.Context, fmt.Sprintf("%s/%d/rpc/%d", scribeConfig.RPCURL, confNum, client.ChainID), metrics.Get()) + backendClient, err := backend.DialBackend(c.Context, fmt.Sprintf("%s/%d/rpc/%d", scribeConfig.RPCURL, confNum, client.ChainID), metrics.Get()) if err != nil { return nil, nil, scribeConfig, fmt.Errorf("could not start client for %s", fmt.Sprintf("%s/1/rpc/%d", scribeConfig.RPCURL, client.ChainID)) } @@ -94,7 +96,7 @@ var scribeCommand = &cli.Command{ if err != nil { return err } - scribe, err := node.NewScribe(db, clients, decodeConfig, metrics.Get()) + scribe, err := service.NewScribe(db, clients, decodeConfig, metrics.Get()) if err != nil { return fmt.Errorf("could not create scribe: %w", err) } diff --git a/services/scribe/config/chain.go b/services/scribe/config/chain.go index 13f2c3724a..fbaeb24906 100644 --- a/services/scribe/config/chain.go +++ b/services/scribe/config/chain.go @@ -9,16 +9,6 @@ import ( // TODO add tests for this config type -// ConfirmationConfig holds config data for reorg protection. -type ConfirmationConfig struct { - // RequiredConfirmations is the number of confirmations required for a block to be finalized. - RequiredConfirmations uint32 `yaml:"required_confirmations"` - // ConfirmationThreshold is the number of blocks to wait until doing a reorg check. - ConfirmationThreshold uint64 `yaml:"confirmation_threshold"` - // ConfirmationMinWait is the amount of time in seconds to wait before checking confirmations - ConfirmationRefreshRate int `yaml:"confirmation_min_wait"` -} - // ChainConfig defines the config for a specific chain. type ChainConfig struct { // ChainID is the ID of the chain. @@ -35,8 +25,14 @@ type ChainConfig struct { ConcurrencyThreshold uint64 `yaml:"concurrency_threshold"` // GetBlockBatchSize is the amount of blocks to get at a time when doing confirmations. GetBlockBatchAmount int `yaml:"get_block_batch_amount"` - // ConfirmationConfig holds config data for reorg protection. - ConfirmationConfig ConfirmationConfig `yaml:"confirmation_config"` + // Confirmations is the number of blocks away from the head to livefill to. + Confirmations uint64 `yaml:"confirmations"` + // LivefillThreshold is the number of blocks away from the head minus confirmations to livefill to. + LivefillThreshold uint64 `yaml:"livefill_threshold"` + // LivefillRange is the number of blocks that the livefill indexer with request for with get logs at once. + LivefillRange uint64 `yaml:"livefill_range"` + // LivefillFlushInterval is how long to wait before flushing the livefill indexer db (in seconds) + LivefillFlushInterval uint64 `yaml:"livefill_flush_interval"` } // ChainConfigs contains an array of ChainConfigs. diff --git a/services/scribe/config/contract.go b/services/scribe/config/contract.go index 171b01aaa0..aee5170a5c 100644 --- a/services/scribe/config/contract.go +++ b/services/scribe/config/contract.go @@ -14,6 +14,8 @@ type ContractConfig struct { Address string `yaml:"address"` // StartBlock is the block number to start indexing events from. StartBlock uint64 `yaml:"start_block"` + // EndBlock is the block number to stop indexing events at. If this is set, it will enforce the start block and ignore the last indexed block. + EndBlock uint64 `yaml:"end_block"` // RefreshRate is the rate at which the contract is refreshed. RefreshRate uint64 `yaml:"refresh_rate"` } diff --git a/services/scribe/db/athead_test.go b/services/scribe/db/athead_test.go new file mode 100644 index 0000000000..ba8a106007 --- /dev/null +++ b/services/scribe/db/athead_test.go @@ -0,0 +1,264 @@ +package db_test + +import ( + "github.com/brianvoe/gofakeit/v6" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + . "github.com/stretchr/testify/assert" + "github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner" + "github.com/synapsecns/sanguine/ethergo/signer/wallet" + "github.com/synapsecns/sanguine/services/scribe/db" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + "math/big" + "time" +) + +func (t *DBSuite) TestUnconfirmedLogsQuery() { + t.RunOnAllDBs(func(testDB db.EventDB) { + chainID := gofakeit.Uint32() + contractAddress := common.BigToAddress(big.NewInt(gofakeit.Int64())) + const confirmedBlockHeight = 100 + const headBlock = 110 + for i := 1; i <= confirmedBlockHeight; i++ { + txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + log := t.MakeRandomLog(txHash) + log.BlockNumber = uint64(i) + log.Address = contractAddress + // For testing, all confirmed txs will have an index of 1 + log.Index = 1 + err := testDB.StoreLogs(t.GetTestContext(), chainID, log) + Nil(t.T(), err) + } + err := testDB.StoreLastIndexed(t.GetTestContext(), contractAddress, chainID, confirmedBlockHeight, scribeTypes.IndexingConfirmed) + Nil(t.T(), err) + + // For testing, having the same txhash for all unconfirmed blocks. + for i := confirmedBlockHeight + 1; i <= headBlock; i++ { + txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + + log := t.MakeRandomLog(txHash) + log.BlockNumber = uint64(i) + log.TxHash = common.BigToHash(big.NewInt(gofakeit.Int64())) + log.Address = contractAddress + // For testing, all unconfirmed txs will have an index of 0 + log.Index = 0 + err := testDB.StoreLogsAtHead(t.GetTestContext(), chainID, log) + Nil(t.T(), err) + } + + logFilter := db.LogFilter{ + ChainID: chainID, + ContractAddress: contractAddress.String(), + } + logs, err := testDB.RetrieveLogsFromHeadRangeQuery(t.GetTestContext(), logFilter, 0, headBlock, 1) + Nil(t.T(), err) + Equal(t.T(), 100, len(logs)) + if len(logs) == 100 { + Equal(t.T(), uint(0), logs[0].Index) + // Check block range + Equal(t.T(), uint64(110), logs[0].BlockNumber) + Equal(t.T(), uint64(11), logs[99].BlockNumber) + // check threshold of confirmed vs unconfirmed + Equal(t.T(), uint(1), logs[10].Index) + Equal(t.T(), uint(0), logs[9].Index) + } + logs, err = testDB.RetrieveLogsFromHeadRangeQuery(t.GetTestContext(), logFilter, 0, headBlock, 2) + Nil(t.T(), err) + + Equal(t.T(), 10, len(logs)) + if len(logs) == 10 { + // Check that these are confirmed logs + Equal(t.T(), uint(1), logs[0].Index) + } + }) +} + +func (t *DBSuite) TestFlushLog() { + t.RunOnAllDBs(func(testDB db.EventDB) { + chainID := gofakeit.Uint32() + contractAddress := common.BigToAddress(big.NewInt(gofakeit.Int64())) + const deleteUpToBlock = 110 + const desiredBlockHeight = 200 + for i := 1; i <= deleteUpToBlock; i++ { + txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + log := t.MakeRandomLog(txHash) + log.BlockNumber = uint64(i) + log.Address = contractAddress + + // For testing, all to delete txs will have an index of 1 + log.Index = 1 + err := testDB.StoreLogsAtHead(t.GetTestContext(), chainID, log) + Nil(t.T(), err) + } + time.Sleep(1 * time.Second) + deleteTimestamp := time.Now().UnixNano() + for i := deleteUpToBlock + 1; i <= desiredBlockHeight; i++ { + txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + + log := t.MakeRandomLog(txHash) + log.BlockNumber = uint64(i) + log.TxHash = common.BigToHash(big.NewInt(gofakeit.Int64())) + log.Address = contractAddress + // For testing, all no delete txs will have an index of 0 + log.Index = 0 + err := testDB.StoreLogsAtHead(t.GetTestContext(), chainID, log) + Nil(t.T(), err) + } + logFilter := db.LogFilter{ + ChainID: chainID, + ContractAddress: contractAddress.String(), + } + logs, err := testDB.RetrieveLogsFromHeadRangeQuery(t.GetTestContext(), logFilter, 0, desiredBlockHeight, 1) + Nil(t.T(), err) + Equal(t.T(), 100, len(logs)) + if 100 == len(logs) { + Equal(t.T(), uint64(desiredBlockHeight), logs[0].BlockNumber) + } + err = testDB.FlushFromHeadTables(t.GetTestContext(), deleteTimestamp) + Nil(t.T(), err) + logs, err = testDB.RetrieveLogsFromHeadRangeQuery(t.GetTestContext(), logFilter, 0, desiredBlockHeight, 1) + Nil(t.T(), err) + Equal(t.T(), 90, len(logs)) + if len(logs) == 90 { + // Check that the earliest log has a timestamp of 110 + Equal(t.T(), uint(0), logs[0].Index) + Equal(t.T(), uint64(desiredBlockHeight), logs[0].BlockNumber) + } + }) +} + +// nolint:dupl +func (t *DBSuite) TestUnconfirmedTxsQuery() { + t.RunOnAllDBs(func(testDB db.EventDB) { + chainID := gofakeit.Uint32() + const lastIndexed = 100 + const confirmedBlockHeight = 100 + const headBlock = 110 + testWallet, err := wallet.FromRandom() + Nil(t.T(), err) + signer := localsigner.NewSigner(testWallet.PrivateKey()) + + for i := 1; i <= confirmedBlockHeight; i++ { + // Nonce is used to determine if a tx is confirmed or not + testTx := types.NewTx(&types.LegacyTx{ + Nonce: uint64(1), + GasPrice: new(big.Int).SetUint64(gofakeit.Uint64()), + Gas: gofakeit.Uint64(), + To: addressPtr(common.BigToAddress(new(big.Int).SetUint64(gofakeit.Uint64()))), + Value: new(big.Int).SetUint64(gofakeit.Uint64()), + Data: []byte(gofakeit.Paragraph(1, 2, 3, " ")), + }) + transactor, err := localsigner.NewSigner(testWallet.PrivateKey()).GetTransactor(t.GetTestContext(), testTx.ChainId()) + Nil(t.T(), err) + + signedTx, err := transactor.Signer(signer.Address(), testTx) + Nil(t.T(), err) + + err = testDB.StoreEthTx(t.GetTestContext(), signedTx, chainID, common.BigToHash(big.NewInt(5)), uint64(i), gofakeit.Uint64()) + Nil(t.T(), err) + } + + // For testing, have the same txhash for all unconfirmed blocks. + for i := confirmedBlockHeight + 1; i <= headBlock; i++ { + testTx := types.NewTx(&types.LegacyTx{ + Nonce: uint64(0), + GasPrice: new(big.Int).SetUint64(gofakeit.Uint64()), + Gas: gofakeit.Uint64(), + To: addressPtr(common.BigToAddress(new(big.Int).SetUint64(gofakeit.Uint64()))), + Value: new(big.Int).SetUint64(gofakeit.Uint64()), + Data: []byte(gofakeit.Paragraph(1, 2, 3, " ")), + }) + transactor, err := localsigner.NewSigner(testWallet.PrivateKey()).GetTransactor(t.GetTestContext(), testTx.ChainId()) + Nil(t.T(), err) + + signedTx, err := transactor.Signer(signer.Address(), testTx) + Nil(t.T(), err) + + err = testDB.StoreEthTxAtHead(t.GetTestContext(), signedTx, chainID, common.BigToHash(big.NewInt(5)), uint64(i), gofakeit.Uint64()) + Nil(t.T(), err) + } + + txFilter := db.EthTxFilter{ + ChainID: chainID, + } + txs, err := testDB.RetrieveUnconfirmedEthTxsFromHeadRangeQuery(t.GetTestContext(), txFilter, 0, headBlock, lastIndexed, 1) + Nil(t.T(), err) + Equal(t.T(), 100, len(txs)) + if len(txs) == 100 { + Equal(t.T(), uint64(0), txs[0].Tx.Nonce()) + // Check block range + Equal(t.T(), uint64(110), txs[0].BlockNumber) + Equal(t.T(), uint64(11), txs[99].BlockNumber) + // check threshold of confirmed vs unconfirmed + Equal(t.T(), uint64(1), txs[10].Tx.Nonce()) + Equal(t.T(), uint64(0), txs[9].Tx.Nonce()) + } + txs, err = testDB.RetrieveUnconfirmedEthTxsFromHeadRangeQuery(t.GetTestContext(), txFilter, 0, headBlock, lastIndexed, 2) + Nil(t.T(), err) + + Equal(t.T(), 10, len(txs)) + if len(txs) == 10 { + // Check that these are confirmed logs + Equal(t.T(), uint64(1), txs[0].Tx.Nonce()) + } + }) +} + +func (t *DBSuite) TestUnconfirmedRecieptQuery() { + t.RunOnAllDBs(func(testDB db.EventDB) { + chainID := gofakeit.Uint32() + contractAddress := common.BigToAddress(big.NewInt(gofakeit.Int64())) + const confirmedBlockHeight = 100 + const headBlock = 110 + for i := 1; i <= confirmedBlockHeight; i++ { + txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + receipt := t.MakeRandomReceipt(txHash) + receipt.BlockNumber = big.NewInt(int64(i)) + receipt.ContractAddress = contractAddress + // For testing, all confirmed receipts will have a status of 1 + receipt.Status = 1 + err := testDB.StoreReceipt(t.GetTestContext(), chainID, receipt) + Nil(t.T(), err) + } + err := testDB.StoreLastIndexed(t.GetTestContext(), contractAddress, chainID, confirmedBlockHeight, scribeTypes.IndexingConfirmed) + Nil(t.T(), err) + + // For testing, having the same txhash for all unconfirmed blocks. + for i := confirmedBlockHeight + 1; i <= headBlock; i++ { + txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + + receipt := t.MakeRandomReceipt(txHash) + receipt.BlockNumber = big.NewInt(int64(i)) + receipt.ContractAddress = contractAddress + // For testing, all confirmed receipts will have a status of 1 + receipt.Status = 0 + err := testDB.StoreReceiptAtHead(t.GetTestContext(), chainID, receipt) + Nil(t.T(), err) + } + + receiptFilter := db.ReceiptFilter{ + ChainID: chainID, + ContractAddress: contractAddress.String(), + } + receipts, err := testDB.RetrieveReceiptsFromHeadRangeQuery(t.GetTestContext(), receiptFilter, 0, headBlock, 1) + Nil(t.T(), err) + Equal(t.T(), 100, len(receipts)) + if len(receipts) == 100 { + Equal(t.T(), uint64(0), receipts[0].Status) + // Check block range + Equal(t.T(), uint64(110), receipts[0].BlockNumber.Uint64()) + Equal(t.T(), uint64(11), receipts[99].BlockNumber.Uint64()) + // check threshold of confirmed vs unconfirmed + Equal(t.T(), uint64(1), receipts[10].Status) + Equal(t.T(), uint64(0), receipts[9].Status) + } + receipts, err = testDB.RetrieveReceiptsFromHeadRangeQuery(t.GetTestContext(), receiptFilter, 0, headBlock, 2) + Nil(t.T(), err) + + Equal(t.T(), 10, len(receipts)) + if len(receipts) == 10 { + // Check that these are confirmed logs + Equal(t.T(), uint64(1), receipts[0].Status) + } + }) +} diff --git a/services/scribe/db/datastore/sql/base/athead.go b/services/scribe/db/datastore/sql/base/athead.go new file mode 100644 index 0000000000..3cbd572015 --- /dev/null +++ b/services/scribe/db/datastore/sql/base/athead.go @@ -0,0 +1,274 @@ +package base + +import ( + "context" + "database/sql" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/synapsecns/sanguine/core/dbcommon" + "github.com/synapsecns/sanguine/services/scribe/db" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "time" +) + +// TODO support more filtering options + +// StoreLogsAtHead stores a log at the Head of the chain. +func (s Store) StoreLogsAtHead(ctx context.Context, chainID uint32, logs ...types.Log) error { + var storeLogs []LogAtHead + for _, log := range logs { + var topics []sql.NullString + + topicsLength := len(log.Topics) + // Ethereum topics are always 3 long, excluding the primary topic. + indexedTopics := 3 + // Loop through the topics and convert them to nullStrings. + // If the topic is empty, we set Valid to false. + // If the topic is not empty, provide its string value and set Valid to true. + for index := 0; index <= indexedTopics+1; index++ { + if index < topicsLength { + topics = append(topics, sql.NullString{ + String: log.Topics[index].String(), + Valid: true, + }) + } else { + topics = append(topics, sql.NullString{ + Valid: false, + }) + } + } + + newLog := LogAtHead{ + ContractAddress: log.Address.String(), + ChainID: chainID, + PrimaryTopic: topics[0], + TopicA: topics[1], + TopicB: topics[2], + TopicC: topics[3], + Data: log.Data, + BlockNumber: log.BlockNumber, + TxHash: log.TxHash.String(), + TxIndex: uint64(log.TxIndex), + BlockHash: log.BlockHash.String(), + BlockIndex: uint64(log.Index), + Removed: log.Removed, + Confirmed: false, + InsertTime: uint64(time.Now().UnixNano()), + } + + storeLogs = append(storeLogs, newLog) + } + + dbTx := s.DB().WithContext(ctx) + if s.db.Dialector.Name() == dbcommon.Sqlite.String() { + dbTx = dbTx.Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: ContractAddressFieldName}, {Name: ChainIDFieldName}, {Name: TxHashFieldName}, {Name: BlockIndexFieldName}, + }, + DoNothing: true, + }).CreateInBatches(&storeLogs, 10) + } else { + dbTx = dbTx.Clauses(clause.Insert{ + Modifier: "IGNORE", + }).Create(&storeLogs) + } + + if dbTx.Error != nil { + return fmt.Errorf("could not store log: %w", dbTx.Error) + } + + return nil +} + +// StoreReceiptAtHead stores a receipt. +func (s Store) StoreReceiptAtHead(ctx context.Context, chainID uint32, receipt types.Receipt) error { + dbTx := s.DB().WithContext(ctx) + if s.DB().Dialector.Name() == dbcommon.Sqlite.String() { + dbTx = dbTx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: TxHashFieldName}, {Name: ChainIDFieldName}}, + DoNothing: true, + }) + } else { + dbTx = dbTx.Clauses(clause.Insert{ + Modifier: "IGNORE", + }) + } + dbTx = dbTx.Create(&ReceiptAtHead{ + ChainID: chainID, + Type: receipt.Type, + PostState: receipt.PostState, + Status: receipt.Status, + CumulativeGasUsed: receipt.CumulativeGasUsed, + Bloom: receipt.Bloom.Bytes(), + TxHash: receipt.TxHash.String(), + ContractAddress: receipt.ContractAddress.String(), + GasUsed: receipt.GasUsed, + BlockHash: receipt.BlockHash.String(), + BlockNumber: receipt.BlockNumber.Uint64(), + TransactionIndex: uint64(receipt.TransactionIndex), + Confirmed: false, + InsertTime: uint64(time.Now().UnixNano()), + }) + + if dbTx.Error != nil { + return fmt.Errorf("could not store receipt: %w", dbTx.Error) + } + + return nil +} + +// StoreEthTxAtHead stores a processed text at Head. +func (s Store) StoreEthTxAtHead(ctx context.Context, tx *types.Transaction, chainID uint32, blockHash common.Hash, blockNumber uint64, transactionIndex uint64) error { + marshalledTx, err := tx.MarshalBinary() + if err != nil { + return fmt.Errorf("could not marshall tx to binary: %w", err) + } + dbTx := s.DB().WithContext(ctx) + if s.DB().Dialector.Name() == dbcommon.Sqlite.String() { + dbTx = dbTx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: TxHashFieldName}, {Name: ChainIDFieldName}}, + DoNothing: true, + }) + } else { + dbTx = dbTx.Clauses(clause.Insert{ + Modifier: "IGNORE", + }) + } + + dbTx = dbTx.Create(&EthTxAtHead{ + TxHash: tx.Hash().String(), + ChainID: chainID, + BlockHash: blockHash.String(), + BlockNumber: blockNumber, + RawTx: marshalledTx, + GasFeeCap: tx.GasFeeCap().Uint64(), + Confirmed: false, + TransactionIndex: transactionIndex, + InsertTime: uint64(time.Now().UnixNano()), + }) + + if dbTx.Error != nil { + return fmt.Errorf("could not create raw tx: %w", dbTx.Error) + } + + return nil +} + +// RetrieveLogsFromHeadRangeQuery retrieves all logs (including unconfirmed) for a given contract address, chain ID, and range. +func (s Store) RetrieveLogsFromHeadRangeQuery(ctx context.Context, logFilter db.LogFilter, startBlock uint64, endBlock uint64, page int) ([]*types.Log, error) { + if logFilter.ContractAddress == "" || logFilter.ChainID == 0 { + return nil, fmt.Errorf("contract address and chain ID must be passed") + } + if page < 1 { + page = 1 + } + + lastIndexed, err := s.RetrieveLastIndexed(ctx, common.HexToAddress(logFilter.ContractAddress), logFilter.ChainID, false) + if err != nil { + return nil, fmt.Errorf("could not get last block indexed: %w", err) + } + queryFilter := logFilterToQuery(logFilter) + + var dbLogs []Log + subQuery1 := s.DB().WithContext(ctx).ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Model(Log{}).Select("*").Where("block_number BETWEEN ? AND ?", startBlock, lastIndexed).Where(queryFilter).Find(&[]Log{}) + }) + subQuery2 := s.DB().WithContext(ctx).ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Model(LogAtHead{}).Select(LogColumns).Where("block_number BETWEEN ? AND ?", lastIndexed+1, endBlock).Where(queryFilter).Find(&[]Log{}) + }) + query := fmt.Sprintf("SELECT * FROM (%s UNION %s) AS unionedTable ORDER BY %s DESC, %s DESC LIMIT %d OFFSET %d", subQuery1, subQuery2, BlockNumberFieldName, BlockIndexFieldName, PageSize, (page-1)*PageSize) + dbTx := s.DB().WithContext(ctx).Raw(query).Scan(&dbLogs) + + if dbTx.Error != nil { + return nil, fmt.Errorf("error getting newly confirmed data %w", dbTx.Error) + } + return buildLogsFromDBLogs(dbLogs), nil +} + +// RetrieveReceiptsFromHeadRangeQuery retrieves all receipts (including unconfirmed) for a given contract address, chain ID, and range. +func (s Store) RetrieveReceiptsFromHeadRangeQuery(ctx context.Context, receiptFilter db.ReceiptFilter, startBlock uint64, endBlock uint64, page int) ([]types.Receipt, error) { + if receiptFilter.ContractAddress == "" || receiptFilter.ChainID == 0 { + return nil, fmt.Errorf("contract address and chain ID must be passed") + } + if page < 1 { + page = 1 + } + + lastIndexed, err := s.RetrieveLastIndexed(ctx, common.HexToAddress(receiptFilter.ContractAddress), receiptFilter.ChainID, false) + if err != nil { + return nil, fmt.Errorf("could not get last block indexed: %w", err) + } + queryFilter := receiptFilterToQuery(receiptFilter) + + var dbReceipts []Receipt + subQuery1 := s.DB().WithContext(ctx).ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Model(Receipt{}).Select("*").Where("block_number BETWEEN ? AND ?", startBlock, lastIndexed).Where(queryFilter).Find(&[]Receipt{}) + }) + subQuery2 := s.DB().WithContext(ctx).ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Model(ReceiptAtHead{}).Select(ReceiptColumns).Where("block_number BETWEEN ? AND ?", lastIndexed+1, endBlock).Where(queryFilter).Find(&[]Receipt{}) + }) + query := fmt.Sprintf("SELECT * FROM (%s UNION %s) AS unionedTable ORDER BY %s DESC, %s DESC LIMIT %d OFFSET %d", subQuery1, subQuery2, BlockNumberFieldName, TransactionIndexFieldName, PageSize, (page-1)*PageSize) + dbTx := s.DB().WithContext(ctx).Raw(query).Scan(&dbReceipts) + + if dbTx.Error != nil { + return nil, fmt.Errorf("error getting newly confirmed data %w", dbTx.Error) + } + receipts, err := s.buildReceiptsFromDBReceipts(ctx, dbReceipts, receiptFilter.ChainID) + if err != nil { + return nil, fmt.Errorf("error building receipts from db receipts: %w", err) + } + return receipts, nil +} + +// TODO make a query for getting latest tx + +// RetrieveUnconfirmedEthTxsFromHeadRangeQuery retrieves all unconfirmed ethTx for a given chain ID and range. +// lastIndexed is passed because the ethtx table does not have contract addresses, thus the last indexed for that contract +// cannot be determined for the join. Pass last indexed for the log that you are trying to mature with data. +func (s Store) RetrieveUnconfirmedEthTxsFromHeadRangeQuery(ctx context.Context, ethTxFilter db.EthTxFilter, startBlock uint64, endBlock uint64, lastIndexed uint64, page int) ([]db.TxWithBlockNumber, error) { + if ethTxFilter.ChainID == 0 { + return nil, fmt.Errorf("chain ID must be passed") + } + if page < 1 { + page = 1 + } + + queryFilter := ethTxFilterToQuery(ethTxFilter) + var dbEthTxs []EthTx + subQuery1 := s.DB().WithContext(ctx).ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Model(EthTx{}).Select("*").Where("block_number BETWEEN ? AND ?", startBlock, lastIndexed).Where(queryFilter).Find(&[]EthTx{}) + }) + subQuery2 := s.DB().WithContext(ctx).ToSQL(func(tx *gorm.DB) *gorm.DB { + return tx.Model(EthTxAtHead{}).Select(EthTxColumns).Where("block_number BETWEEN ? AND ?", lastIndexed+1, endBlock).Where(queryFilter).Find(&[]EthTx{}) + }) + query := fmt.Sprintf("SELECT * FROM (%s UNION %s) AS unionedTable ORDER BY %s DESC, %s DESC LIMIT %d OFFSET %d", subQuery1, subQuery2, BlockNumberFieldName, TransactionIndexFieldName, PageSize, (page-1)*PageSize) + dbTx := s.DB().WithContext(ctx).Raw(query).Scan(&dbEthTxs) + + if dbTx.Error != nil { + return nil, fmt.Errorf("error getting newly confirmed data %w", dbTx.Error) + } + txs, err := buildEthTxsFromDBEthTxs(dbEthTxs) + if err != nil { + return nil, fmt.Errorf("error building receipts from db receipts: %w", err) + } + return txs, nil +} + +// FlushFromHeadTables deletes all logs, receipts, and txs from the head table that are older than the given time. +func (s Store) FlushFromHeadTables(ctx context.Context, time int64) error { + err := s.DB().WithContext(ctx).Model(&LogAtHead{}).Where("insert_time < ?", time).Delete(&LogAtHead{}).Error + if err != nil { + return fmt.Errorf("error flushing logs from head: %w", err) + } + err = s.DB().WithContext(ctx).Model(&EthTxAtHead{}).Where("insert_time < ?", time).Delete(&EthTxAtHead{}).Error + if err != nil { + return fmt.Errorf("error flushing eth_txes from head: %w", err) + } + err = s.DB().WithContext(ctx).Model(&ReceiptAtHead{}).Where("insert_time < ?", time).Delete(&ReceiptAtHead{}).Error + if err != nil { + return fmt.Errorf("error flushing receipts from head: %w", err) + } + return nil +} diff --git a/services/scribe/db/datastore/sql/base/base_store.go b/services/scribe/db/datastore/sql/base/base_store.go index e4043dbc8a..5c4986d14f 100644 --- a/services/scribe/db/datastore/sql/base/base_store.go +++ b/services/scribe/db/datastore/sql/base/base_store.go @@ -26,7 +26,7 @@ func (s Store) DB() *gorm.DB { // see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time func GetAllModels() (allModels []interface{}) { allModels = append(allModels, - &Log{}, &Receipt{}, &EthTx{}, &LastIndexedInfo{}, &LastConfirmedBlockInfo{}, &BlockTime{}, &LastBlockTime{}, + &Log{}, &Receipt{}, &EthTx{}, &LastIndexedInfo{}, &LastConfirmedBlockInfo{}, &BlockTime{}, &LastBlockTime{}, &LogAtHead{}, &ReceiptAtHead{}, &EthTxAtHead{}, // InsertTime is the time at which this log receipt inserted ) return allModels } diff --git a/services/scribe/db/datastore/sql/base/lastindexed.go b/services/scribe/db/datastore/sql/base/lastindexed.go index 5d3f012861..146f5748c1 100644 --- a/services/scribe/db/datastore/sql/base/lastindexed.go +++ b/services/scribe/db/datastore/sql/base/lastindexed.go @@ -3,6 +3,7 @@ package base import ( "context" "fmt" + "golang.org/x/sync/errgroup" "github.com/ethereum/go-ethereum/common" "github.com/synapsecns/sanguine/core/metrics" @@ -11,10 +12,12 @@ import ( "gorm.io/gorm/clause" ) +const lastIndexedLivefillKey = "0x0000000000000000000000000000000000000000" + // StoreLastIndexed stores the last indexed block number for a contract. // It updates the value if there is a previous last indexed value, and creates a new -// entry if there is no previous value. -func (s Store) StoreLastIndexed(parentCtx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64) (err error) { +// entry if there's no previous value. +func (s Store) StoreLastIndexed(parentCtx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64, livefillAtHead bool) (err error) { ctx, span := s.metrics.Tracer().Start(parentCtx, "StoreLastIndexed", trace.WithAttributes( attribute.String("contractAddress", contractAddress.String()), attribute.Int("chainID", int(chainID)), @@ -25,6 +28,11 @@ func (s Store) StoreLastIndexed(parentCtx context.Context, contractAddress commo metrics.EndSpanWithErr(span, err) }() + address := contractAddress.String() + if livefillAtHead { + address = lastIndexedLivefillKey + } + dbTx := s.DB().WithContext(ctx). Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: ContractAddressFieldName}, {Name: ChainIDFieldName}}, @@ -36,7 +44,7 @@ func (s Store) StoreLastIndexed(parentCtx context.Context, contractAddress commo Exprs: []clause.Expression{ clause.Eq{ Column: clause.Column{Name: ContractAddressFieldName}, - Value: contractAddress.String(), + Value: address, }, clause.Eq{ Column: clause.Column{Name: ChainIDFieldName}, @@ -53,7 +61,7 @@ func (s Store) StoreLastIndexed(parentCtx context.Context, contractAddress commo }, }). Create(&LastIndexedInfo{ - ContractAddress: contractAddress.String(), + ContractAddress: address, ChainID: chainID, BlockNumber: blockNumber, }) @@ -64,12 +72,17 @@ func (s Store) StoreLastIndexed(parentCtx context.Context, contractAddress commo } // RetrieveLastIndexed retrieves the last indexed block number for a contract. -func (s Store) RetrieveLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32) (uint64, error) { +func (s Store) RetrieveLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, livefill bool) (uint64, error) { entry := LastIndexedInfo{} + address := contractAddress.String() + if livefill { + address = lastIndexedLivefillKey + } + dbTx := s.DB().WithContext(ctx). Model(&LastIndexedInfo{}). Where(&LastIndexedInfo{ - ContractAddress: contractAddress.String(), + ContractAddress: address, ChainID: chainID, }). First(&entry) @@ -81,3 +94,54 @@ func (s Store) RetrieveLastIndexed(ctx context.Context, contractAddress common.A } return entry.BlockNumber, nil } + +// StoreLastIndexedMultiple stores the last indexed block numbers for numerous contracts. +func (s Store) StoreLastIndexedMultiple(parentCtx context.Context, contractAddresses []common.Address, chainID uint32, blockNumber uint64) error { + g, groupCtx := errgroup.WithContext(parentCtx) + + for i := range contractAddresses { + index := i + g.Go(func() error { + err := s.StoreLastIndexed(groupCtx, contractAddresses[index], chainID, blockNumber, false) + if err != nil { + return fmt.Errorf("could not backfill: %w", err) + } + return nil + }) + } + if err := g.Wait(); err != nil { + return fmt.Errorf("could not store last indexed: %w", err) + } + + return nil +} + +// RetrieveLastIndexedMultiple retrieves the last indexed block numbers for numerous contracts. +func (s Store) RetrieveLastIndexedMultiple(ctx context.Context, contractAddresses []common.Address, chainID uint32) (map[common.Address]uint64, error) { + var entries []LastIndexedInfo + addrStrings := make([]string, len(contractAddresses)) + for i, addr := range contractAddresses { + addrStrings[i] = addr.String() + } + + dbTx := s.DB().WithContext(ctx). + Model(&LastIndexedInfo{}). + Where("contract_address in ? AND chain_id = ?", addrStrings, chainID). + Find(&entries) + + if dbTx.Error != nil { + return nil, fmt.Errorf("could not retrieve last indexed info: %w", dbTx.Error) + } + + result := make(map[common.Address]uint64) + for _, addr := range contractAddresses { + result[addr] = 0 + } + + for _, entry := range entries { + addr := common.HexToAddress(entry.ContractAddress) + result[addr] = entry.BlockNumber + } + + return result, nil +} diff --git a/services/scribe/db/datastore/sql/base/log.go b/services/scribe/db/datastore/sql/base/log.go index 1d740a4b7d..306e74ff06 100644 --- a/services/scribe/db/datastore/sql/base/log.go +++ b/services/scribe/db/datastore/sql/base/log.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "github.com/synapsecns/sanguine/core/dbcommon" - "github.com/synapsecns/sanguine/services/scribe/db" "github.com/ethereum/go-ethereum/common" @@ -40,7 +39,7 @@ func (s Store) StoreLogs(ctx context.Context, chainID uint32, logs ...types.Log) } } - storeLogs = append(storeLogs, Log{ + newLog := Log{ ContractAddress: log.Address.String(), ChainID: chainID, PrimaryTopic: topics[0], @@ -54,8 +53,10 @@ func (s Store) StoreLogs(ctx context.Context, chainID uint32, logs ...types.Log) BlockHash: log.BlockHash.String(), BlockIndex: uint64(log.Index), Removed: log.Removed, - Confirmed: false, - }) + Confirmed: true, + } + + storeLogs = append(storeLogs, newLog) } dbTx := s.DB().WithContext(ctx) @@ -93,23 +94,6 @@ func (s Store) ConfirmLogsForBlockHash(ctx context.Context, chainID uint32, bloc return nil } -// ConfirmLogsInRange confirms logs in a range. -func (s Store) ConfirmLogsInRange(ctx context.Context, startBlock, endBlock uint64, chainID uint32) error { - rangeQuery := fmt.Sprintf("%s BETWEEN ? AND ?", BlockNumberFieldName) - dbTx := s.DB().WithContext(ctx). - Model(&Log{}). - Order(BlockNumberFieldName+" desc"). - Where(rangeQuery, startBlock, endBlock). - Where(&Log{ChainID: chainID}). - Update(ConfirmedFieldName, true) - - if dbTx.Error != nil { - return fmt.Errorf("could not confirm logs: %w", dbTx.Error) - } - - return nil -} - // DeleteLogsForBlockHash deletes logs with a given block hash. func (s Store) DeleteLogsForBlockHash(ctx context.Context, blockHash common.Hash, chainID uint32) error { dbTx := s.DB().WithContext(ctx). @@ -146,9 +130,6 @@ func (s Store) RetrieveLogsWithFilter(ctx context.Context, logFilter db.LogFilte dbLogs := []Log{} queryFilter := logFilterToQuery(logFilter) - // TODO DELETE - logger.Infof("RetrieveLogsWithFilter query: %v", queryFilter) - dbTx := s.DB().WithContext(ctx). Model(&Log{}). Where(&queryFilter). diff --git a/services/scribe/db/datastore/sql/base/model.go b/services/scribe/db/datastore/sql/base/model.go index 0e823e0583..7901218b3d 100644 --- a/services/scribe/db/datastore/sql/base/model.go +++ b/services/scribe/db/datastore/sql/base/model.go @@ -44,6 +44,9 @@ var ( // PageSize is the amount of entries per page of logs. var PageSize = 100 +// LogColumns are all of the columns of the Log table. +const LogColumns = "contract_address,chain_id,primary_topic,topic_a,topic_b,topic_c,data,block_number,tx_hash,tx_index,block_hash,block_index,removed,confirmed" + // Log stores the log of an event. type Log struct { // ContractAddress is the address of the contract that generated the event @@ -76,6 +79,9 @@ type Log struct { Confirmed bool `gorm:"confirmed"` } +// ReceiptColumns are all of the columns of the Receipt table. +const ReceiptColumns = "chain_id,receipt_type,post_state,status,cumulative_gas_used,bloom,tx_hash,contract_address,gas_used,block_hash,block_number,transaction_index,confirmed" + // Receipt stores the receipt of a transaction. type Receipt struct { // ChainID is the chain id of the receipt @@ -106,6 +112,9 @@ type Receipt struct { Confirmed bool `gorm:"column:confirmed"` } +// EthTxColumns are all of the columns of the EthTx table. +const EthTxColumns = "tx_hash,chain_id,block_hash,block_number,raw_tx,gas_fee_cap,gas_tip_cap,confirmed,transaction_index" + // EthTx contains a processed ethereum transaction. type EthTx struct { // TxHash is the hash of the transaction @@ -167,3 +176,93 @@ type LastBlockTime struct { // BlockNumber is the block number BlockNumber uint64 `gorm:"column:block_number"` } + +// LogAtHead stores the log of an event that occurred near the tip of the chain. +type LogAtHead struct { + // ContractAddress is the address of the contract that generated the event + ContractAddress string `gorm:"column:contract_address;primaryKey;index:idx_head_address,priority:1,sort:desc"` + // ChainID is the chain id of the contract that generated the event + ChainID uint32 `gorm:"column:chain_id;primaryKey;index:idx_head_address,priority:2,sort:desc"` + // PrimaryTopic is the primary topic of the event. Topics[0] + PrimaryTopic sql.NullString `gorm:"primary_topic"` + // TopicA is the first topic. Topics[1] + TopicA sql.NullString `gorm:"topic_a"` + // TopicB is the second topic. Topics[2] + TopicB sql.NullString `gorm:"topic_b"` + // TopicC is the third topic. Topics[3] + TopicC sql.NullString `gorm:"topic_c"` + // Data is the data provided by the contract + Data []byte `gorm:"data"` + // BlockNumber is the block in which the transaction was included + BlockNumber uint64 `gorm:"column:block_number;index:idx_head_block_number,priority:1,sort:desc"` + // TxHash is the hash of the transaction + TxHash string `gorm:"column:tx_hash;primaryKey;index:idx_head_tx_hash,priority:1,sort:desc"` + // TxIndex is the index of the transaction in the block + TxIndex uint64 `gorm:"tx_index"` + // BlockHash is the hash of the block in which the transaction was included + BlockHash string `gorm:"column:block_hash;index:idx_head_block_hash,priority:1,sort:desc"` + // Index is the index of the log in the block + BlockIndex uint64 `gorm:"column:block_index;primaryKey;index:idx_head_block_number,priority:2,sort:desc"` + // Removed is true if this log was reverted due to a chain re-organization + Removed bool `gorm:"removed"` + // Confirmed is true if this log has been confirmed by the chain + Confirmed bool `gorm:"confirmed"` + // InsertTime is the time at which this log was inserted + InsertTime uint64 `gorm:"column:insert_time"` +} + +// ReceiptAtHead stores the receipt of a transaction at the tip. +type ReceiptAtHead struct { + // ChainID is the chain id of the receipt + ChainID uint32 `gorm:"column:chain_id;primaryKey"` + // Type is the type + Type uint8 `gorm:"column:receipt_type"` + // PostState is the post state + PostState []byte `gorm:"column:post_state"` + // Status is the status of the transaction + Status uint64 `gorm:"column:status"` + // CumulativeGasUsed is the total amount of gas used when this transaction was executed in the block + CumulativeGasUsed uint64 `gorm:"column:cumulative_gas_used"` + // Bloom is the bloom filter + Bloom []byte `gorm:"column:bloom"` + // TxHash is the hash of the transaction + TxHash string `gorm:"column:tx_hash;primaryKey"` + // ContractAddress is the address of the contract + ContractAddress string `gorm:"column:contract_address"` + // GasUsed is the amount of gas used by this transaction alone + GasUsed uint64 `gorm:"column:gas_used"` + // BlockHash is the hash of the block in which this transaction was included + BlockHash string `gorm:"column:block_hash"` + // BlockNumber is the block in which this transaction was included + BlockNumber uint64 `gorm:"column:block_number;index:idx_head_block_number_receipt,priority:1,sort:desc"` + // TransactionIndex is the index of the transaction in the block + TransactionIndex uint64 `gorm:"column:transaction_index;index:idx_head_block_number_receipt,priority:2,sort:desc"` + // Confirmed is true if this log has been confirmed by the chain + Confirmed bool `gorm:"column:confirmed"` + // InsertTime is the time at which this receipt was inserted + InsertTime uint64 `gorm:"column:insert_time"` +} + +// EthTxAtHead contains a processed ethereum transaction at the tip of the chain. +type EthTxAtHead struct { + // TxHash is the hash of the transaction + TxHash string `gorm:"column:tx_hash;primaryKey"` + // ChainID is the chain id of the transaction + ChainID uint32 `gorm:"column:chain_id;primaryKey"` + // BlockHash is the hash of the block in which the transaction was included + BlockHash string `gorm:"column:block_hash;index:idx_head_tx_block_hash,priority:1,sort:desc"` + // BlockNumber is the block in which the transaction was included + BlockNumber uint64 `gorm:"column:block_number;index:idx_head_block_number_tx,priority:1,sort:desc"` + // RawTx is the raw serialized transaction + RawTx []byte `gorm:"column:raw_tx"` + // GasFeeCap contains the gas fee cap stored in wei + GasFeeCap uint64 + // GasTipCap contains the gas tip cap stored in wei + GasTipCap uint64 + // Confirmed is true if this log has been confirmed by the chain + Confirmed bool `gorm:"column:confirmed"` + // TransactionIndex is the index of the transaction in the block + TransactionIndex uint64 `gorm:"column:transaction_index;index:idx_head_block_number_tx,priority:2,sort:desc"` + // InsertTime is the time at which this tx was inserted + InsertTime uint64 `gorm:"column:insert_time"` +} diff --git a/services/scribe/db/datastore/sql/base/receipt.go b/services/scribe/db/datastore/sql/base/receipt.go index e099906dad..3bfdc21f1b 100644 --- a/services/scribe/db/datastore/sql/base/receipt.go +++ b/services/scribe/db/datastore/sql/base/receipt.go @@ -5,9 +5,8 @@ import ( "errors" "fmt" "github.com/synapsecns/sanguine/core/dbcommon" - "math/big" - "github.com/synapsecns/sanguine/services/scribe/db" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -41,7 +40,7 @@ func (s Store) StoreReceipt(ctx context.Context, chainID uint32, receipt types.R BlockHash: receipt.BlockHash.String(), BlockNumber: receipt.BlockNumber.Uint64(), TransactionIndex: uint64(receipt.TransactionIndex), - Confirmed: false, + Confirmed: true, }) if dbTx.Error != nil { @@ -68,22 +67,6 @@ func (s Store) ConfirmReceiptsForBlockHash(ctx context.Context, chainID uint32, return nil } -// ConfirmReceiptsInRange confirms receipts in a range. -func (s Store) ConfirmReceiptsInRange(ctx context.Context, startBlock, endBlock uint64, chainID uint32) error { - rangeQuery := fmt.Sprintf("%s BETWEEN ? AND ?", BlockNumberFieldName) - dbTx := s.DB().WithContext(ctx). - Model(&Receipt{ChainID: chainID}). - Order(BlockNumberFieldName+" desc"). - Where(rangeQuery, startBlock, endBlock). - Update(ConfirmedFieldName, true) - - if dbTx.Error != nil { - return fmt.Errorf("could not confirm receipts: %w", dbTx.Error) - } - - return nil -} - // DeleteReceiptsForBlockHash deletes receipts with a given block hash. func (s Store) DeleteReceiptsForBlockHash(ctx context.Context, chainID uint32, blockHash common.Hash) error { dbTx := s.DB().WithContext(ctx). @@ -149,7 +132,7 @@ func (s Store) RetrieveReceiptsInRange(ctx context.Context, receiptFilter db.Rec if page < 1 { page = 1 } - dbReceipts := []Receipt{} + var dbReceipts []Receipt query := receiptFilterToQuery(receiptFilter) rangeQuery := fmt.Sprintf("%s BETWEEN ? AND ?", BlockNumberFieldName) dbTx := s.DB().WithContext(ctx). @@ -177,14 +160,14 @@ func (s Store) RetrieveReceiptsInRange(ctx context.Context, receiptFilter db.Rec } func (s Store) buildReceiptsFromDBReceipts(ctx context.Context, dbReceipts []Receipt, chainID uint32) ([]types.Receipt, error) { - receipts := []types.Receipt{} + var receipts []types.Receipt for i := range dbReceipts { dbReceipt := dbReceipts[i] // Retrieve Logs that match the receipt's tx hash in order to add them to the Receipt. logFilter := db.BuildLogFilter(nil, nil, &dbReceipt.TxHash, nil, nil, nil, nil) logFilter.ChainID = chainID - logs := []*types.Log{} + var logs []*types.Log page := 1 for { logGroup, err := s.RetrieveLogsWithFilter(ctx, logFilter, page) diff --git a/services/scribe/db/datastore/sql/base/transaction.go b/services/scribe/db/datastore/sql/base/transaction.go index bb0180bae6..7faae792b8 100644 --- a/services/scribe/db/datastore/sql/base/transaction.go +++ b/services/scribe/db/datastore/sql/base/transaction.go @@ -5,15 +5,14 @@ import ( "errors" "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/synapsecns/sanguine/core/dbcommon" - "gorm.io/gorm/clause" - "github.com/ethereum/go-ethereum/core/types" + "github.com/synapsecns/sanguine/core/dbcommon" "github.com/synapsecns/sanguine/services/scribe/db" "gorm.io/gorm" + "gorm.io/gorm/clause" ) -// StoreEthTx stores a processed text. +// StoreEthTx stores a processed tx. func (s Store) StoreEthTx(ctx context.Context, tx *types.Transaction, chainID uint32, blockHash common.Hash, blockNumber uint64, transactionIndex uint64) error { marshalledTx, err := tx.MarshalBinary() if err != nil { @@ -39,7 +38,7 @@ func (s Store) StoreEthTx(ctx context.Context, tx *types.Transaction, chainID ui RawTx: marshalledTx, GasFeeCap: tx.GasFeeCap().Uint64(), GasTipCap: tx.GasTipCap().Uint64(), - Confirmed: false, + Confirmed: true, TransactionIndex: transactionIndex, }) @@ -67,23 +66,6 @@ func (s Store) ConfirmEthTxsForBlockHash(ctx context.Context, blockHash common.H return nil } -// ConfirmEthTxsInRange confirms eth txs in a range. -func (s Store) ConfirmEthTxsInRange(ctx context.Context, startBlock, endBlock uint64, chainID uint32) error { - rangeQuery := fmt.Sprintf("%s BETWEEN ? AND ?", BlockNumberFieldName) - dbTx := s.DB().WithContext(ctx). - Model(&EthTx{}). - Where(&EthTx{ChainID: chainID}). - Order(BlockNumberFieldName+" desc"). - Where(rangeQuery, startBlock, endBlock). - Update(ConfirmedFieldName, true) - - if dbTx.Error != nil { - return fmt.Errorf("could not confirm eth txs: %w", dbTx.Error) - } - - return nil -} - // DeleteEthTxsForBlockHash deletes eth txs with a given block hash. func (s Store) DeleteEthTxsForBlockHash(ctx context.Context, blockHash common.Hash, chainID uint32) error { dbTx := s.DB().WithContext(ctx). diff --git a/services/scribe/db/datastore/sql/mysql/logger.go b/services/scribe/db/datastore/sql/mysql/logger.go index 27cf5112b3..5c74e0e934 100644 --- a/services/scribe/db/datastore/sql/mysql/logger.go +++ b/services/scribe/db/datastore/sql/mysql/logger.go @@ -5,4 +5,4 @@ import ( ) // Logger is the mysql logger. -var logger = log.Logger("synapse-mysql") +var logger = log.Logger("scribe-mysql") diff --git a/services/scribe/db/datastore/sql/mysql/store.go b/services/scribe/db/datastore/sql/mysql/store.go index a9e5c59619..824492812c 100644 --- a/services/scribe/db/datastore/sql/mysql/store.go +++ b/services/scribe/db/datastore/sql/mysql/store.go @@ -4,7 +4,9 @@ import ( "context" "fmt" "github.com/synapsecns/sanguine/core/metrics" + scribeLogger "github.com/synapsecns/sanguine/services/scribe/logger" gormLogger "gorm.io/gorm/logger" + "time" "github.com/synapsecns/sanguine/services/scribe/db/datastore/sql/base" @@ -32,7 +34,7 @@ var NamingStrategy = schema.NamingStrategy{ // NewMysqlStore creates a new mysql store for a given data store. func NewMysqlStore(parentCtx context.Context, dbURL string, handler metrics.Handler, skipMigrations bool) (_ *Store, err error) { logger.Debug("creating mysql store") - + scribeLogger.ReportScribeState(0, 0, nil, scribeLogger.CreatingSQLStore) ctx, span := handler.Tracer().Start(parentCtx, "start-mysql") defer func() { metrics.EndSpanWithErr(span, err) diff --git a/services/scribe/db/datastore/sql/sqlite/logger.go b/services/scribe/db/datastore/sql/sqlite/logger.go index f66759928c..65f7fc8f3b 100644 --- a/services/scribe/db/datastore/sql/sqlite/logger.go +++ b/services/scribe/db/datastore/sql/sqlite/logger.go @@ -5,4 +5,4 @@ import ( ) // Logger is the mysql logger. -var logger = log.Logger("synapse-sqlite") +var logger = log.Logger("scribe-sqlite") diff --git a/services/scribe/db/datastore/sql/sqlite/store.go b/services/scribe/db/datastore/sql/sqlite/store.go index d956e8ae04..db9ea6ec79 100644 --- a/services/scribe/db/datastore/sql/sqlite/store.go +++ b/services/scribe/db/datastore/sql/sqlite/store.go @@ -3,6 +3,7 @@ package sqlite import ( "context" "fmt" + scribeLogger "github.com/synapsecns/sanguine/services/scribe/logger" gormLogger "gorm.io/gorm/logger" "github.com/synapsecns/sanguine/core/metrics" @@ -21,6 +22,7 @@ type Store struct { // NewSqliteStore creates a new sqlite data store. func NewSqliteStore(parentCtx context.Context, dbPath string, handler metrics.Handler, skipMigrations bool) (_ *Store, err error) { logger.Debugf("creating sqlite store at %s", dbPath) + scribeLogger.ReportScribeState(0, 0, nil, scribeLogger.CreatingSQLStore) ctx, span := handler.Tracer().Start(parentCtx, "start-sqlite") defer func() { diff --git a/services/scribe/db/event.go b/services/scribe/db/event.go index b828f145b7..632c8c014f 100644 --- a/services/scribe/db/event.go +++ b/services/scribe/db/event.go @@ -12,33 +12,33 @@ import ( type EventDBWriter interface { // StoreLogs stores a log StoreLogs(ctx context.Context, chainID uint32, log ...types.Log) error + // StoreLogsAtHead stores a log at the tip. + StoreLogsAtHead(ctx context.Context, chainID uint32, log ...types.Log) error // ConfirmLogsForBlockHash confirms logs for a given block hash. ConfirmLogsForBlockHash(ctx context.Context, chainID uint32, blockHash common.Hash) error - // ConfirmLogsInRange confirms logs in a range. - ConfirmLogsInRange(ctx context.Context, startBlock, endBlock uint64, chainID uint32) error // DeleteLogsForBlockHash deletes logs with a given block hash. DeleteLogsForBlockHash(ctx context.Context, blockHash common.Hash, chainID uint32) error // StoreReceipt stores a receipt StoreReceipt(ctx context.Context, chainID uint32, receipt types.Receipt) error - // ConfirmReceiptsForBlockHash confirms receipts for a given block hash. - ConfirmReceiptsForBlockHash(ctx context.Context, chainID uint32, blockHash common.Hash) error - // ConfirmReceiptsInRange confirms receipts in a range. - ConfirmReceiptsInRange(ctx context.Context, startBlock, endBlock uint64, chainID uint32) error + // StoreReceiptAtHead stores a receipt to the tip + StoreReceiptAtHead(ctx context.Context, chainID uint32, receipt types.Receipt) error // DeleteReceiptsForBlockHash deletes receipts with a given block hash. DeleteReceiptsForBlockHash(ctx context.Context, chainID uint32, blockHash common.Hash) error // StoreEthTx stores a processed transaction StoreEthTx(ctx context.Context, tx *types.Transaction, chainID uint32, blockHash common.Hash, blockNumber uint64, transactionIndex uint64) error + // StoreEthTxAtHead stores a processed transaction at the tip. + StoreEthTxAtHead(ctx context.Context, tx *types.Transaction, chainID uint32, blockHash common.Hash, blockNumber uint64, transactionIndex uint64) error // ConfirmEthTxsForBlockHash confirms eth txs for a given block hash. ConfirmEthTxsForBlockHash(ctx context.Context, blockHash common.Hash, chainID uint32) error - // ConfirmEthTxsInRange confirms eth txs in a range. - ConfirmEthTxsInRange(ctx context.Context, startBlock, endBlock uint64, chainID uint32) error // DeleteEthTxsForBlockHash deletes eth txs with a given block hash. DeleteEthTxsForBlockHash(ctx context.Context, blockHash common.Hash, chainID uint32) error // StoreLastIndexed stores the last indexed for a contract address - StoreLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64) error + StoreLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64, livefillAtHead bool) error + // StoreLastIndexedMultiple stores the last indexed block numbers for numerous contracts. + StoreLastIndexedMultiple(ctx context.Context, contractAddresses []common.Address, chainID uint32, blockNumber uint64) error // StoreLastConfirmedBlock stores the last block number that has been confirmed. // It updates the value if there is a previous last block confirmed value, and creates a new @@ -71,8 +71,10 @@ type EventDBReader interface { RetrieveEthTxsInRange(ctx context.Context, ethTxFilter EthTxFilter, startBlock, endBlock uint64, page int) ([]TxWithBlockNumber, error) // RetrieveLastIndexed retrieves the last indexed for a contract address - RetrieveLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32) (uint64, error) + RetrieveLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, livefillAtHead bool) (uint64, error) + // RetrieveLastIndexedMultiple retrieves the last indexed block numbers for numerous contracts. + RetrieveLastIndexedMultiple(ctx context.Context, contractAddresses []common.Address, chainID uint32) (map[common.Address]uint64, error) // RetrieveLastConfirmedBlock retrieves the last block number that has been confirmed. RetrieveLastConfirmedBlock(ctx context.Context, chainID uint32) (uint64, error) @@ -90,6 +92,16 @@ type EventDBReader interface { RetrieveBlockTimesCountForChain(ctx context.Context, chainID uint32) (int64, error) // RetrieveReceiptsWithStaleBlockHash gets receipts that are from a reorged/stale block. RetrieveReceiptsWithStaleBlockHash(ctx context.Context, chainID uint32, blockHashes []string, startBlock uint64, endBlock uint64) ([]types.Receipt, error) + + // RetrieveLogsFromHeadRangeQuery gets unconfirmed logs from the head in a range. + RetrieveLogsFromHeadRangeQuery(ctx context.Context, logFilter LogFilter, startBlock uint64, endBlock uint64, page int) (logs []*types.Log, err error) + // RetrieveReceiptsFromHeadRangeQuery gets unconfirmed receipts from the head in a range. + RetrieveReceiptsFromHeadRangeQuery(ctx context.Context, receiptFilter ReceiptFilter, startBlock uint64, endBlock uint64, page int) ([]types.Receipt, error) + // RetrieveUnconfirmedEthTxsFromHeadRangeQuery retrieves all unconfirmed ethTx for a given chain ID and range. + RetrieveUnconfirmedEthTxsFromHeadRangeQuery(ctx context.Context, receiptFilter EthTxFilter, startBlock uint64, endBlock uint64, lastIndexed uint64, page int) ([]TxWithBlockNumber, error) + + // FlushFromHeadTables flushes unconfirmed logs, receipts, and txs from the head. + FlushFromHeadTables(ctx context.Context, time int64) error } // EventDB stores events. diff --git a/services/scribe/db/lastindexed_test.go b/services/scribe/db/lastindexed_test.go index 9b47c86f2f..abc086aa92 100644 --- a/services/scribe/db/lastindexed_test.go +++ b/services/scribe/db/lastindexed_test.go @@ -2,6 +2,7 @@ package db_test import ( "github.com/synapsecns/sanguine/services/scribe/db" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" "math/big" "github.com/brianvoe/gofakeit/v6" @@ -17,35 +18,59 @@ func (t *DBSuite) TestStoreRetrieveLastIndexed() { lastIndexed := gofakeit.Uint64() // Before storing, ensure that the last indexed block is 0. - retrievedLastIndexed, err := testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID) + retrievedLastIndexed, err := testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID, scribeTypes.IndexingConfirmed) Nil(t.T(), err) Equal(t.T(), retrievedLastIndexed, uint64(0)) // Store a new contract address and last indexed. - err = testDB.StoreLastIndexed(t.GetTestContext(), addressA, chainID, lastIndexed) + err = testDB.StoreLastIndexed(t.GetTestContext(), addressA, chainID, lastIndexed, scribeTypes.IndexingConfirmed) Nil(t.T(), err) // Ensure the last indexed for the contract address matches the one stored. - retrievedLastIndexed, err = testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID) + retrievedLastIndexed, err = testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID, scribeTypes.IndexingConfirmed) Nil(t.T(), err) Equal(t.T(), retrievedLastIndexed, lastIndexed) // Update addressA's last indexed to a new value. - err = testDB.StoreLastIndexed(t.GetTestContext(), addressA, chainID, lastIndexed+1) + err = testDB.StoreLastIndexed(t.GetTestContext(), addressA, chainID, lastIndexed+1, scribeTypes.IndexingConfirmed) Nil(t.T(), err) // Ensure the last indexed for the contract address matches the one stored. - retrievedLastIndexed, err = testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID) + retrievedLastIndexed, err = testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID, scribeTypes.IndexingConfirmed) Nil(t.T(), err) Equal(t.T(), retrievedLastIndexed, lastIndexed+1) // Store a second contract address and last indexed. - err = testDB.StoreLastIndexed(t.GetTestContext(), addressB, chainID+1, lastIndexed) + err = testDB.StoreLastIndexed(t.GetTestContext(), addressB, chainID+1, lastIndexed, scribeTypes.IndexingConfirmed) Nil(t.T(), err) // Ensure the last indexed for the contract address matches the one stored. - retrievedLastIndexed, err = testDB.RetrieveLastIndexed(t.GetTestContext(), addressB, chainID+1) + retrievedLastIndexed, err = testDB.RetrieveLastIndexed(t.GetTestContext(), addressB, chainID+1, scribeTypes.IndexingConfirmed) Nil(t.T(), err) Equal(t.T(), retrievedLastIndexed, lastIndexed) }) } + +func (t *DBSuite) TestStoreRetrieveLastIndexedMultiple() { + t.RunOnAllDBs(func(testDB db.EventDB) { + addressA := common.BigToAddress(big.NewInt(gofakeit.Int64())) + addressB := common.BigToAddress(big.NewInt(gofakeit.Int64())) + chainID := gofakeit.Uint32() + lastIndexed := gofakeit.Uint64() + + // Before storing, ensure that the last indexed block is 0. + retrievedLastIndexed, err := testDB.RetrieveLastIndexed(t.GetTestContext(), addressA, chainID, scribeTypes.IndexingConfirmed) + Nil(t.T(), err) + Equal(t.T(), uint64(0), retrievedLastIndexed) + + // Store a new contract address and last indexed. + err = testDB.StoreLastIndexedMultiple(t.GetTestContext(), []common.Address{addressA, addressB}, chainID, lastIndexed) + Nil(t.T(), err) + + // Ensure the last indexed for the contract address matches the one stored. + retrievedLastIndexedMap, err := testDB.RetrieveLastIndexedMultiple(t.GetTestContext(), []common.Address{addressA, addressB}, chainID) + Nil(t.T(), err) + Equal(t.T(), lastIndexed, retrievedLastIndexedMap[addressA]) + Equal(t.T(), lastIndexed, retrievedLastIndexedMap[addressB]) + }) +} diff --git a/services/scribe/db/log_test.go b/services/scribe/db/log_test.go index e63ed9f936..266e775c4f 100644 --- a/services/scribe/db/log_test.go +++ b/services/scribe/db/log_test.go @@ -26,12 +26,14 @@ func (t *DBSuite) TestStoreRetrieveLog() { logB := t.MakeRandomLog(txHashA) logB.BlockNumber = 2 err = testDB.StoreLogs(t.GetTestContext(), chainID, logB) + Nil(t.T(), err) txHashC := common.BigToHash(big.NewInt(txHashRandom + 1)) logC := t.MakeRandomLog(txHashC) logC.BlockNumber = 1 err = testDB.StoreLogs(t.GetTestContext(), chainID+1, logC) + Nil(t.T(), err) // Ensure the logs from the database match the ones stored. @@ -71,37 +73,6 @@ func (t *DBSuite) TestStoreRetrieveLog() { }) } -func (t *DBSuite) TestConfirmLogsInRange() { - t.RunOnAllDBs(func(testDB db.EventDB) { - chainID := gofakeit.Uint32() - - mostRecentBlock := 4 - // Store five logs. - for i := mostRecentBlock; i >= 0; i-- { - txHash := common.BigToHash(big.NewInt(gofakeit.Int64())) - log := t.MakeRandomLog(txHash) - log.BlockNumber = uint64(i) - err := testDB.StoreLogs(t.GetTestContext(), chainID, log) - Nil(t.T(), err) - } - - // Confirm the first two logs. - err := testDB.ConfirmLogsInRange(t.GetTestContext(), 0, 1, chainID) - Nil(t.T(), err) - - // Ensure the first two logs are confirmed. - logFilter := db.LogFilter{ - ChainID: chainID, - Confirmed: true, - } - retrievedLogs, err := testDB.RetrieveLogsWithFilter(t.GetTestContext(), logFilter, 1) - Nil(t.T(), err) - Equal(t.T(), 2, len(retrievedLogs)) - Equal(t.T(), uint64(1), retrievedLogs[0].BlockNumber) - Equal(t.T(), uint64(0), retrievedLogs[1].BlockNumber) - }) -} - func (t *DBSuite) TestDeleteLogsForBlockHash() { t.RunOnAllDBs(func(testDB db.EventDB) { chainID := gofakeit.Uint32() diff --git a/services/scribe/db/mocks/event_db.go b/services/scribe/db/mocks/event_db.go index 80bf577e5a..6404bd40b8 100644 --- a/services/scribe/db/mocks/event_db.go +++ b/services/scribe/db/mocks/event_db.go @@ -33,20 +33,6 @@ func (_m *EventDB) ConfirmEthTxsForBlockHash(ctx context.Context, blockHash comm return r0 } -// ConfirmEthTxsInRange provides a mock function with given fields: ctx, startBlock, endBlock, chainID -func (_m *EventDB) ConfirmEthTxsInRange(ctx context.Context, startBlock uint64, endBlock uint64, chainID uint32) error { - ret := _m.Called(ctx, startBlock, endBlock, chainID) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, uint32) error); ok { - r0 = rf(ctx, startBlock, endBlock, chainID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // ConfirmLogsForBlockHash provides a mock function with given fields: ctx, chainID, blockHash func (_m *EventDB) ConfirmLogsForBlockHash(ctx context.Context, chainID uint32, blockHash common.Hash) error { ret := _m.Called(ctx, chainID, blockHash) @@ -61,48 +47,6 @@ func (_m *EventDB) ConfirmLogsForBlockHash(ctx context.Context, chainID uint32, return r0 } -// ConfirmLogsInRange provides a mock function with given fields: ctx, startBlock, endBlock, chainID -func (_m *EventDB) ConfirmLogsInRange(ctx context.Context, startBlock uint64, endBlock uint64, chainID uint32) error { - ret := _m.Called(ctx, startBlock, endBlock, chainID) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, uint32) error); ok { - r0 = rf(ctx, startBlock, endBlock, chainID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ConfirmReceiptsForBlockHash provides a mock function with given fields: ctx, chainID, blockHash -func (_m *EventDB) ConfirmReceiptsForBlockHash(ctx context.Context, chainID uint32, blockHash common.Hash) error { - ret := _m.Called(ctx, chainID, blockHash) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint32, common.Hash) error); ok { - r0 = rf(ctx, chainID, blockHash) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ConfirmReceiptsInRange provides a mock function with given fields: ctx, startBlock, endBlock, chainID -func (_m *EventDB) ConfirmReceiptsInRange(ctx context.Context, startBlock uint64, endBlock uint64, chainID uint32) error { - ret := _m.Called(ctx, startBlock, endBlock, chainID) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, uint32) error); ok { - r0 = rf(ctx, startBlock, endBlock, chainID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // DeleteEthTxsForBlockHash provides a mock function with given fields: ctx, blockHash, chainID func (_m *EventDB) DeleteEthTxsForBlockHash(ctx context.Context, blockHash common.Hash, chainID uint32) error { ret := _m.Called(ctx, blockHash, chainID) @@ -145,6 +89,20 @@ func (_m *EventDB) DeleteReceiptsForBlockHash(ctx context.Context, chainID uint3 return r0 } +// FlushFromHeadTables provides a mock function with given fields: ctx, time +func (_m *EventDB) FlushFromHeadTables(ctx context.Context, time int64) error { + ret := _m.Called(ctx, time) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { + r0 = rf(ctx, time) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // RetrieveBlockTime provides a mock function with given fields: ctx, chainID, blockNumber func (_m *EventDB) RetrieveBlockTime(ctx context.Context, chainID uint32, blockNumber uint64) (uint64, error) { ret := _m.Called(ctx, chainID, blockNumber) @@ -296,20 +254,43 @@ func (_m *EventDB) RetrieveLastConfirmedBlock(ctx context.Context, chainID uint3 return r0, r1 } -// RetrieveLastIndexed provides a mock function with given fields: ctx, contractAddress, chainID -func (_m *EventDB) RetrieveLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32) (uint64, error) { - ret := _m.Called(ctx, contractAddress, chainID) +// RetrieveLastIndexed provides a mock function with given fields: ctx, contractAddress, chainID, livefillAtHead +func (_m *EventDB) RetrieveLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, livefillAtHead bool) (uint64, error) { + ret := _m.Called(ctx, contractAddress, chainID, livefillAtHead) var r0 uint64 - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint32) uint64); ok { - r0 = rf(ctx, contractAddress, chainID) + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint32, bool) uint64); ok { + r0 = rf(ctx, contractAddress, chainID, livefillAtHead) } else { r0 = ret.Get(0).(uint64) } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint32) error); ok { - r1 = rf(ctx, contractAddress, chainID) + if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint32, bool) error); ok { + r1 = rf(ctx, contractAddress, chainID, livefillAtHead) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RetrieveLastIndexedMultiple provides a mock function with given fields: ctx, contractAddresses, chainID +func (_m *EventDB) RetrieveLastIndexedMultiple(ctx context.Context, contractAddresses []common.Address, chainID uint32) (map[common.Address]uint64, error) { + ret := _m.Called(ctx, contractAddresses, chainID) + + var r0 map[common.Address]uint64 + if rf, ok := ret.Get(0).(func(context.Context, []common.Address, uint32) map[common.Address]uint64); ok { + r0 = rf(ctx, contractAddresses, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[common.Address]uint64) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []common.Address, uint32) error); ok { + r1 = rf(ctx, contractAddresses, chainID) } else { r1 = ret.Error(1) } @@ -338,6 +319,29 @@ func (_m *EventDB) RetrieveLogCountForContract(ctx context.Context, contractAddr return r0, r1 } +// RetrieveLogsFromHeadRangeQuery provides a mock function with given fields: ctx, logFilter, startBlock, endBlock, page +func (_m *EventDB) RetrieveLogsFromHeadRangeQuery(ctx context.Context, logFilter db.LogFilter, startBlock uint64, endBlock uint64, page int) ([]*types.Log, error) { + ret := _m.Called(ctx, logFilter, startBlock, endBlock, page) + + var r0 []*types.Log + if rf, ok := ret.Get(0).(func(context.Context, db.LogFilter, uint64, uint64, int) []*types.Log); ok { + r0 = rf(ctx, logFilter, startBlock, endBlock, page) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Log) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, db.LogFilter, uint64, uint64, int) error); ok { + r1 = rf(ctx, logFilter, startBlock, endBlock, page) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RetrieveLogsInRange provides a mock function with given fields: ctx, logFilter, startBlock, endBlock, page func (_m *EventDB) RetrieveLogsInRange(ctx context.Context, logFilter db.LogFilter, startBlock uint64, endBlock uint64, page int) ([]*types.Log, error) { ret := _m.Called(ctx, logFilter, startBlock, endBlock, page) @@ -428,6 +432,29 @@ func (_m *EventDB) RetrieveReceiptCountForChain(ctx context.Context, chainID uin return r0, r1 } +// RetrieveReceiptsFromHeadRangeQuery provides a mock function with given fields: ctx, receiptFilter, startBlock, endBlock, page +func (_m *EventDB) RetrieveReceiptsFromHeadRangeQuery(ctx context.Context, receiptFilter db.ReceiptFilter, startBlock uint64, endBlock uint64, page int) ([]types.Receipt, error) { + ret := _m.Called(ctx, receiptFilter, startBlock, endBlock, page) + + var r0 []types.Receipt + if rf, ok := ret.Get(0).(func(context.Context, db.ReceiptFilter, uint64, uint64, int) []types.Receipt); ok { + r0 = rf(ctx, receiptFilter, startBlock, endBlock, page) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Receipt) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, db.ReceiptFilter, uint64, uint64, int) error); ok { + r1 = rf(ctx, receiptFilter, startBlock, endBlock, page) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RetrieveReceiptsInRange provides a mock function with given fields: ctx, receiptFilter, startBlock, endBlock, page func (_m *EventDB) RetrieveReceiptsInRange(ctx context.Context, receiptFilter db.ReceiptFilter, startBlock uint64, endBlock uint64, page int) ([]types.Receipt, error) { ret := _m.Called(ctx, receiptFilter, startBlock, endBlock, page) @@ -497,6 +524,29 @@ func (_m *EventDB) RetrieveReceiptsWithStaleBlockHash(ctx context.Context, chain return r0, r1 } +// RetrieveUnconfirmedEthTxsFromHeadRangeQuery provides a mock function with given fields: ctx, receiptFilter, startBlock, endBlock, lastIndexed, page +func (_m *EventDB) RetrieveUnconfirmedEthTxsFromHeadRangeQuery(ctx context.Context, receiptFilter db.EthTxFilter, startBlock uint64, endBlock uint64, lastIndexed uint64, page int) ([]db.TxWithBlockNumber, error) { + ret := _m.Called(ctx, receiptFilter, startBlock, endBlock, lastIndexed, page) + + var r0 []db.TxWithBlockNumber + if rf, ok := ret.Get(0).(func(context.Context, db.EthTxFilter, uint64, uint64, uint64, int) []db.TxWithBlockNumber); ok { + r0 = rf(ctx, receiptFilter, startBlock, endBlock, lastIndexed, page) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]db.TxWithBlockNumber) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, db.EthTxFilter, uint64, uint64, uint64, int) error); ok { + r1 = rf(ctx, receiptFilter, startBlock, endBlock, lastIndexed, page) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // StoreBlockTime provides a mock function with given fields: ctx, chainID, blockNumber, timestamp func (_m *EventDB) StoreBlockTime(ctx context.Context, chainID uint32, blockNumber uint64, timestamp uint64) error { ret := _m.Called(ctx, chainID, blockNumber, timestamp) @@ -525,6 +575,20 @@ func (_m *EventDB) StoreEthTx(ctx context.Context, tx *types.Transaction, chainI return r0 } +// StoreEthTxAtHead provides a mock function with given fields: ctx, tx, chainID, blockHash, blockNumber, transactionIndex +func (_m *EventDB) StoreEthTxAtHead(ctx context.Context, tx *types.Transaction, chainID uint32, blockHash common.Hash, blockNumber uint64, transactionIndex uint64) error { + ret := _m.Called(ctx, tx, chainID, blockHash, blockNumber, transactionIndex) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, uint32, common.Hash, uint64, uint64) error); ok { + r0 = rf(ctx, tx, chainID, blockHash, blockNumber, transactionIndex) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // StoreLastConfirmedBlock provides a mock function with given fields: ctx, chainID, blockNumber func (_m *EventDB) StoreLastConfirmedBlock(ctx context.Context, chainID uint32, blockNumber uint64) error { ret := _m.Called(ctx, chainID, blockNumber) @@ -539,13 +603,27 @@ func (_m *EventDB) StoreLastConfirmedBlock(ctx context.Context, chainID uint32, return r0 } -// StoreLastIndexed provides a mock function with given fields: ctx, contractAddress, chainID, blockNumber -func (_m *EventDB) StoreLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64) error { - ret := _m.Called(ctx, contractAddress, chainID, blockNumber) +// StoreLastIndexed provides a mock function with given fields: ctx, contractAddress, chainID, blockNumber, livefillAtHead +func (_m *EventDB) StoreLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64, livefillAtHead bool) error { + ret := _m.Called(ctx, contractAddress, chainID, blockNumber, livefillAtHead) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint32, uint64, bool) error); ok { + r0 = rf(ctx, contractAddress, chainID, blockNumber, livefillAtHead) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// StoreLastIndexedMultiple provides a mock function with given fields: ctx, contractAddresses, chainID, blockNumber +func (_m *EventDB) StoreLastIndexedMultiple(ctx context.Context, contractAddresses []common.Address, chainID uint32, blockNumber uint64) error { + ret := _m.Called(ctx, contractAddresses, chainID, blockNumber) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint32, uint64) error); ok { - r0 = rf(ctx, contractAddress, chainID, blockNumber) + if rf, ok := ret.Get(0).(func(context.Context, []common.Address, uint32, uint64) error); ok { + r0 = rf(ctx, contractAddresses, chainID, blockNumber) } else { r0 = ret.Error(0) } @@ -574,6 +652,27 @@ func (_m *EventDB) StoreLogs(ctx context.Context, chainID uint32, log ...types.L return r0 } +// StoreLogsAtHead provides a mock function with given fields: ctx, chainID, log +func (_m *EventDB) StoreLogsAtHead(ctx context.Context, chainID uint32, log ...types.Log) error { + _va := make([]interface{}, len(log)) + for _i := range log { + _va[_i] = log[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, chainID) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint32, ...types.Log) error); ok { + r0 = rf(ctx, chainID, log...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // StoreReceipt provides a mock function with given fields: ctx, chainID, receipt func (_m *EventDB) StoreReceipt(ctx context.Context, chainID uint32, receipt types.Receipt) error { ret := _m.Called(ctx, chainID, receipt) @@ -588,6 +687,20 @@ func (_m *EventDB) StoreReceipt(ctx context.Context, chainID uint32, receipt typ return r0 } +// StoreReceiptAtHead provides a mock function with given fields: ctx, chainID, receipt +func (_m *EventDB) StoreReceiptAtHead(ctx context.Context, chainID uint32, receipt types.Receipt) error { + ret := _m.Called(ctx, chainID, receipt) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint32, types.Receipt) error); ok { + r0 = rf(ctx, chainID, receipt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + type mockConstructorTestingTNewEventDB interface { mock.TestingT Cleanup(func()) diff --git a/services/scribe/db/receipt_test.go b/services/scribe/db/receipt_test.go index 849f89b599..ef43e3693d 100644 --- a/services/scribe/db/receipt_test.go +++ b/services/scribe/db/receipt_test.go @@ -1,7 +1,6 @@ package db_test import ( - "fmt" "math/big" "github.com/brianvoe/gofakeit/v6" @@ -113,35 +112,6 @@ func (t *DBSuite) TestStoreRetrieveReceipt() { }) } -func (t *DBSuite) TestConfirmReceiptsInRange() { - t.RunOnAllDBs(func(testDB db.EventDB) { - chainID := gofakeit.Uint32() - - // Store five receipts. - for i := 4; i >= 0; i-- { - receipt := t.MakeRandomReceipt(common.BigToHash(big.NewInt(gofakeit.Int64()))) - receipt.BlockNumber = big.NewInt(int64(i)) - err := testDB.StoreReceipt(t.GetTestContext(), chainID, receipt) - Nil(t.T(), err) - } - - // Confirm the first two receipts. - err := testDB.ConfirmReceiptsInRange(t.GetTestContext(), 0, 1, chainID) - Nil(t.T(), err) - - // Ensure the first two receipts are confirmed. - receiptFilter := db.ReceiptFilter{ - ChainID: chainID, - Confirmed: true, - } - retrievedReceipts, err := testDB.RetrieveReceiptsWithFilter(t.GetTestContext(), receiptFilter, 1) - Nil(t.T(), err) - Equal(t.T(), 2, len(retrievedReceipts)) - Equal(t.T(), retrievedReceipts[0].BlockNumber, big.NewInt(1)) - Equal(t.T(), retrievedReceipts[1].BlockNumber, big.NewInt(0)) - }) -} - func (t *DBSuite) TestDeleteReceiptsForBlockHash() { t.RunOnAllDBs(func(testDB db.EventDB) { chainID := gofakeit.Uint32() @@ -202,7 +172,6 @@ func (t *DBSuite) TestRetrieveReceiptsWithStaleBlockHash() { for i := 0; i < 10; i++ { receipt := t.MakeRandomReceipt(common.BigToHash(big.NewInt(gofakeit.Int64()))) receipt.BlockNumber = big.NewInt(int64(i)) - fmt.Println("SSS", i, blockHashes[i%3]) receipt.BlockHash = blockHashes[i%3] err := testDB.StoreReceipt(t.GetTestContext(), chainID, receipt) Nil(t.T(), err) diff --git a/services/scribe/db/transaction_test.go b/services/scribe/db/transaction_test.go index 4e0191592d..b4f02f1ba6 100644 --- a/services/scribe/db/transaction_test.go +++ b/services/scribe/db/transaction_test.go @@ -77,9 +77,13 @@ func (t *DBSuite) TestStoreAndRetrieveEthTx() { signedTx, err := transactor.Signer(signer.Address(), testTx) Nil(t.T(), err) + // Store same tx with different blockhash err = testDB.StoreEthTx(t.GetTestContext(), signedTx, uint32(testTx.ChainId().Uint64()), common.BigToHash(big.NewInt(gofakeit.Int64())), gofakeit.Uint64(), gofakeit.Uint64()) Nil(t.T(), err) + // err = testDB.StoreEthTxAtHead(t.GetTestContext(), signedTx, uint32(testTx.ChainId().Uint64()), common.BigToHash(big.NewInt(gofakeit.Int64())), gofakeit.Uint64(), gofakeit.Uint64()) + // Nil(t.T(), err) + ethTxFilter := db.EthTxFilter{ ChainID: uint32(testTx.ChainId().Uint64()), TxHash: signedTx.Hash().String(), @@ -95,50 +99,6 @@ func (t *DBSuite) TestStoreAndRetrieveEthTx() { }) } -func (t *DBSuite) TestConfirmEthTxsInRange() { - testWallet, err := wallet.FromRandom() - Nil(t.T(), err) - - signer := localsigner.NewSigner(testWallet.PrivateKey()) - - t.RunOnAllDBs(func(testDB db.EventDB) { - chainID := gofakeit.Uint32() - - // Store five txs. - for i := 0; i < 5; i++ { - testTx := types.NewTx(&types.LegacyTx{ - Nonce: uint64(i), - GasPrice: new(big.Int).SetUint64(gofakeit.Uint64()), - Gas: gofakeit.Uint64(), - To: addressPtr(common.BigToAddress(new(big.Int).SetUint64(gofakeit.Uint64()))), - Value: new(big.Int).SetUint64(gofakeit.Uint64()), - Data: []byte(gofakeit.Paragraph(1, 2, 3, " ")), - }) - transactor, err := localsigner.NewSigner(testWallet.PrivateKey()).GetTransactor(t.GetTestContext(), testTx.ChainId()) - Nil(t.T(), err) - - signedTx, err := transactor.Signer(signer.Address(), testTx) - Nil(t.T(), err) - - err = testDB.StoreEthTx(t.GetTestContext(), signedTx, chainID, common.BigToHash(big.NewInt(gofakeit.Int64())), uint64(i), gofakeit.Uint64()) - Nil(t.T(), err) - } - - // Confirm the first two txs. - err = testDB.ConfirmEthTxsInRange(t.GetTestContext(), 0, 1, chainID) - Nil(t.T(), err) - - // Ensure the first two receipts are confirmed. - ethTxFilter := db.EthTxFilter{ - ChainID: chainID, - Confirmed: true, - } - retrievedTxs, err := testDB.RetrieveEthTxsWithFilter(t.GetTestContext(), ethTxFilter, 1) - Nil(t.T(), err) - Equal(t.T(), 2, len(retrievedTxs)) - }) -} - func (t *DBSuite) TestDeleteEthTxsForBlockHash() { testWallet, err := wallet.FromRandom() Nil(t.T(), err) diff --git a/services/scribe/go.mod b/services/scribe/go.mod index d5aa0001e8..90cf6257c7 100644 --- a/services/scribe/go.mod +++ b/services/scribe/go.mod @@ -17,7 +17,7 @@ replace ( require ( bitbucket.org/tentontrain/math v0.0.0-20220519191623-a4e86beba92a - github.com/99designs/gqlgen v0.17.31 + github.com/99designs/gqlgen v0.17.36 github.com/Flaque/filet v0.0.0-20201012163910-45f684403088 github.com/MichaelMure/go-term-markdown v0.1.4 github.com/Yamashou/gqlgenc v0.10.0 @@ -39,6 +39,7 @@ require ( github.com/lmittmann/w3 v0.10.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 + github.com/ravilushqa/otelgqlgen v0.13.1 github.com/richardwilkes/toolbox v1.74.0 github.com/soheilhy/cmux v0.1.5 github.com/stretchr/testify v1.8.4 @@ -47,8 +48,8 @@ require ( github.com/synapsecns/sanguine/services/omnirpc v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/tools v0.0.0-00010101000000-000000000000 github.com/tenderly/tenderly-cli v1.4.6 - github.com/urfave/cli/v2 v2.24.4 - github.com/vektah/gqlparser/v2 v2.5.1 + github.com/urfave/cli/v2 v2.25.5 + github.com/vektah/gqlparser/v2 v2.5.8 github.com/vektra/mockery/v2 v2.14.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 go.opentelemetry.io/otel v1.16.0 @@ -145,8 +146,6 @@ require ( github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.22.3 // 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 @@ -171,7 +170,7 @@ require ( github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hedzr/cmdr v1.10.49 // indirect github.com/hedzr/log v1.6.3 // indirect @@ -190,8 +189,6 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/josephburnett/jd v1.6.1 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect github.com/keep-network/keep-common v1.7.1-0.20211012131917-7102d7b9c6a0 // indirect @@ -204,7 +201,6 @@ require ( github.com/lucasb-eyer/go-colorful v1.0.3 // indirect github.com/lunixbochs/vtclean v1.0.0 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/manifoldco/promptui v0.7.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -259,7 +255,7 @@ require ( github.com/rung/go-safecast v1.0.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.8.1 // indirect @@ -294,9 +290,10 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 // indirect + go.opentelemetry.io/contrib v1.16.1 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect @@ -313,13 +310,13 @@ require ( golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.9.3 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/DataDog/dd-trace-go.v1 v1.52.0 // indirect diff --git a/services/scribe/go.sum b/services/scribe/go.sum index ef238e6652..97e71ee62d 100644 --- a/services/scribe/go.sum +++ b/services/scribe/go.sum @@ -59,8 +59,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= -github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158= -github.com/99designs/gqlgen v0.17.31/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4= +github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go= +github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= @@ -133,7 +133,6 @@ github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNu github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -466,11 +465,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= 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-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -660,8 +656,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= -github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= +github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -751,10 +747,6 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/josephburnett/jd v1.6.1 h1:Uzqhcje4WqvVyp85F3Oj0ezISPTlnhnr/KaLZIy8qh0= -github.com/josephburnett/jd v1.6.1/go.mod h1:R8ZnZnLt2D4rhW4NvBc/USTo6mzyNT6fYNIIWOJA9GY= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -842,8 +834,6 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/manifoldco/promptui v0.3.0/go.mod h1:zoCNXiJnyM03LlBgTsWv8mq28s7aTC71UgKasqRJHww= github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= @@ -1059,6 +1049,8 @@ github.com/pyroscope-io/godeltaprof v0.1.0 h1:UBqtjt0yZi4jTxqZmLAs34XG6ycS3vUTlh github.com/pyroscope-io/godeltaprof v0.1.0/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= github.com/pyroscope-io/otel-profiling-go v0.4.0 h1:Hk/rbUqOWoByoWy1tt4r5BX5xoKAvs5drr0511Ki8ic= github.com/pyroscope-io/otel-profiling-go v0.4.0/go.mod h1:MXaofiWU7PgLP7eISUZJYVO4Z8WYMqpkYgeP4XrPLyg= +github.com/ravilushqa/otelgqlgen v0.13.1 h1:V+zFE75iDd2/CSzy5kKnb+Fi09SsE5535wv9U2nUEFE= +github.com/ravilushqa/otelgqlgen v0.13.1/go.mod h1:ZIyWykK2paCuNi9k8gk5edcNSwDJuxZaW90vZXpafxw= github.com/rbretecher/go-postman-collection v0.9.0 h1:vXw6KBhASpz0L0igH3OsJCx5pjKbWXn9RiYMMnOO4QQ= github.com/rbretecher/go-postman-collection v0.9.0/go.mod h1:pptkyjdB/sqPycH+CCa1zrA6Wpj2Kc8Nz846qRstVVs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -1109,8 +1101,8 @@ github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1224,8 +1216,8 @@ github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2 h1:USRngIQppxeyb39XzkVH github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.2/go.mod h1:1frv9RN1rlTq0jzCq+mVuEQisubZCQ4OU6S/8CaHzGY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= @@ -1237,8 +1229,8 @@ github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002 github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4= -github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= +github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= +github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= github.com/vektra/mockery/v2 v2.14.0 h1:KZ1p5Hrn8tiY+LErRMr14HHle6khxo+JKOXLBW/yfqs= github.com/vektra/mockery/v2 v2.14.0/go.mod h1:bnD1T8tExSgPD1ripLkDbr60JA9VtQeu12P3wgLZd7M= github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k= @@ -1281,14 +1273,16 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0 h1:E4MMXDxufRnIHXhoTNOlNsdkWpC5HdLhfj84WNRKPkc= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.40.0/go.mod h1:A8+gHkpqTfMKxdKWq1pp360nAs096K26CH5Sm2YHDdA= +go.opentelemetry.io/contrib v1.16.1 h1:EpASvVyGx6/ZTlmXzxYfTMZxHROelCeXXa2uLiwltcs= +go.opentelemetry.io/contrib v1.16.1/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= @@ -1428,8 +1422,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -1737,8 +1731,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= diff --git a/services/scribe/graphql/client/client.go b/services/scribe/graphql/client/client.go index 826db8709d..a02ed9a1f3 100644 --- a/services/scribe/graphql/client/client.go +++ b/services/scribe/graphql/client/client.go @@ -34,6 +34,9 @@ type Query struct { LogCount *int "json:\"logCount\" graphql:\"logCount\"" ReceiptCount *int "json:\"receiptCount\" graphql:\"receiptCount\"" BlockTimeCount *int "json:\"blockTimeCount\" graphql:\"blockTimeCount\"" + LogsAtHeadRange []*model.Log "json:\"logsAtHeadRange\" graphql:\"logsAtHeadRange\"" + ReceiptsAtHeadRange []*model.Receipt "json:\"receiptsAtHeadRange\" graphql:\"receiptsAtHeadRange\"" + TransactionsAtHeadRange []*model.Transaction "json:\"transactionsAtHeadRange\" graphql:\"transactionsAtHeadRange\"" } type GetLogs struct { Response []*struct { @@ -63,6 +66,20 @@ type GetLogsRange struct { Removed bool "json:\"removed\" graphql:\"removed\"" } "json:\"response\" graphql:\"response\"" } +type GetLogsAtHeadRange struct { + Response []*struct { + ContractAddress string "json:\"contract_address\" graphql:\"contract_address\"" + ChainID int "json:\"chain_id\" graphql:\"chain_id\"" + Topics []string "json:\"topics\" graphql:\"topics\"" + Data string "json:\"data\" graphql:\"data\"" + BlockNumber int "json:\"block_number\" graphql:\"block_number\"" + TxHash string "json:\"tx_hash\" graphql:\"tx_hash\"" + TxIndex int "json:\"tx_index\" graphql:\"tx_index\"" + BlockHash string "json:\"block_hash\" graphql:\"block_hash\"" + Index int "json:\"index\" graphql:\"index\"" + Removed bool "json:\"removed\" graphql:\"removed\"" + } "json:\"response\" graphql:\"response\"" +} type GetLogsResolvers struct { Response []*struct { Receipt struct { @@ -124,6 +141,21 @@ type GetReceiptsRange struct { TransactionIndex int "json:\"transaction_index\" graphql:\"transaction_index\"" } "json:\"response\" graphql:\"response\"" } +type GetReceiptsAtHeadRange struct { + Response []*struct { + ChainID int "json:\"chain_id\" graphql:\"chain_id\"" + Type int "json:\"type\" graphql:\"type\"" + PostState string "json:\"post_state\" graphql:\"post_state\"" + Status int "json:\"status\" graphql:\"status\"" + CumulativeGasUsed int "json:\"cumulative_gas_used\" graphql:\"cumulative_gas_used\"" + Bloom string "json:\"bloom\" graphql:\"bloom\"" + TxHash string "json:\"tx_hash\" graphql:\"tx_hash\"" + ContractAddress string "json:\"contract_address\" graphql:\"contract_address\"" + GasUsed int "json:\"gas_used\" graphql:\"gas_used\"" + BlockNumber int "json:\"block_number\" graphql:\"block_number\"" + TransactionIndex int "json:\"transaction_index\" graphql:\"transaction_index\"" + } "json:\"response\" graphql:\"response\"" +} type GetReceiptsResolvers struct { Response []*struct { Logs []*struct { @@ -190,6 +222,24 @@ type GetTransactionsRange struct { Sender string "json:\"sender\" graphql:\"sender\"" } "json:\"response\" graphql:\"response\"" } +type GetTransactionsAtHeadRange struct { + Response []*struct { + ChainID int "json:\"chain_id\" graphql:\"chain_id\"" + TxHash string "json:\"tx_hash\" graphql:\"tx_hash\"" + Protected bool "json:\"protected\" graphql:\"protected\"" + Type int "json:\"type\" graphql:\"type\"" + Data string "json:\"data\" graphql:\"data\"" + Gas int "json:\"gas\" graphql:\"gas\"" + GasPrice int "json:\"gas_price\" graphql:\"gas_price\"" + GasTipCap string "json:\"gas_tip_cap\" graphql:\"gas_tip_cap\"" + GasFeeCap string "json:\"gas_fee_cap\" graphql:\"gas_fee_cap\"" + Value string "json:\"value\" graphql:\"value\"" + Nonce int "json:\"nonce\" graphql:\"nonce\"" + To string "json:\"to\" graphql:\"to\"" + Timestamp int "json:\"timestamp\" graphql:\"timestamp\"" + Sender string "json:\"sender\" graphql:\"sender\"" + } "json:\"response\" graphql:\"response\"" +} type GetTransactionsResolvers struct { Response []*struct { Receipt struct { @@ -309,6 +359,38 @@ func (c *Client) GetLogsRange(ctx context.Context, chainID int, startBlock int, return &res, nil } +const GetLogsAtHeadRangeDocument = `query GetLogsAtHeadRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $page: Int!) { + response: logsAtHeadRange(chain_id: $chain_id, start_block: $start_block, end_block: $end_block, page: $page) { + contract_address + chain_id + topics + data + block_number + tx_hash + tx_index + block_hash + index + removed + } +} +` + +func (c *Client) GetLogsAtHeadRange(ctx context.Context, chainID int, startBlock int, endBlock int, page int, httpRequestOptions ...client.HTTPRequestOption) (*GetLogsAtHeadRange, error) { + vars := map[string]interface{}{ + "chain_id": chainID, + "start_block": startBlock, + "end_block": endBlock, + "page": page, + } + + var res GetLogsAtHeadRange + if err := c.Client.Post(ctx, "GetLogsAtHeadRange", GetLogsAtHeadRangeDocument, &res, vars, httpRequestOptions...); err != nil { + return nil, err + } + + return &res, nil +} + const GetLogsResolversDocument = `query GetLogsResolvers ($chain_id: Int!, $page: Int!) { response: logs(chain_id: $chain_id, page: $page) { receipt { @@ -421,6 +503,39 @@ func (c *Client) GetReceiptsRange(ctx context.Context, chainID int, startBlock i return &res, nil } +const GetReceiptsAtHeadRangeDocument = `query GetReceiptsAtHeadRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $page: Int!) { + response: receiptsAtHeadRange(chain_id: $chain_id, start_block: $start_block, end_block: $end_block, page: $page) { + chain_id + type + post_state + status + cumulative_gas_used + bloom + tx_hash + contract_address + gas_used + block_number + transaction_index + } +} +` + +func (c *Client) GetReceiptsAtHeadRange(ctx context.Context, chainID int, startBlock int, endBlock int, page int, httpRequestOptions ...client.HTTPRequestOption) (*GetReceiptsAtHeadRange, error) { + vars := map[string]interface{}{ + "chain_id": chainID, + "start_block": startBlock, + "end_block": endBlock, + "page": page, + } + + var res GetReceiptsAtHeadRange + if err := c.Client.Post(ctx, "GetReceiptsAtHeadRange", GetReceiptsAtHeadRangeDocument, &res, vars, httpRequestOptions...); err != nil { + return nil, err + } + + return &res, nil +} + const GetReceiptsResolversDocument = `query GetReceiptsResolvers ($chain_id: Int!, $page: Int!) { response: receipts(chain_id: $chain_id, page: $page) { logs { @@ -537,6 +652,43 @@ func (c *Client) GetTransactionsRange(ctx context.Context, chainID int, startBlo return &res, nil } +const GetTransactionsAtHeadRangeDocument = `query GetTransactionsAtHeadRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $last_indexed: Int!, $page: Int!) { + response: transactionsAtHeadRange(chain_id: $chain_id, start_block: $start_block, end_block: $end_block, last_indexed: $last_indexed, page: $page) { + chain_id + tx_hash + protected + type + data + gas + gas_price + gas_tip_cap + gas_fee_cap + value + nonce + to + timestamp + sender + } +} +` + +func (c *Client) GetTransactionsAtHeadRange(ctx context.Context, chainID int, startBlock int, endBlock int, lastIndexed int, page int, httpRequestOptions ...client.HTTPRequestOption) (*GetTransactionsAtHeadRange, error) { + vars := map[string]interface{}{ + "chain_id": chainID, + "start_block": startBlock, + "end_block": endBlock, + "last_indexed": lastIndexed, + "page": page, + } + + var res GetTransactionsAtHeadRange + if err := c.Client.Post(ctx, "GetTransactionsAtHeadRange", GetTransactionsAtHeadRangeDocument, &res, vars, httpRequestOptions...); err != nil { + return nil, err + } + + return &res, nil +} + const GetTransactionsResolversDocument = `query GetTransactionsResolvers ($chain_id: Int!, $page: Int!) { response: transactions(chain_id: $chain_id, page: $page) { receipt { diff --git a/services/scribe/graphql/client/queries/queries.graphql b/services/scribe/graphql/client/queries/queries.graphql index 97a559733c..19e6238462 100644 --- a/services/scribe/graphql/client/queries/queries.graphql +++ b/services/scribe/graphql/client/queries/queries.graphql @@ -28,6 +28,21 @@ query GetLogsRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $page } } +query GetLogsAtHeadRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $page: Int!) { + response: logsAtHeadRange (chain_id: $chain_id, start_block: $start_block, end_block: $end_block, page: $page) { + contract_address + chain_id + topics + data + block_number + tx_hash + tx_index + block_hash + index + removed + } +} + query GetLogsResolvers ($chain_id: Int!, $page: Int!) { response: logs (chain_id: $chain_id, page: $page) { receipt { @@ -92,6 +107,22 @@ query GetReceiptsRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $ } } +query GetReceiptsAtHeadRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $page: Int!) { + response: receiptsAtHeadRange (chain_id: $chain_id, start_block: $start_block, end_block: $end_block, page: $page) { + chain_id + type + post_state + status + cumulative_gas_used + bloom + tx_hash + contract_address + gas_used + block_number + transaction_index + } +} + query GetReceiptsResolvers ($chain_id: Int!, $page: Int!) { response: receipts (chain_id: $chain_id, page: $page) { logs { @@ -161,6 +192,24 @@ query GetTransactionsRange ($chain_id: Int!, $start_block: Int!, $end_block: Int } } +query GetTransactionsAtHeadRange ($chain_id: Int!, $start_block: Int!, $end_block: Int!, $last_indexed: Int!, $page: Int!) { + response: transactionsAtHeadRange (chain_id: $chain_id, start_block: $start_block, end_block: $end_block, last_indexed: $last_indexed, page: $page) { + chain_id + tx_hash + protected + type + data + gas + gas_price + gas_tip_cap + gas_fee_cap + value + nonce + to + timestamp + sender + } +} query GetTransactionsResolvers ($chain_id: Int!, $page: Int!) { response: transactions (chain_id: $chain_id, page: $page) { receipt { diff --git a/services/scribe/graphql/server/gin.go b/services/scribe/graphql/server/gin.go index 4cb96a04ed..9718426244 100644 --- a/services/scribe/graphql/server/gin.go +++ b/services/scribe/graphql/server/gin.go @@ -3,6 +3,7 @@ package server import ( "github.com/99designs/gqlgen/graphql/handler" "github.com/gin-gonic/gin" + "github.com/ravilushqa/otelgqlgen" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/services/scribe/db" "github.com/synapsecns/sanguine/services/scribe/graphql/server/graph" @@ -27,6 +28,8 @@ func EnableGraphql(engine *gin.Engine, eventDB db.EventDB, omniRPCURL string, me }}, ), ) + // TODO; investigate WithCreateSpanFromFields(predicate) + server.Use(otelgqlgen.Middleware(otelgqlgen.WithTracerProvider(metrics.GetTracerProvider()))) engine.GET(GraphqlEndpoint, graphqlHandler(server)) engine.POST(GraphqlEndpoint, graphqlHandler(server)) diff --git a/services/scribe/graphql/server/graph/queries.resolvers.go b/services/scribe/graphql/server/graph/queries.resolvers.go index e9bbf149e5..f4a0b8c6ee 100644 --- a/services/scribe/graphql/server/graph/queries.resolvers.go +++ b/services/scribe/graphql/server/graph/queries.resolvers.go @@ -2,7 +2,7 @@ package graph // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.31 +// Code generated by github.com/99designs/gqlgen version v0.17.36 import ( "context" @@ -15,6 +15,7 @@ import ( "github.com/synapsecns/sanguine/services/scribe/db" "github.com/synapsecns/sanguine/services/scribe/graphql/server/graph/model" resolvers "github.com/synapsecns/sanguine/services/scribe/graphql/server/graph/resolver" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" ) // Logs is the resolver for the logs field. @@ -93,6 +94,7 @@ func (r *queryResolver) TransactionsRange(ctx context.Context, txHash *string, c func (r *queryResolver) BlockTime(ctx context.Context, chainID int, blockNumber int) (*int, error) { blockTime, err := r.DB.RetrieveBlockTime(ctx, uint32(chainID), uint64(blockNumber)) if err != nil { + fmt.Println(err, "TESTING") blockTimeRaw, err := r.getBlockTime(ctx, uint32(chainID), uint64(blockNumber)) if err != nil { return nil, fmt.Errorf("error retrieving block time: %w", err) @@ -177,7 +179,7 @@ func (r *queryResolver) TxSender(ctx context.Context, txHash string, chainID int // LastIndexed is the resolver for the lastIndexed field. func (r *queryResolver) LastIndexed(ctx context.Context, contractAddress string, chainID int) (*int, error) { - blockNumber, err := r.DB.RetrieveLastIndexed(ctx, common.HexToAddress(contractAddress), uint32(chainID)) + blockNumber, err := r.DB.RetrieveLastIndexed(ctx, common.HexToAddress(contractAddress), uint32(chainID), scribeTypes.IndexingConfirmed) if err != nil { return nil, fmt.Errorf("error retrieving contract last block: %w", err) } @@ -223,6 +225,42 @@ func (r *queryResolver) BlockTimeCount(ctx context.Context, chainID int) (*int, return &blockTimesCountInt, nil } +// LogsAtHeadRange is the resolver for the logsAtHeadRange field. +func (r *queryResolver) LogsAtHeadRange(ctx context.Context, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) ([]*model.Log, error) { + logsFilter := db.BuildLogFilter(contractAddress, blockNumber, txHash, txIndex, blockHash, index, confirmed) + logsFilter.ChainID = uint32(chainID) + logs, err := r.DB.RetrieveLogsFromHeadRangeQuery(ctx, logsFilter, uint64(startBlock), uint64(endBlock), page) + if err != nil { + return nil, fmt.Errorf("error retrieving logs: %w", err) + } + + return r.logsToModelLogs(logs, logsFilter.ChainID), nil +} + +// ReceiptsAtHeadRange is the resolver for the receiptsAtHeadRange field. +func (r *queryResolver) ReceiptsAtHeadRange(ctx context.Context, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) ([]*model.Receipt, error) { + receiptsFilter := db.BuildReceiptFilter(txHash, contractAddress, blockHash, blockNumber, txIndex, confirmed) + receiptsFilter.ChainID = uint32(chainID) + receipts, err := r.DB.RetrieveReceiptsFromHeadRangeQuery(ctx, receiptsFilter, uint64(startBlock), uint64(endBlock), page) + if err != nil { + return nil, fmt.Errorf("error retrieving receipts: %w", err) + } + + return r.receiptsToModelReceipts(receipts, receiptsFilter.ChainID), nil +} + +// TransactionsAtHeadRange is the resolver for the transactionsAtHeadRange field. +func (r *queryResolver) TransactionsAtHeadRange(ctx context.Context, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, lastIndexed int, page int) ([]*model.Transaction, error) { + transactionsFilter := db.BuildEthTxFilter(txHash, blockNumber, blockHash, confirmed) + transactionsFilter.ChainID = uint32(chainID) + transactions, err := r.DB.RetrieveUnconfirmedEthTxsFromHeadRangeQuery(ctx, transactionsFilter, uint64(startBlock), uint64(endBlock), uint64(lastIndexed), page) + if err != nil { + return nil, fmt.Errorf("error retrieving transactions: %w", err) + } + + return r.ethTxsToModelTransactions(ctx, transactions, transactionsFilter.ChainID), nil +} + // Query returns resolvers.QueryResolver implementation. func (r *Resolver) Query() resolvers.QueryResolver { return &queryResolver{r} } diff --git a/services/scribe/graphql/server/graph/resolver/server.go b/services/scribe/graphql/server/graph/resolver/server.go index f8faf0e7f0..3907017661 100644 --- a/services/scribe/graphql/server/graph/resolver/server.go +++ b/services/scribe/graphql/server/graph/resolver/server.go @@ -79,11 +79,14 @@ type ComplexityRoot struct { LastStoredBlockNumber func(childComplexity int, chainID int) int LogCount func(childComplexity int, contractAddress string, chainID int) int Logs func(childComplexity int, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, page int) int + LogsAtHeadRange func(childComplexity int, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) int LogsRange func(childComplexity int, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) int ReceiptCount func(childComplexity int, chainID int) int Receipts func(childComplexity int, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, page int) int + ReceiptsAtHeadRange func(childComplexity int, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) int ReceiptsRange func(childComplexity int, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) int Transactions func(childComplexity int, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, page int) int + TransactionsAtHeadRange func(childComplexity int, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, lastIndexed int, page int) int TransactionsRange func(childComplexity int, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, page int) int TxSender func(childComplexity int, txHash string, chainID int) int } @@ -149,6 +152,9 @@ type QueryResolver interface { LogCount(ctx context.Context, contractAddress string, chainID int) (*int, error) ReceiptCount(ctx context.Context, chainID int) (*int, error) BlockTimeCount(ctx context.Context, chainID int) (*int, error) + LogsAtHeadRange(ctx context.Context, contractAddress *string, chainID int, blockNumber *int, txHash *string, txIndex *int, blockHash *string, index *int, confirmed *bool, startBlock int, endBlock int, page int) ([]*model.Log, error) + ReceiptsAtHeadRange(ctx context.Context, chainID int, txHash *string, contractAddress *string, blockHash *string, blockNumber *int, txIndex *int, confirmed *bool, startBlock int, endBlock int, page int) ([]*model.Receipt, error) + TransactionsAtHeadRange(ctx context.Context, txHash *string, chainID int, blockNumber *int, blockHash *string, confirmed *bool, startBlock int, endBlock int, lastIndexed int, page int) ([]*model.Transaction, error) } type ReceiptResolver interface { Logs(ctx context.Context, obj *model.Receipt) ([]*model.Log, error) @@ -172,7 +178,7 @@ func (e *executableSchema) Schema() *ast.Schema { } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -391,6 +397,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Logs(childComplexity, args["contract_address"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["tx_hash"].(*string), args["tx_index"].(*int), args["block_hash"].(*string), args["index"].(*int), args["confirmed"].(*bool), args["page"].(int)), true + case "Query.logsAtHeadRange": + if e.complexity.Query.LogsAtHeadRange == nil { + break + } + + args, err := ec.field_Query_logsAtHeadRange_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.LogsAtHeadRange(childComplexity, args["contract_address"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["tx_hash"].(*string), args["tx_index"].(*int), args["block_hash"].(*string), args["index"].(*int), args["confirmed"].(*bool), args["start_block"].(int), args["end_block"].(int), args["page"].(int)), true + case "Query.logsRange": if e.complexity.Query.LogsRange == nil { break @@ -427,6 +445,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Receipts(childComplexity, args["chain_id"].(int), args["tx_hash"].(*string), args["contract_address"].(*string), args["block_hash"].(*string), args["block_number"].(*int), args["tx_index"].(*int), args["confirmed"].(*bool), args["page"].(int)), true + case "Query.receiptsAtHeadRange": + if e.complexity.Query.ReceiptsAtHeadRange == nil { + break + } + + args, err := ec.field_Query_receiptsAtHeadRange_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.ReceiptsAtHeadRange(childComplexity, args["chain_id"].(int), args["tx_hash"].(*string), args["contract_address"].(*string), args["block_hash"].(*string), args["block_number"].(*int), args["tx_index"].(*int), args["confirmed"].(*bool), args["start_block"].(int), args["end_block"].(int), args["page"].(int)), true + case "Query.receiptsRange": if e.complexity.Query.ReceiptsRange == nil { break @@ -451,6 +481,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Transactions(childComplexity, args["tx_hash"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["block_hash"].(*string), args["confirmed"].(*bool), args["page"].(int)), true + case "Query.transactionsAtHeadRange": + if e.complexity.Query.TransactionsAtHeadRange == nil { + break + } + + args, err := ec.field_Query_transactionsAtHeadRange_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TransactionsAtHeadRange(childComplexity, args["tx_hash"].(*string), args["chain_id"].(int), args["block_number"].(*int), args["block_hash"].(*string), args["confirmed"].(*bool), args["start_block"].(int), args["end_block"].(int), args["last_indexed"].(int), args["page"].(int)), true + case "Query.transactionsRange": if e.complexity.Query.TransactionsRange == nil { break @@ -712,25 +754,40 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true switch rc.Operation.Operation { case ast.Query: return func(ctx context.Context) *graphql.Response { - if !first { - return nil + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, rc.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { + result := <-ec.deferredResults + atomic.AddInt32(&ec.pendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } } - first = false - ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data := ec._Query(ctx, rc.Operation.SelectionSet) var buf bytes.Buffer data.MarshalGQL(&buf) - - return &graphql.Response{ - Data: buf.Bytes(), + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + + return &response } default: @@ -741,6 +798,28 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema + deferred int32 + pendingDeferred int32 + deferredResults chan graphql.DeferredResult +} + +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() } func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { @@ -879,6 +958,45 @@ directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITI blockTimeCount( chain_id: Int! ): Int + # returns all logs that match the given filter and range (including from the unconfirmed logs table) + logsAtHeadRange( + contract_address: String + chain_id: Int! + block_number: Int + tx_hash: String + tx_index: Int + block_hash: String + index: Int + confirmed: Boolean + start_block: Int! + end_block: Int! + page: Int! + ): [Log] + # returns all receipts that match the given filter and range (including from the unconfirmed receipts table) + receiptsAtHeadRange( + chain_id: Int! + tx_hash: String + contract_address: String + block_hash: String + block_number: Int + tx_index: Int + confirmed: Boolean + start_block: Int! + end_block: Int! + page: Int! + ): [Receipt] + # returns all transactions that match the given filter and range (including from the unconfirmed transactions table) + transactionsAtHeadRange( + tx_hash: String + chain_id: Int! + block_number: Int + block_hash: String + confirmed: Boolean + start_block: Int! + end_block: Int! + last_indexed: Int! + page: Int! + ): [Transaction] } `, BuiltIn: false}, {Name: "../schema/types.graphql", Input: `scalar JSON @@ -1100,6 +1218,111 @@ func (ec *executionContext) field_Query_logCount_args(ctx context.Context, rawAr return args, nil } +func (ec *executionContext) field_Query_logsAtHeadRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["contract_address"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contract_address")) + arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["contract_address"] = arg0 + var arg1 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg1, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["chain_id"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_number"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_hash"] = arg3 + var arg4 *int + if tmp, ok := rawArgs["tx_index"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_index")) + arg4, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_index"] = arg4 + var arg5 *string + if tmp, ok := rawArgs["block_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) + arg5, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_hash"] = arg5 + var arg6 *int + if tmp, ok := rawArgs["index"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("index")) + arg6, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["index"] = arg6 + var arg7 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg7, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["confirmed"] = arg7 + var arg8 int + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg8, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg8 + var arg9 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg9, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg9 + var arg10 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + arg10, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["page"] = arg10 + return args, nil +} + func (ec *executionContext) field_Query_logsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1307,7 +1530,7 @@ func (ec *executionContext) field_Query_receiptCount_args(ctx context.Context, r return args, nil } -func (ec *executionContext) field_Query_receiptsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_receiptsAtHeadRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 int @@ -1403,7 +1626,7 @@ func (ec *executionContext) field_Query_receiptsRange_args(ctx context.Context, return args, nil } -func (ec *executionContext) field_Query_receipts_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_receiptsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 int @@ -1470,47 +1693,65 @@ func (ec *executionContext) field_Query_receipts_args(ctx context.Context, rawAr } args["confirmed"] = arg6 var arg7 int + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg7, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg7 + var arg8 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg8, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg8 + var arg9 int if tmp, ok := rawArgs["page"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - arg7, err = ec.unmarshalNInt2int(ctx, tmp) + arg9, err = ec.unmarshalNInt2int(ctx, tmp) if err != nil { return nil, err } } - args["page"] = arg7 + args["page"] = arg9 return args, nil } -func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_receipts_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 *string - if tmp, ok := rawArgs["tx_hash"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) - arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + var arg0 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg0, err = ec.unmarshalNInt2int(ctx, tmp) if err != nil { return nil, err } } - args["tx_hash"] = arg0 - var arg1 int - if tmp, ok := rawArgs["chain_id"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) - arg1, err = ec.unmarshalNInt2int(ctx, tmp) + args["chain_id"] = arg0 + var arg1 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp) if err != nil { return nil, err } } - args["chain_id"] = arg1 - var arg2 *int - if tmp, ok := rawArgs["block_number"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) - arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + args["tx_hash"] = arg1 + var arg2 *string + if tmp, ok := rawArgs["contract_address"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contract_address")) + arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) if err != nil { return nil, err } } - args["block_number"] = arg2 + args["contract_address"] = arg2 var arg3 *string if tmp, ok := rawArgs["block_hash"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) @@ -1520,33 +1761,33 @@ func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Conte } } args["block_hash"] = arg3 - var arg4 *bool - if tmp, ok := rawArgs["confirmed"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) - arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + var arg4 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg4, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["confirmed"] = arg4 - var arg5 int - if tmp, ok := rawArgs["start_block"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) - arg5, err = ec.unmarshalNInt2int(ctx, tmp) + args["block_number"] = arg4 + var arg5 *int + if tmp, ok := rawArgs["tx_index"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_index")) + arg5, err = ec.unmarshalOInt2ᚖint(ctx, tmp) if err != nil { return nil, err } } - args["start_block"] = arg5 - var arg6 int - if tmp, ok := rawArgs["end_block"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) - arg6, err = ec.unmarshalNInt2int(ctx, tmp) + args["tx_index"] = arg5 + var arg6 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg6, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) if err != nil { return nil, err } } - args["end_block"] = arg6 + args["confirmed"] = arg6 var arg7 int if tmp, ok := rawArgs["page"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) @@ -1559,7 +1800,7 @@ func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Conte return args, nil } -func (ec *executionContext) field_Query_transactions_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_transactionsAtHeadRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 *string @@ -1608,8 +1849,173 @@ func (ec *executionContext) field_Query_transactions_args(ctx context.Context, r } args["confirmed"] = arg4 var arg5 int - if tmp, ok := rawArgs["page"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg5, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg5 + var arg6 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg6, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg6 + var arg7 int + if tmp, ok := rawArgs["last_indexed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("last_indexed")) + arg7, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["last_indexed"] = arg7 + var arg8 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + arg8, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["page"] = arg8 + return args, nil +} + +func (ec *executionContext) field_Query_transactionsRange_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_hash"] = arg0 + var arg1 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg1, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["chain_id"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_number"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["block_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_hash"] = arg3 + var arg4 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["confirmed"] = arg4 + var arg5 int + if tmp, ok := rawArgs["start_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("start_block")) + arg5, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["start_block"] = arg5 + var arg6 int + if tmp, ok := rawArgs["end_block"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("end_block")) + arg6, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["end_block"] = arg6 + var arg7 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + arg7, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["page"] = arg7 + return args, nil +} + +func (ec *executionContext) field_Query_transactions_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *string + if tmp, ok := rawArgs["tx_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tx_hash")) + arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["tx_hash"] = arg0 + var arg1 int + if tmp, ok := rawArgs["chain_id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("chain_id")) + arg1, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["chain_id"] = arg1 + var arg2 *int + if tmp, ok := rawArgs["block_number"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_number")) + arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_number"] = arg2 + var arg3 *string + if tmp, ok := rawArgs["block_hash"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("block_hash")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["block_hash"] = arg3 + var arg4 *bool + if tmp, ok := rawArgs["confirmed"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirmed")) + arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["confirmed"] = arg4 + var arg5 int + if tmp, ok := rawArgs["page"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) arg5, err = ec.unmarshalNInt2int(ctx, tmp) if err != nil { return nil, err @@ -2576,7 +2982,7 @@ func (ec *executionContext) fieldContext_Query_logs(ctx context.Context, field g ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_logs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2658,7 +3064,7 @@ func (ec *executionContext) fieldContext_Query_logsRange(ctx context.Context, fi ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_logsRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2742,7 +3148,7 @@ func (ec *executionContext) fieldContext_Query_receipts(ctx context.Context, fie ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_receipts_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2826,7 +3232,7 @@ func (ec *executionContext) fieldContext_Query_receiptsRange(ctx context.Context ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_receiptsRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2916,7 +3322,7 @@ func (ec *executionContext) fieldContext_Query_transactions(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_transactions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3006,7 +3412,7 @@ func (ec *executionContext) fieldContext_Query_transactionsRange(ctx context.Con ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_transactionsRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3058,7 +3464,7 @@ func (ec *executionContext) fieldContext_Query_blockTime(ctx context.Context, fi ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_blockTime_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3110,7 +3516,7 @@ func (ec *executionContext) fieldContext_Query_lastStoredBlockNumber(ctx context ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_lastStoredBlockNumber_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3162,7 +3568,7 @@ func (ec *executionContext) fieldContext_Query_firstStoredBlockNumber(ctx contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_firstStoredBlockNumber_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3214,7 +3620,7 @@ func (ec *executionContext) fieldContext_Query_lastConfirmedBlockNumber(ctx cont ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_lastConfirmedBlockNumber_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3266,7 +3672,7 @@ func (ec *executionContext) fieldContext_Query_txSender(ctx context.Context, fie ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_txSender_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3318,7 +3724,7 @@ func (ec *executionContext) fieldContext_Query_lastIndexed(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_lastIndexed_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3370,7 +3776,7 @@ func (ec *executionContext) fieldContext_Query_logCount(ctx context.Context, fie ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_logCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3422,7 +3828,7 @@ func (ec *executionContext) fieldContext_Query_receiptCount(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_receiptCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -3474,13 +3880,13 @@ func (ec *executionContext) fieldContext_Query_blockTimeCount(ctx context.Contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_blockTimeCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } -func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___type(ctx, field) +func (ec *executionContext) _Query_logsAtHeadRange(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_logsAtHeadRange(ctx, field) if err != nil { return graphql.Null } @@ -3493,7 +3899,7 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.introspectType(fc.Args["name"].(string)) + return ec.resolvers.Query().LogsAtHeadRange(rctx, fc.Args["contract_address"].(*string), fc.Args["chain_id"].(int), fc.Args["block_number"].(*int), fc.Args["tx_hash"].(*string), fc.Args["tx_index"].(*int), fc.Args["block_hash"].(*string), fc.Args["index"].(*int), fc.Args["confirmed"].(*bool), fc.Args["start_block"].(int), fc.Args["end_block"].(int), fc.Args["page"].(int)) }) if err != nil { ec.Error(ctx, err) @@ -3502,41 +3908,49 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.([]*model.Log) fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOLog2ᚕᚖgithubᚗcomᚋsynapsecnsᚋsanguineᚋservicesᚋscribeᚋgraphqlᚋserverᚋgraphᚋmodelᚐLog(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_logsAtHeadRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, - IsResolver: false, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "contract_address": + return ec.fieldContext_Log_contract_address(ctx, field) + case "chain_id": + return ec.fieldContext_Log_chain_id(ctx, field) + case "topics": + return ec.fieldContext_Log_topics(ctx, field) + case "data": + return ec.fieldContext_Log_data(ctx, field) + case "block_number": + return ec.fieldContext_Log_block_number(ctx, field) + case "tx_hash": + return ec.fieldContext_Log_tx_hash(ctx, field) + case "tx_index": + return ec.fieldContext_Log_tx_index(ctx, field) + case "block_hash": + return ec.fieldContext_Log_block_hash(ctx, field) + case "index": + return ec.fieldContext_Log_index(ctx, field) + case "removed": + return ec.fieldContext_Log_removed(ctx, field) + case "page": + return ec.fieldContext_Log_page(ctx, field) + case "transaction": + return ec.fieldContext_Log_transaction(ctx, field) + case "receipt": + return ec.fieldContext_Log_receipt(ctx, field) + case "json": + return ec.fieldContext_Log_json(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Log", field.Name) }, } defer func() { @@ -3546,15 +3960,15 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Query_logsAtHeadRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } -func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___schema(ctx, field) +func (ec *executionContext) _Query_receiptsAtHeadRange(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_receiptsAtHeadRange(ctx, field) if err != nil { return graphql.Null } @@ -3567,7 +3981,7 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.introspectSchema() + return ec.resolvers.Query().ReceiptsAtHeadRange(rctx, fc.Args["chain_id"].(int), fc.Args["tx_hash"].(*string), fc.Args["contract_address"].(*string), fc.Args["block_hash"].(*string), fc.Args["block_number"].(*int), fc.Args["tx_index"].(*int), fc.Args["confirmed"].(*bool), fc.Args["start_block"].(int), fc.Args["end_block"].(int), fc.Args["page"].(int)) }) if err != nil { ec.Error(ctx, err) @@ -3576,42 +3990,290 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Schema) + res := resTmp.([]*model.Receipt) fc.Result = res - return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) + return ec.marshalOReceipt2ᚕᚖgithubᚗcomᚋsynapsecnsᚋsanguineᚋservicesᚋscribeᚋgraphqlᚋserverᚋgraphᚋmodelᚐReceipt(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_receiptsAtHeadRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, - IsResolver: false, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "description": - return ec.fieldContext___Schema_description(ctx, field) - case "types": - return ec.fieldContext___Schema_types(ctx, field) - case "queryType": - return ec.fieldContext___Schema_queryType(ctx, field) - case "mutationType": - return ec.fieldContext___Schema_mutationType(ctx, field) - case "subscriptionType": - return ec.fieldContext___Schema_subscriptionType(ctx, field) - case "directives": - return ec.fieldContext___Schema_directives(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _Receipt_chain_id(ctx context.Context, field graphql.CollectedField, obj *model.Receipt) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Receipt_chain_id(ctx, field) - if err != nil { - return graphql.Null + case "chain_id": + return ec.fieldContext_Receipt_chain_id(ctx, field) + case "type": + return ec.fieldContext_Receipt_type(ctx, field) + case "post_state": + return ec.fieldContext_Receipt_post_state(ctx, field) + case "status": + return ec.fieldContext_Receipt_status(ctx, field) + case "cumulative_gas_used": + return ec.fieldContext_Receipt_cumulative_gas_used(ctx, field) + case "bloom": + return ec.fieldContext_Receipt_bloom(ctx, field) + case "tx_hash": + return ec.fieldContext_Receipt_tx_hash(ctx, field) + case "contract_address": + return ec.fieldContext_Receipt_contract_address(ctx, field) + case "gas_used": + return ec.fieldContext_Receipt_gas_used(ctx, field) + case "block_number": + return ec.fieldContext_Receipt_block_number(ctx, field) + case "transaction_index": + return ec.fieldContext_Receipt_transaction_index(ctx, field) + case "page": + return ec.fieldContext_Receipt_page(ctx, field) + case "logs": + return ec.fieldContext_Receipt_logs(ctx, field) + case "transaction": + return ec.fieldContext_Receipt_transaction(ctx, field) + case "json": + return ec.fieldContext_Receipt_json(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Receipt", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_receiptsAtHeadRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_transactionsAtHeadRange(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_transactionsAtHeadRange(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TransactionsAtHeadRange(rctx, fc.Args["tx_hash"].(*string), fc.Args["chain_id"].(int), fc.Args["block_number"].(*int), fc.Args["block_hash"].(*string), fc.Args["confirmed"].(*bool), fc.Args["start_block"].(int), fc.Args["end_block"].(int), fc.Args["last_indexed"].(int), fc.Args["page"].(int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*model.Transaction) + fc.Result = res + return ec.marshalOTransaction2ᚕᚖgithubᚗcomᚋsynapsecnsᚋsanguineᚋservicesᚋscribeᚋgraphqlᚋserverᚋgraphᚋmodelᚐTransaction(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_transactionsAtHeadRange(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "chain_id": + return ec.fieldContext_Transaction_chain_id(ctx, field) + case "tx_hash": + return ec.fieldContext_Transaction_tx_hash(ctx, field) + case "protected": + return ec.fieldContext_Transaction_protected(ctx, field) + case "type": + return ec.fieldContext_Transaction_type(ctx, field) + case "data": + return ec.fieldContext_Transaction_data(ctx, field) + case "gas": + return ec.fieldContext_Transaction_gas(ctx, field) + case "gas_price": + return ec.fieldContext_Transaction_gas_price(ctx, field) + case "gas_tip_cap": + return ec.fieldContext_Transaction_gas_tip_cap(ctx, field) + case "gas_fee_cap": + return ec.fieldContext_Transaction_gas_fee_cap(ctx, field) + case "value": + return ec.fieldContext_Transaction_value(ctx, field) + case "nonce": + return ec.fieldContext_Transaction_nonce(ctx, field) + case "to": + return ec.fieldContext_Transaction_to(ctx, field) + case "page": + return ec.fieldContext_Transaction_page(ctx, field) + case "sender": + return ec.fieldContext_Transaction_sender(ctx, field) + case "timestamp": + return ec.fieldContext_Transaction_timestamp(ctx, field) + case "logs": + return ec.fieldContext_Transaction_logs(ctx, field) + case "receipt": + return ec.fieldContext_Transaction_receipt(ctx, field) + case "json": + return ec.fieldContext_Transaction_json(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Transaction", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_transactionsAtHeadRange_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(fc.Args["name"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___schema(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + fc.Result = res + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "description": + return ec.fieldContext___Schema_description(ctx, field) + case "types": + return ec.fieldContext___Schema_types(ctx, field) + case "queryType": + return ec.fieldContext___Schema_queryType(ctx, field) + case "mutationType": + return ec.fieldContext___Schema_mutationType(ctx, field) + case "subscriptionType": + return ec.fieldContext___Schema_subscriptionType(ctx, field) + case "directives": + return ec.fieldContext___Schema_directives(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Receipt_chain_id(ctx context.Context, field graphql.CollectedField, obj *model.Receipt) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Receipt_chain_id(ctx, field) + if err != nil { + return graphql.Null } ctx = graphql.WithFieldContext(ctx, fc) defer func() { @@ -6605,7 +7267,7 @@ func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, fiel ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6793,7 +7455,7 @@ func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -6969,41 +7631,48 @@ var blockTimeImplementors = []string{"BlockTime"} func (ec *executionContext) _BlockTime(ctx context.Context, sel ast.SelectionSet, obj *model.BlockTime) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, blockTimeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("BlockTime") case "chain_id": - out.Values[i] = ec._BlockTime_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "block_number": - out.Values[i] = ec._BlockTime_block_number(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "timestamp": - out.Values[i] = ec._BlockTime_timestamp(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7011,93 +7680,72 @@ var logImplementors = []string{"Log"} func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj *model.Log) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, logImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Log") case "contract_address": - out.Values[i] = ec._Log_contract_address(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "chain_id": - out.Values[i] = ec._Log_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "topics": - out.Values[i] = ec._Log_topics(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "data": - out.Values[i] = ec._Log_data(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "block_number": - out.Values[i] = ec._Log_block_number(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_hash": - out.Values[i] = ec._Log_tx_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_index": - out.Values[i] = ec._Log_tx_index(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "block_hash": - out.Values[i] = ec._Log_block_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "index": - out.Values[i] = ec._Log_index(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "removed": - out.Values[i] = ec._Log_removed(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "page": - out.Values[i] = ec._Log_page(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "transaction": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7105,19 +7753,35 @@ func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj }() res = ec._Log_transaction(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "receipt": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7125,19 +7789,35 @@ func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj }() res = ec._Log_receipt(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "json": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7145,23 +7825,51 @@ func (ec *executionContext) _Log(ctx context.Context, sel ast.SelectionSet, obj }() res = ec._Log_json(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7174,7 +7882,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr }) out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ Object: field.Name, @@ -7187,7 +7895,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr case "logs": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7198,16 +7906,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "logsRange": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7218,16 +7925,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "receipts": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7238,16 +7944,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "receiptsRange": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7258,16 +7963,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "transactions": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7278,16 +7982,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "transactionsRange": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7298,16 +8001,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "blockTime": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7318,16 +8020,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "lastStoredBlockNumber": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7338,16 +8039,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "firstStoredBlockNumber": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7358,16 +8058,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "lastConfirmedBlockNumber": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7378,16 +8077,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "txSender": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7398,16 +8096,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "lastIndexed": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7418,16 +8115,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "logCount": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7438,16 +8134,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "receiptCount": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7458,16 +8153,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "blockTimeCount": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7478,32 +8172,96 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) - case "__type": + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "logsAtHeadRange": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_logsAtHeadRange(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "receiptsAtHeadRange": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_receiptsAtHeadRange(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "transactionsAtHeadRange": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_transactionsAtHeadRange(ctx, field) + return res + } + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___type(ctx, field) }) - case "__schema": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___schema(ctx, field) }) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7511,100 +8269,77 @@ var receiptImplementors = []string{"Receipt"} func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, obj *model.Receipt) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, receiptImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Receipt") case "chain_id": - out.Values[i] = ec._Receipt_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "type": - out.Values[i] = ec._Receipt_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "post_state": - out.Values[i] = ec._Receipt_post_state(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "status": - out.Values[i] = ec._Receipt_status(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "cumulative_gas_used": - out.Values[i] = ec._Receipt_cumulative_gas_used(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "bloom": - out.Values[i] = ec._Receipt_bloom(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_hash": - out.Values[i] = ec._Receipt_tx_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "contract_address": - out.Values[i] = ec._Receipt_contract_address(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_used": - out.Values[i] = ec._Receipt_gas_used(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "block_number": - out.Values[i] = ec._Receipt_block_number(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "transaction_index": - out.Values[i] = ec._Receipt_transaction_index(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "page": - out.Values[i] = ec._Receipt_page(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "logs": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7614,14 +8349,30 @@ func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "transaction": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7629,19 +8380,35 @@ func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, }() res = ec._Receipt_transaction(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "json": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7649,23 +8416,51 @@ func (ec *executionContext) _Receipt(ctx context.Context, sel ast.SelectionSet, }() res = ec._Receipt_json(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7673,121 +8468,92 @@ var transactionImplementors = []string{"Transaction"} func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionSet, obj *model.Transaction) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, transactionImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Transaction") case "chain_id": - out.Values[i] = ec._Transaction_chain_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "tx_hash": - out.Values[i] = ec._Transaction_tx_hash(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "protected": - out.Values[i] = ec._Transaction_protected(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "type": - out.Values[i] = ec._Transaction_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "data": - out.Values[i] = ec._Transaction_data(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas": - out.Values[i] = ec._Transaction_gas(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_price": - out.Values[i] = ec._Transaction_gas_price(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_tip_cap": - out.Values[i] = ec._Transaction_gas_tip_cap(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "gas_fee_cap": - out.Values[i] = ec._Transaction_gas_fee_cap(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "value": - out.Values[i] = ec._Transaction_value(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "nonce": - out.Values[i] = ec._Transaction_nonce(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "to": - out.Values[i] = ec._Transaction_to(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "page": - out.Values[i] = ec._Transaction_page(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "sender": - out.Values[i] = ec._Transaction_sender(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "timestamp": - out.Values[i] = ec._Transaction_timestamp(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&out.Invalids, 1) } case "logs": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7797,14 +8563,30 @@ func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionS return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "receipt": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7812,19 +8594,35 @@ func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionS }() res = ec._Transaction_receipt(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "json": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -7832,23 +8630,51 @@ func (ec *executionContext) _Transaction(ctx context.Context, sel ast.SelectionS }() res = ec._Transaction_json(ctx, field, obj) if res == graphql.Null { - atomic.AddUint32(&invalids, 1) + atomic.AddUint32(&fs.Invalids, 1) } return res } - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - }) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7856,52 +8682,55 @@ var __DirectiveImplementors = []string{"__Directive"} func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Directive") case "name": - out.Values[i] = ec.___Directive_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Directive_description(ctx, field, obj) - case "locations": - out.Values[i] = ec.___Directive_locations(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "args": - out.Values[i] = ec.___Directive_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7909,42 +8738,47 @@ var __EnumValueImplementors = []string{"__EnumValue"} func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__EnumValue") case "name": - out.Values[i] = ec.___EnumValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___EnumValue_description(ctx, field, obj) - case "isDeprecated": - out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -7952,56 +8786,57 @@ var __FieldImplementors = []string{"__Field"} func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Field") case "name": - out.Values[i] = ec.___Field_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Field_description(ctx, field, obj) - case "args": - out.Values[i] = ec.___Field_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "type": - out.Values[i] = ec.___Field_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isDeprecated": - out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -8009,42 +8844,47 @@ var __InputValueImplementors = []string{"__InputValue"} func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__InputValue") case "name": - out.Values[i] = ec.___InputValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___InputValue_description(ctx, field, obj) - case "type": - out.Values[i] = ec.___InputValue_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "defaultValue": - out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -8052,53 +8892,54 @@ var __SchemaImplementors = []string{"__Schema"} func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Schema") case "description": - out.Values[i] = ec.___Schema_description(ctx, field, obj) - case "types": - out.Values[i] = ec.___Schema_types(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "queryType": - out.Values[i] = ec.___Schema_queryType(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "mutationType": - out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) - case "subscriptionType": - out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) - case "directives": - out.Values[i] = ec.___Schema_directives(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -8106,63 +8947,56 @@ var __TypeImplementors = []string{"__Type"} func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Type") case "kind": - out.Values[i] = ec.___Type_kind(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "name": - out.Values[i] = ec.___Type_name(ctx, field, obj) - case "description": - out.Values[i] = ec.___Type_description(ctx, field, obj) - case "fields": - out.Values[i] = ec.___Type_fields(ctx, field, obj) - case "interfaces": - out.Values[i] = ec.___Type_interfaces(ctx, field, obj) - case "possibleTypes": - out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) - case "enumValues": - out.Values[i] = ec.___Type_enumValues(ctx, field, obj) - case "inputFields": - out.Values[i] = ec.___Type_inputFields(ctx, field, obj) - case "ofType": - out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } diff --git a/services/scribe/graphql/server/graph/schema/queries.graphql b/services/scribe/graphql/server/graph/schema/queries.graphql index 0f9d90ef0a..8fe6265d98 100644 --- a/services/scribe/graphql/server/graph/schema/queries.graphql +++ b/services/scribe/graphql/server/graph/schema/queries.graphql @@ -109,4 +109,43 @@ type Query { blockTimeCount( chain_id: Int! ): Int + # returns all logs that match the given filter and range (including from the unconfirmed logs table) + logsAtHeadRange( + contract_address: String + chain_id: Int! + block_number: Int + tx_hash: String + tx_index: Int + block_hash: String + index: Int + confirmed: Boolean + start_block: Int! + end_block: Int! + page: Int! + ): [Log] + # returns all receipts that match the given filter and range (including from the unconfirmed receipts table) + receiptsAtHeadRange( + chain_id: Int! + tx_hash: String + contract_address: String + block_hash: String + block_number: Int + tx_index: Int + confirmed: Boolean + start_block: Int! + end_block: Int! + page: Int! + ): [Receipt] + # returns all transactions that match the given filter and range (including from the unconfirmed transactions table) + transactionsAtHeadRange( + tx_hash: String + chain_id: Int! + block_number: Int + block_hash: String + confirmed: Boolean + start_block: Int! + end_block: Int! + last_indexed: Int! + page: Int! + ): [Transaction] } diff --git a/services/scribe/graphql/server/graph/types.resolvers.go b/services/scribe/graphql/server/graph/types.resolvers.go index 778a71872d..ac3127998a 100644 --- a/services/scribe/graphql/server/graph/types.resolvers.go +++ b/services/scribe/graphql/server/graph/types.resolvers.go @@ -2,7 +2,7 @@ package graph // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.31 +// Code generated by github.com/99designs/gqlgen version v0.17.36 import ( "context" diff --git a/services/scribe/graphql/server/graph/utils.go b/services/scribe/graphql/server/graph/utils.go index db5af76d6a..5ad8a42082 100644 --- a/services/scribe/graphql/server/graph/utils.go +++ b/services/scribe/graphql/server/graph/utils.go @@ -3,15 +3,16 @@ package graph import ( "context" "fmt" + "github.com/synapsecns/sanguine/services/scribe/backend" + "math/big" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ipfs/go-log" "github.com/jpillora/backoff" - "github.com/synapsecns/sanguine/services/scribe/backfill" "github.com/synapsecns/sanguine/services/scribe/db" "github.com/synapsecns/sanguine/services/scribe/graphql/server/graph/model" - "math/big" - "time" ) var logger = log.Logger("scribe-graph") @@ -129,12 +130,12 @@ func (r Resolver) getBlockTime(ctx context.Context, chainID uint32, blockNumber Factor: 2, Jitter: true, Min: 30 * time.Millisecond, - Max: 3 * time.Second, + Max: 5 * time.Second, } timeout := time.Duration(0) - var backendClient backfill.ScribeBackend - backendClient, err := backfill.DialBackend(ctx, fmt.Sprintf("%s/%d", r.OmniRPCURL, chainID), r.Metrics) + var backendClient backend.ScribeBackend + backendClient, err := backend.DialBackend(ctx, fmt.Sprintf("%s/%d", r.OmniRPCURL, chainID), r.Metrics) if err != nil { return nil, fmt.Errorf("could not create backend client: %w", err) } @@ -149,6 +150,8 @@ func (r Resolver) getBlockTime(ctx context.Context, chainID uint32, blockNumber if err != nil { timeout = b.Duration() + fmt.Println("TESTING--", fmt.Sprintf("%s/%d", r.OmniRPCURL, chainID), err) + continue } blockTime := block.Time diff --git a/services/scribe/grpc/server/server.go b/services/scribe/grpc/server/server.go index a7e440bacf..f59b516915 100644 --- a/services/scribe/grpc/server/server.go +++ b/services/scribe/grpc/server/server.go @@ -151,7 +151,7 @@ func (s *server) StreamLogs(req *pbscribe.StreamLogsRequest, res pbscribe.Scribe // TODO: Make wait time configurable (?). time.Sleep(time.Duration(wait) * time.Second) wait = 1 - latestScribeBlock, err := s.db.RetrieveLastIndexed(ctx, common.HexToAddress(req.Filter.ContractAddress.GetData()), req.Filter.ChainId) + latestScribeBlock, err := s.db.RetrieveLastIndexed(ctx, common.HexToAddress(req.Filter.ContractAddress.GetData()), req.Filter.ChainId, false) if err != nil { continue } @@ -203,7 +203,7 @@ func (s *server) setBlocks(ctx context.Context, req *pbscribe.StreamLogsRequest) for i, block := range blocks { switch block { case "latest": - lastIndexed, err := s.db.RetrieveLastIndexed(ctx, common.HexToAddress(req.Filter.ContractAddress.GetData()), req.Filter.ChainId) + lastIndexed, err := s.db.RetrieveLastIndexed(ctx, common.HexToAddress(req.Filter.ContractAddress.GetData()), req.Filter.ChainId, false) if err != nil { return 0, 0, fmt.Errorf("could not retrieve last indexed block: %w", err) } diff --git a/services/scribe/logger/doc.go b/services/scribe/logger/doc.go new file mode 100644 index 0000000000..6570c59f1d --- /dev/null +++ b/services/scribe/logger/doc.go @@ -0,0 +1,6 @@ +// Package logger handles logging various scribe events and errors. +package logger + +// check unparam +// check cyclop +// check gocognit,cyclop,maintidx diff --git a/services/scribe/logger/handler.go b/services/scribe/logger/handler.go new file mode 100644 index 0000000000..5dc5c3d570 --- /dev/null +++ b/services/scribe/logger/handler.go @@ -0,0 +1,151 @@ +package logger + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ipfs/go-log" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" +) + +var logger = log.Logger("scribe") + +const ( + // ContextCancelled is returned when the context is canceled. + ContextCancelled ErrorType = iota + // LivefillIndexerError is returned when the livefill indexer encounters an error. + LivefillIndexerError + // BackfillIndexerError is returned when an indexer backfilling a contract to the head encounters an error. + BackfillIndexerError + // GetLogsError is returned when the logs cannot be retrieved. + GetLogsError + // GetTxError is returned when the tx cannot be retrieved. + GetTxError + // CouldNotGetReceiptError is returned when the receipt cannot be retrieved. + CouldNotGetReceiptError + // GetBlockError is returned when the block cannot be retrieved. + GetBlockError + // BlockByNumberError is returned when the block cannot be retrieved. + BlockByNumberError + // StoreError is returned when data cannot be inserted into the database. + StoreError + // ReadError is returned when data cannot be read from the database. + ReadError + // TestError is returned when an error during a test occurs. + TestError + // EmptyGetLogsChunk is returned when a getLogs chunk is empty. + EmptyGetLogsChunk + // FatalScribeError is for when something goes wrong with scribe. + FatalScribeError +) + +const ( + // InitiatingLivefill is returned when a contract backfills and is moving to livefill. + InitiatingLivefill StatusType = iota + // ConcurrencyThresholdReached is returned when the concurrency threshold is reached. + ConcurrencyThresholdReached + // FlushingLivefillAtHead is returned when a livefill indexer is flushing at the head. + FlushingLivefillAtHead + // CreatingSQLStore is returned when a SQL store is being created. + CreatingSQLStore +) + +// ErrorType is a type of error. +type ErrorType int + +// StatusType is a type of status for a process in scribe. +type StatusType int + +// ReportIndexerError reports an error that occurs in an indexer. +// +// nolint +func ReportIndexerError(err error, indexerData scribeTypes.IndexerConfig, errorType ErrorType) { + // nolint:exhaustive + if err == nil { + logger.Errorf("Error, @DEV: NIL ERROR\n%s", unpackIndexerConfig(indexerData)) + return + } + + errStr := err.Error() + + // Stop cloudflare error messages from nuking readablity of logs + if len(errStr) > 1000 { + errStr = errStr[:1000] + } + switch errorType { + case ContextCancelled: + logger.Errorf("Context canceled for indexer. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case LivefillIndexerError: + logger.Errorf("Livefill indexer failed. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case GetLogsError: + logger.Errorf("Could not get logs. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case GetTxError: + logger.Errorf("Could not get tx. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case CouldNotGetReceiptError: + logger.Errorf("Could not get receipt. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case GetBlockError: + logger.Errorf("Could not get head block. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case BlockByNumberError: + logger.Errorf("Could not get block header. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case StoreError: + logger.Errorf("Could not store data into database. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case ReadError: + logger.Errorf("Could not read data from database. Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + case EmptyGetLogsChunk: + logger.Warnf("Encountered empty getlogs chunk%s", unpackIndexerConfig(indexerData)) + default: + logger.Errorf("Error: %v\n%s", errStr, unpackIndexerConfig(indexerData)) + } +} + +// ReportScribeError reports an error that occurs anywhere in scribe. +// +// nolint:exhaustive +func ReportScribeError(err error, chainID uint32, errorType ErrorType) { + switch errorType { + case ContextCancelled: + logger.Errorf("Context canceled for scribe on chain %d. Error: %v", chainID, err) + case GetBlockError: + logger.Errorf("Could not get head block on chain %d. Error: %v", chainID, err) + case TestError: + logger.Errorf("Test error on chain %d. Error: %v", chainID, err) + + default: + + logger.Errorf("Error on chain %d: %v", chainID, err) + } +} + +// ReportScribeState reports a state that occurs anywhere in scribe. +func ReportScribeState(chainID uint32, block uint64, addresses []common.Address, statusType StatusType) { + // nolint:exhaustive + switch statusType { + case InitiatingLivefill: + logger.Warnf("Initiating livefill on chain %d on block %d while interacting with contract %s", chainID, block, dumpAddresses(addresses)) + case ConcurrencyThresholdReached: + logger.Warnf("Concurrency threshold reached on chain %d on block %d while interacting with contract %s", chainID, block, dumpAddresses(addresses)) + case FlushingLivefillAtHead: + logger.Warnf("Flushing logs at head on chain %d", chainID) + case CreatingSQLStore: + logger.Warnf("Creating SQL store") + default: + logger.Warnf("Event on chain %d on block %d while interacting with contract %s", chainID, block, dumpAddresses(addresses)) + } +} + +func unpackIndexerConfig(indexerData scribeTypes.IndexerConfig) string { + return fmt.Sprintf("Contracts: %v, GetLogsRange: %d, GetLogsBatchAmount: %d, StoreConcurrency: %d, ChainID: %d, StartHeight: %d, EndHeight: %d, ConcurrencyThreshold: %d", + indexerData.Addresses, indexerData.GetLogsRange, indexerData.GetLogsBatchAmount, indexerData.StoreConcurrency, indexerData.ChainID, indexerData.StartHeight, indexerData.EndHeight, indexerData.ConcurrencyThreshold) +} + +func dumpAddresses(addresses []common.Address) string { + addressesStr := "" + for i := range addresses { + if i == len(addresses)-1 { + addressesStr += addresses[i].String() + } else { + addressesStr += addresses[i].String() + ", " + } + } + return addressesStr +} diff --git a/services/scribe/node/doc.go b/services/scribe/node/doc.go deleted file mode 100644 index 9381ebca7e..0000000000 --- a/services/scribe/node/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package node uses the backfiller to get all previous logs, and then listens to the -// height of the blockchain in order to add new logs as blocks come in. -package node diff --git a/services/scribe/node/export_test.go b/services/scribe/node/export_test.go deleted file mode 100644 index d5d0c0de1f..0000000000 --- a/services/scribe/node/export_test.go +++ /dev/null @@ -1,6 +0,0 @@ -package node - -//// ProcessRange exports filtering logs for testing. -// func (s Scribe) ProcessRange(ctx context.Context, chainID uint32, requiredConfirmations uint32) error { -// return s.confirmBlocks(ctx, chainID, requiredConfirmations) -//} diff --git a/services/scribe/node/logger.go b/services/scribe/node/logger.go deleted file mode 100644 index 0f1e475eac..0000000000 --- a/services/scribe/node/logger.go +++ /dev/null @@ -1,5 +0,0 @@ -package node - -import "github.com/ipfs/go-log" - -var logger = log.Logger("synapse-scribe-node") diff --git a/services/scribe/node/scribe.go b/services/scribe/node/scribe.go deleted file mode 100644 index 25301221a3..0000000000 --- a/services/scribe/node/scribe.go +++ /dev/null @@ -1,340 +0,0 @@ -package node - -import ( - "context" - "fmt" - lru "github.com/hashicorp/golang-lru" - "github.com/jpillora/backoff" - "github.com/synapsecns/sanguine/core/metrics" - "github.com/synapsecns/sanguine/ethergo/util" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "go.opentelemetry.io/otel/attribute" - otelMetrics "go.opentelemetry.io/otel/metric" - - "golang.org/x/sync/errgroup" - - "math/big" - "time" -) - -// Scribe is a live scribe that logs all event data. -type Scribe struct { - // eventDB is the database to store event data in. - eventDB db.EventDB - // clients is a mapping of chain IDs -> clients. - clients map[uint32][]backfill.ScribeBackend - // scribeBackfiller is the backfiller for the scribe. - scribeBackfiller *backfill.ScribeBackfiller - // config is the config for the scribe. - config config.Config - // handler is the metrics handler for the scribe. - handler metrics.Handler - // reorgMeters holds a otel counter meter for reorgs for each chain - reorgMeters map[uint32]otelMetrics.Int64Counter -} - -// checkFinality checks if the block is final on the chain. -// and deletes irrelevant blocks. -const checkFinality = false - -// NewScribe creates a new scribe. -func NewScribe(eventDB db.EventDB, clients map[uint32][]backfill.ScribeBackend, config config.Config, handler metrics.Handler) (*Scribe, error) { - scribeBackfiller, err := backfill.NewScribeBackfiller(eventDB, clients, config, handler) - if err != nil { - return nil, fmt.Errorf("could not create scribe backfiller: %w", err) - } - - return &Scribe{ - eventDB: eventDB, - clients: clients, - scribeBackfiller: scribeBackfiller, - config: config, - handler: handler, - reorgMeters: make(map[uint32]otelMetrics.Int64Counter), - }, nil -} - -// Start starts the scribe. This works by starting a backfill and recording what the -// current block, which it will backfill to. Then, each chain will listen for new block -// heights and backfill to that height. -// -//nolint:cyclop -func (s Scribe) Start(ctx context.Context) error { - g, groupCtx := errgroup.WithContext(ctx) - - for i := range s.config.Chains { - chainConfig := s.config.Chains[i] - chainID := chainConfig.ChainID - reorgMeter, err := s.handler.Metrics().NewCounter(fmt.Sprintf("scribe_reorg_meter_%d", chainID), "reorg_counter", "a reorg meter", "reorg events") - if err != nil { - return fmt.Errorf("error creating otel counter %w", err) - } - s.reorgMeters[chainID] = reorgMeter - // Set default confirmation values - if chainConfig.ConfirmationConfig.RequiredConfirmations == 0 || - chainConfig.ConfirmationConfig.ConfirmationThreshold == 0 || - chainConfig.ConfirmationConfig.ConfirmationRefreshRate == 0 { - chainConfig.ConfirmationConfig = config.ConfirmationConfig{ - RequiredConfirmations: 250, - ConfirmationThreshold: 100, - ConfirmationRefreshRate: 5, - } - } - confirmationRefreshRateTime := time.Duration(chainConfig.ConfirmationConfig.ConfirmationRefreshRate) * time.Second - - // Livefill the chains - g.Go(func() error { - err := s.scribeBackfiller.ChainBackfillers[chainID].Backfill(ctx, nil, true) - if err != nil { - return fmt.Errorf("could not backfill: %w", err) - } - return nil - }) - - // Check confirmations - g.Go(func() error { - if !checkFinality { - return nil - } - b := &backoff.Backoff{ - Factor: 2, - Jitter: true, - Min: 1 * time.Second, - Max: 10 * time.Second, - } - timeout := confirmationRefreshRateTime - for { - select { - case <-groupCtx.Done(): - logger.Warnf("scribe for chain %d shutting down", chainConfig.ChainID) - return nil - case <-time.After(timeout): - err := s.confirmBlocks(groupCtx, chainConfig) - if err != nil { - timeout = b.Duration() - logger.Warnf("could not confirm blocks on chain %d, retrying: %v", chainConfig.ChainID, err) - - continue - } - - // Set the timeout to the confirmation refresh rate. - timeout = confirmationRefreshRateTime - logger.Infof("processed blocks chain %d, continuing to confirm blocks", chainConfig.ChainID) - b.Reset() - } - } - }) - } - if err := g.Wait(); err != nil { - return fmt.Errorf("livefill failed: %w", err) - } - - return nil -} - -// confirmBlocks checks for reorgs with data stored in the scribe database -// 0. Every few seconds (depending on ConfirmationRefreshRate in the config), the confirmBlocks function is called. -// 1. First, the head block and the last confirmed block is retrieved. There is no "backfill" capability for reorgs. -// it is suggested you reindex that range you want to confirm if it is far from the head. -// 2. Block hashes for the blocks since the last confirmed block up to (latest block - the confirmation threshold) -// are batch requested. -// 3. These hashes are used to query the databases for receipts that do not have those block hashes. -// 4. The returned receipts have their blocks deleted and re-backfilled. -// 5. The entire range of blocks is then confirmed and last confirmed is updated. -// -//nolint:gocognit, cyclop -func (s Scribe) confirmBlocks(ctx context.Context, chainConfig config.ChainConfig) error { - chainID := chainConfig.ChainID - requiredConfirmations := chainConfig.ConfirmationConfig.RequiredConfirmations - getBlockBatchAmount := chainConfig.GetBlockBatchAmount - if getBlockBatchAmount == 0 { - getBlockBatchAmount = 25 - } - confirmationThreshold := chainConfig.ConfirmationConfig.ConfirmationThreshold - - latestBlock, err := s.clients[chainID][0].BlockNumber(ctx) - if err != nil { - return fmt.Errorf("could not get current block number: %w", err) - } - lastConfirmedBlock, err := s.eventDB.RetrieveLastConfirmedBlock(ctx, chainID) - if err != nil { - return fmt.Errorf("could not retrieve last confirmed block: %w", err) - } - - // If not enough blocks have passed since the last confirmed block, the function will terminate. - if confirmationThreshold > latestBlock-lastConfirmedBlock { - return nil - } - - // To prevent getting confirmations for anything more than 1000 blocks in the past (preventing backfilling - // confirmations AKA checking every single hash for reorg) - if latestBlock-lastConfirmedBlock > 1000 { - lastConfirmedBlock = latestBlock - 1000 - } - - confirmTo := latestBlock - confirmationThreshold - confirmFrom := lastConfirmedBlock + 1 - blockHashes, err := GetBlockHashes(ctx, s.clients[chainID][0], confirmFrom, confirmTo, getBlockBatchAmount) - if err != nil { - return fmt.Errorf("could not get blockHashes on chain %d: %w", chainID, err) - } - // get receipts emitted on invalid block hashes - invalidReceipts, err := s.eventDB.RetrieveReceiptsWithStaleBlockHash(ctx, chainID, blockHashes, confirmFrom, confirmTo) - if err != nil { - return fmt.Errorf("could not get invalid receipts from db: %w", err) - } - - // A cache for receipts to prevent multiple rebackfills. - cache, err := lru.New(int(confirmTo - confirmFrom)) - if err != nil { - return fmt.Errorf("could not access cache: %w", err) - } - - for i := range invalidReceipts { - receipt := invalidReceipts[i] - cacheKey := fmt.Sprintf("%s_%d", receipt.BlockHash, receipt.BlockNumber) - - // Skip this receipt if it is part of a block that already has been re-backfilled - if _, ok := cache.Get(cacheKey); ok { - continue - } - g, groupCtx := errgroup.WithContext(ctx) - - g.Go(func() error { - err := s.eventDB.DeleteLogsForBlockHash(groupCtx, receipt.BlockHash, chainID) - - if err != nil { - logger.Errorf(" [LIVEFILL] could not delete logs %d chain: %d, %v", receipt.BlockHash, chainID, err) - - return fmt.Errorf("could not delete logs: %w", err) - } - - return nil - }) - - g.Go(func() error { - err := s.eventDB.DeleteReceiptsForBlockHash(groupCtx, chainID, receipt.BlockHash) - if err != nil { - logger.Errorf(" [LIVEFILL] could not delete receipts %d chain: %d, %v", receipt.BlockHash, chainID, err) - - return fmt.Errorf("could not delete receipts: %w", err) - } - - return nil - }) - - g.Go(func() error { - err := s.eventDB.DeleteEthTxsForBlockHash(groupCtx, receipt.BlockHash, chainID) - if err != nil { - logger.Errorf(" [LIVEFILL] could not delete eth txs %d chain: %d, %v", receipt.BlockHash, chainID, err) - - return fmt.Errorf("could not delete eth txs: %w", err) - } - - return nil - }) - - if err := g.Wait(); err != nil { - logger.Errorf(" [LIVEFILL] could not delete block %d chain: %d, block: %d, %v", latestBlock-uint64(requiredConfirmations), chainID, i, err) - - return fmt.Errorf("could not delete block: %w", err) - } - blockNumber := receipt.BlockNumber.Uint64() - err = s.scribeBackfiller.ChainBackfillers[chainID].Backfill(ctx, &blockNumber, false) - if err != nil { - logger.Errorf(" [LIVEFILL] could not backfill %d chain: %d, block: %d, %v", latestBlock-uint64(requiredConfirmations), chainID, i, err) - - return fmt.Errorf("could not backfill: %w", err) - } - - cache.Add(cacheKey, true) - - // Add to meter - s.reorgMeters[chainID].Add(ctx, 1, otelMetrics.WithAttributeSet( - attribute.NewSet(attribute.Int64("block_number", int64(blockNumber)), attribute.Int64("chain_id", int64(chainID)))), - ) - } - - // update items in the database as confirmed - err = s.confirmToBlockNumber(ctx, chainID, confirmFrom, confirmTo) - if err != nil { - return fmt.Errorf("could not confirm items in database after backfilling %w", err) - } - return nil -} - -// GetBlockHashes gets an array of block hashes from a range of blocks. -func GetBlockHashes(ctx context.Context, backend backfill.ScribeBackend, startBlock, endBlock uint64, getBlockBatchAmount int) ([]string, error) { - iterator := util.NewChunkIterator(big.NewInt(int64(startBlock)), big.NewInt(int64(endBlock)), getBlockBatchAmount-1, true) - blockRange := iterator.NextChunk() - var hashes []string - for blockRange != nil { - blockHashes, err := backfill.BlockHashesInRange(ctx, backend, blockRange.StartBlock.Uint64(), blockRange.EndBlock.Uint64()) - if err != nil { - logger.Errorf("[LIVEFILL] could not get block hashes in range %d to %d, %v", startBlock, endBlock, err) - // TODO potentially add a retry here - return nil, fmt.Errorf("could not get block hashes in batch: %w", err) - } - itr := blockHashes.Iterator() - for !itr.Done() { - _, hash, _ := itr.Next() - hashes = append(hashes, hash) - } - blockRange = iterator.NextChunk() - } - return hashes, nil -} - -func (s Scribe) confirmToBlockNumber(ctx context.Context, chainID uint32, fromBlock uint64, toBlock uint64) error { - g, groupCtx := errgroup.WithContext(ctx) - - g.Go(func() error { - err := s.eventDB.ConfirmLogsInRange(groupCtx, fromBlock, toBlock, chainID) - if err != nil { - logger.Errorf(" [LIVEFILL] confirmToBlockNumber() could not confirm logs fromBlock: %d toBlock: %d chain: %d, %v", fromBlock, toBlock, chainID, err) - - return fmt.Errorf("could not confirm log: %w", err) - } - - return nil - }) - g.Go(func() error { - err := s.eventDB.ConfirmReceiptsInRange(groupCtx, fromBlock, toBlock, chainID) - - if err != nil { - logger.Errorf(" [LIVEFILL] confirmToBlockNumber() could not confirm receipts fromBlock: %d toBlock: %d chain: %d, %v", fromBlock, toBlock, chainID, err) - - return fmt.Errorf("could not confirm receipt: %w", err) - } - - return nil - }) - - g.Go(func() error { - err := s.eventDB.ConfirmEthTxsInRange(groupCtx, fromBlock, toBlock, chainID) - if err != nil { - logger.Errorf(" [LIVEFILL] confirmToBlockNumber() could not confirm txs fromBlock: %d toBlock: %d chain: %d, %v", fromBlock, toBlock, chainID, err) - - return fmt.Errorf("could not confirm transaction: %w", err) - } - - return nil - }) - - if err := g.Wait(); err != nil { - logger.Errorf(" [LIVEFILL] confirmToBlockNumber() could not confirm fromBlock: %d toBlock: %d chain: %d, %v", fromBlock, toBlock, chainID, err) - - return fmt.Errorf("could not confirm blocks: %w", err) - } - - err := s.eventDB.StoreLastConfirmedBlock(ctx, chainID, toBlock) - if err != nil { - logger.Errorf(" [LIVEFILL] confirmToBlockNumber() could not store last confirmed fromBlock: %d toBlock: %d chain: %d, %v", fromBlock, toBlock, chainID, err) - - return fmt.Errorf("could not store last confirmed block: %w", err) - } - - return nil -} diff --git a/services/scribe/node/scribe_test.go b/services/scribe/node/scribe_test.go deleted file mode 100644 index 463a1faec3..0000000000 --- a/services/scribe/node/scribe_test.go +++ /dev/null @@ -1,753 +0,0 @@ -package node_test - -import ( - "context" - "encoding/json" - "fmt" - "github.com/brianvoe/gofakeit/v6" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" - "github.com/ipfs/go-log" - "github.com/jpillora/backoff" - . "github.com/stretchr/testify/assert" - "github.com/synapsecns/sanguine/ethergo/backends" - "github.com/synapsecns/sanguine/ethergo/backends/geth" - "github.com/synapsecns/sanguine/ethergo/contracts" - "github.com/synapsecns/sanguine/services/omnirpc/testhelper" - "github.com/synapsecns/sanguine/services/scribe/backfill" - "github.com/synapsecns/sanguine/services/scribe/config" - "github.com/synapsecns/sanguine/services/scribe/db" - "github.com/synapsecns/sanguine/services/scribe/db/datastore/sql/base" - "github.com/synapsecns/sanguine/services/scribe/node" - "github.com/synapsecns/sanguine/services/scribe/testutil" - "github.com/synapsecns/sanguine/services/scribe/testutil/testcontract" - "math/big" - "net/http" - "os" - "sync" - "time" -) - -// TODO combine these functions with backfill/backend as well as other tests - -// ReachBlockHeight reaches a block height on a backend. -func (l *LiveSuite) ReachBlockHeight(ctx context.Context, backend backends.SimulatedTestBackend, desiredBlockHeight uint64) { - i := 0 - for { - select { - case <-ctx.Done(): - l.T().Log(ctx.Err()) - return - default: - // continue - } - i++ - backend.FundAccount(ctx, common.BigToAddress(big.NewInt(int64(i))), *big.NewInt(params.Wei)) - - latestBlock, err := backend.BlockNumber(ctx) - Nil(l.T(), err) - - if latestBlock >= desiredBlockHeight { - return - } - } -} - -// startOmnirpcServer boots an omnirpc server for an rpc address. -// the url for this rpc is returned. -func (l *LiveSuite) startOmnirpcServer(ctx context.Context, backend backends.SimulatedTestBackend) string { - baseHost := testhelper.NewOmnirpcServer(ctx, l.T(), backend) - return testhelper.GetURL(baseHost, backend) -} - -// ReachBlockHeight reaches a block height on a backend. -func (l *LiveSuite) PopuluateWithLogs(ctx context.Context, backend backends.SimulatedTestBackend, desiredBlockHeight uint64) common.Address { - i := 0 - var address common.Address - for { - select { - case <-ctx.Done(): - l.T().Log(ctx.Err()) - return address - default: - // continue - } - i++ - backend.FundAccount(ctx, common.BigToAddress(big.NewInt(int64(i))), *big.NewInt(params.Wei)) - testContract, testRef := l.manager.GetTestContract(l.GetTestContext(), backend) - address = testContract.Address() - transactOpts := backend.GetTxContext(l.GetTestContext(), nil) - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(l.T(), err) - backend.WaitForConfirmation(l.GetTestContext(), tx) - - latestBlock, err := backend.BlockNumber(ctx) - Nil(l.T(), err) - - if latestBlock >= desiredBlockHeight { - return address - } - } -} -func (l *LiveSuite) TestGetBlockHashes() { - testBackend := geth.NewEmbeddedBackend(l.GetTestContext(), l.T()) - - var wg sync.WaitGroup - wg.Add(2) - - const desiredBlockHeight = 16 - - go func() { - defer wg.Done() - l.ReachBlockHeight(l.GetTestContext(), testBackend, desiredBlockHeight) - }() - - var host string - go func() { - defer wg.Done() - host = l.startOmnirpcServer(l.GetTestContext(), testBackend) - }() - - wg.Wait() - - scribeBackend, err := backfill.DialBackend(l.GetTestContext(), host, l.metrics) - Nil(l.T(), err) - hashes, err := node.GetBlockHashes(l.GetTestContext(), scribeBackend, 1, desiredBlockHeight, 3) - Nil(l.T(), err) - - // Check that the number of hashes is as expected - Equal(l.T(), desiredBlockHeight, len(hashes)) - - // use to make sure we don't double use values - hashSet := make(map[string]bool) - - for _, hash := range hashes { - _, ok := hashSet[hash] - False(l.T(), ok, "hash %s appears at least twice", hash) - hashSet[hash] = true - } -} - -// TestLive tests live recording of events. -func (l LiveSuite) TestLive() { - if os.Getenv("CI") != "" { - l.T().Skip("Test flake: 20 sec of livefilling may fail on CI") - } - chainID := gofakeit.Uint32() - // We need to set up multiple deploy managers, one for each contract. We will use - // b.manager for the first contract, and create a new ones for the next two. - managerB := testutil.NewDeployManager(l.T()) - managerC := testutil.NewDeployManager(l.T()) - // Get simulated blockchain, deploy three test contracts, and set up test variables. - simulatedChain := geth.NewEmbeddedBackendForChainID(l.GetTestContext(), l.T(), big.NewInt(int64(chainID))) - simulatedClient, err := backfill.DialBackend(l.GetTestContext(), simulatedChain.RPCAddress(), l.metrics) - Nil(l.T(), err) - - simulatedChain.FundAccount(l.GetTestContext(), l.wallet.Address(), *big.NewInt(params.Ether)) - testContractA, testRefA := l.manager.GetTestContract(l.GetTestContext(), simulatedChain) - testContractB, testRefB := managerB.GetTestContract(l.GetTestContext(), simulatedChain) - testContractC, testRefC := managerC.GetTestContract(l.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(l.GetTestContext(), nil) - // Put the contracts into a slice so we can iterate over them. - contracts := []contracts.DeployedContract{testContractA, testContractB, testContractC} - // Put the test refs into a slice so we can iterate over them. - testRefs := []*testcontract.TestContractRef{testRefA, testRefB, testRefC} - - // Set up the config. - contractConfigs := config.ContractConfigs{} - for _, contract := range contracts { - contractConfigs = append(contractConfigs, config.ContractConfig{ - Address: contract.Address().String(), - StartBlock: 0, - }) - } - chainConfig := config.ChainConfig{ - ChainID: chainID, - Contracts: contractConfigs, - GetBlockBatchAmount: 1, - GetLogsBatchAmount: 2, - } - scribeConfig := config.Config{ - Chains: []config.ChainConfig{chainConfig}, - } - - clients := make(map[uint32][]backfill.ScribeBackend) - clients[chainID] = append(clients[chainID], simulatedClient) - clients[chainID] = append(clients[chainID], simulatedClient) - - // Set up the scribe. - scribe, err := node.NewScribe(l.testDB, clients, scribeConfig, l.metrics) - Nil(l.T(), err) - - for _, testRef := range testRefs { - tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(l.T(), err) - simulatedChain.WaitForConfirmation(l.GetTestContext(), tx) - tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) - Nil(l.T(), err) - simulatedChain.WaitForConfirmation(l.GetTestContext(), tx) - tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) - Nil(l.T(), err) - simulatedChain.WaitForConfirmation(l.GetTestContext(), tx) - } - - // Livefill for a minute. - ctx, cancel := context.WithTimeout(l.GetTestContext(), 20*time.Second) - defer cancel() - _ = scribe.Start(ctx) - - // Check that the events were recorded. - for _, contract := range contracts { - // Check the storage of logs. - logFilter := db.LogFilter{ - ChainID: chainConfig.ChainID, - ContractAddress: contract.Address().String(), - } - logs, err := l.testDB.RetrieveLogsWithFilter(l.GetTestContext(), logFilter, 1) - Nil(l.T(), err) - // There should be 4 logs. One from `EmitEventA`, one from `EmitEventB`, and two - // from `EmitEventAandB`. - Equal(l.T(), 4, len(logs)) - } - // Check the storage of receipts. - receiptFilter := db.ReceiptFilter{ - ChainID: chainConfig.ChainID, - } - receipts, err := l.testDB.RetrieveReceiptsWithFilter(l.GetTestContext(), receiptFilter, 1) - Nil(l.T(), err) - // There should be 9 receipts. One from `EmitEventA`, one from `EmitEventB`, and - // one from `EmitEventAandB`, for each contract. - Equal(l.T(), 9, len(receipts)) -} - -func (l LiveSuite) TestConfirmationSimple() { - if os.Getenv("CI") != "" { - l.T().Skip("Test flake: 20 seconds of livefilling may fail on CI") - } - chainID := gofakeit.Uint32() - - // Emit some events on the simulated blockchain. - simulatedChain := geth.NewEmbeddedBackendForChainID(l.GetTestContext(), l.T(), big.NewInt(int64(chainID))) - simulatedClient, err := backfill.DialBackend(l.GetTestContext(), simulatedChain.RPCAddress(), l.metrics) - Nil(l.T(), err) - - simulatedChain.FundAccount(l.GetTestContext(), l.wallet.Address(), *big.NewInt(params.Ether)) - testContract, testRef := l.manager.GetTestContract(l.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(l.GetTestContext(), nil) - - // Set up the config. - contractConfig := config.ContractConfig{ - Address: testContract.Address().String(), - StartBlock: 0, - } - chainConfig := config.ChainConfig{ - ChainID: chainID, - Contracts: []config.ContractConfig{contractConfig}, - ConfirmationConfig: config.ConfirmationConfig{ - RequiredConfirmations: 100, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, - } - scribeConfig := config.Config{ - Chains: []config.ChainConfig{chainConfig}, - ConfirmationRefreshRate: 1, - } - - clients := make(map[uint32][]backfill.ScribeBackend) - clients[chainID] = append(clients[chainID], simulatedClient) - clients[chainID] = append(clients[chainID], simulatedClient) - - // Set up the scribe. - scribe, err := node.NewScribe(l.testDB, clients, scribeConfig, l.metrics) - Nil(l.T(), err) - - // Emit 5 events. - for i := 0; i < 5; i++ { - tx, err := testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(l.T(), err) - simulatedChain.WaitForConfirmation(l.GetTestContext(), tx) - } - // Process the events, end livefilling after 20 seconds. - ctx, cancel := context.WithTimeout(l.GetTestContext(), 20*time.Second) - defer cancel() - _ = scribe.Start(ctx) - - // Check if values are confirmed - logFilter := db.LogFilter{ - ChainID: chainConfig.ChainID, - ContractAddress: testContract.Address().String(), - Confirmed: true, - } - logs, err := l.testDB.RetrieveLogsWithFilter(l.GetTestContext(), logFilter, 1) - Nil(l.T(), err) - Equal(l.T(), 8, len(logs)) - receiptFilter := db.ReceiptFilter{ - ChainID: chainConfig.ChainID, - Confirmed: true, - } - receipts, err := l.testDB.RetrieveReceiptsWithFilter(l.GetTestContext(), receiptFilter, 1) - Nil(l.T(), err) - Equal(l.T(), 4, len(receipts)) - txFilter := db.EthTxFilter{ - ChainID: chainConfig.ChainID, - Confirmed: true, - } - - txs, err := l.testDB.RetrieveEthTxsWithFilter(l.GetTestContext(), txFilter, 1) - Nil(l.T(), err) - Equal(l.T(), 4, len(txs)) - - lastConfirmedBlock, err := l.testDB.RetrieveLastConfirmedBlock(l.GetTestContext(), chainConfig.ChainID) - Nil(l.T(), err) - Equal(l.T(), uint64(8), lastConfirmedBlock) - - lastBlockIndexed, err := l.testDB.RetrieveLastIndexed(l.GetTestContext(), testContract.Address(), chainConfig.ChainID) - Nil(l.T(), err) - Equal(l.T(), uint64(9), lastBlockIndexed) -} - -func (l LiveSuite) TestRequiredConfirmationRemAndAdd() { - if os.Getenv("CI") != "" { - l.T().Skip("Test flake: 20 seconds of livefilling may fail on CI") - } - chainID := gofakeit.Uint32() - - // Emit some events on the simulated blockchain. - simulatedChain := geth.NewEmbeddedBackendForChainID(l.GetTestContext(), l.T(), big.NewInt(int64(chainID))) - simulatedClient, err := backfill.DialBackend(l.GetTestContext(), simulatedChain.RPCAddress(), l.metrics) - Nil(l.T(), err) - - simulatedChain.FundAccount(l.GetTestContext(), l.wallet.Address(), *big.NewInt(params.Ether)) - testContract, testRef := l.manager.GetTestContract(l.GetTestContext(), simulatedChain) - transactOpts := simulatedChain.GetTxContext(l.GetTestContext(), nil) - - // Set up the config. - contractConfig := config.ContractConfig{ - Address: testContract.Address().String(), - StartBlock: 0, - } - chainConfig := config.ChainConfig{ - ChainID: chainID, - Contracts: []config.ContractConfig{contractConfig}, - ConfirmationConfig: config.ConfirmationConfig{ - RequiredConfirmations: 100, - ConfirmationThreshold: 1, - ConfirmationRefreshRate: 1, - }, - } - scribeConfig := config.Config{ - Chains: []config.ChainConfig{chainConfig}, - ConfirmationRefreshRate: 1, - } - - clients := make(map[uint32][]backfill.ScribeBackend) - clients[chainID] = append(clients[chainID], simulatedClient) - clients[chainID] = append(clients[chainID], simulatedClient) - - // Set up scribe. - scribe, err := node.NewScribe(l.testDB, clients, scribeConfig, l.metrics) - Nil(l.T(), err) - - for i := 0; i < 5; i++ { - tx, err := testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) - Nil(l.T(), err) - simulatedChain.WaitForConfirmation(l.GetTestContext(), tx) - } - // Process the events, end livefilling after 20 seconds. - ctx, cancel := context.WithTimeout(l.GetTestContext(), 20*time.Second) - defer cancel() - - invalidBlockHash := common.BigToHash(big.NewInt(11111)) - invalidReceipt := types.Receipt{ - ContractAddress: testContract.Address(), - BlockHash: invalidBlockHash, - BlockNumber: big.NewInt(3), - } - receiptFilter := db.ReceiptFilter{ - ChainID: chainConfig.ChainID, - } - // Storing an invalid receipt with a nonsense block hash. The proper behavior will be to evict/rm this receipt upon - // confirmation checking and re-backfill the block. - err = l.testDB.StoreReceipt(l.GetTestContext(), chainConfig.ChainID, invalidReceipt) - Nil(l.T(), err) - startingReceipts, err := l.testDB.RetrieveReceiptsWithFilter(l.GetTestContext(), receiptFilter, 1) - Nil(l.T(), err) - Equal(l.T(), 1, len(startingReceipts)) - - _ = scribe.Start(ctx) - - // Check if values are confirmed - logFilter := db.LogFilter{ - ChainID: chainConfig.ChainID, - ContractAddress: testContract.Address().String(), - Confirmed: true, - } - logs, err := l.testDB.RetrieveLogsWithFilter(l.GetTestContext(), logFilter, 1) - Nil(l.T(), err) - Equal(l.T(), 8, len(logs)) - - receipts, err := l.testDB.RetrieveReceiptsWithFilter(l.GetTestContext(), receiptFilter, 1) - Nil(l.T(), err) - for _, receipt := range receipts { - NotEqual(l.T(), receipt.BlockHash, invalidBlockHash) - } - Equal(l.T(), 5, len(receipts)) - - txFilter := db.EthTxFilter{ - ChainID: chainConfig.ChainID, - Confirmed: true, - } - txs, err := l.testDB.RetrieveEthTxsWithFilter(l.GetTestContext(), txFilter, 1) - Nil(l.T(), err) - Equal(l.T(), 4, len(txs)) - - lastConfirmedBlock, err := l.testDB.RetrieveLastConfirmedBlock(l.GetTestContext(), chainConfig.ChainID) - Nil(l.T(), err) - Equal(l.T(), 9-chainConfig.ConfirmationConfig.ConfirmationThreshold, lastConfirmedBlock) - - lastBlockIndexed, err := l.testDB.RetrieveLastIndexed(l.GetTestContext(), testContract.Address(), chainConfig.ChainID) - Nil(l.T(), err) - Equal(l.T(), uint64(9), lastBlockIndexed) -} - -// TestLivefillParity runs livefill on certain prod chains. Then it checks parity with that chain's block explorer API. -func (l LiveSuite) TestLivefillParity() { - if os.Getenv("CI") != "" { - l.T().Skip("Network test flake") - } - // ethRPCURL := "https://1rpc.io/eth" - // arbRPCURL := "https://endpoints.omniatech.io/v1/arbitrum/one/public" - // maticRPCURL := "https://poly-rpc.gateway.pokt.network" - // avaxRPCURL := "https://avalanche.public-rpc.com" - - ethRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/1" - arbRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/42161" - maticRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/137" - avaxRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/43114" - bscRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/56" - - blockRange := uint64(1000) - - ethClient, err := backfill.DialBackend(l.GetTestContext(), ethRPCURL, l.metrics) - Nil(l.T(), err) - arbClient, err := backfill.DialBackend(l.GetTestContext(), arbRPCURL, l.metrics) - Nil(l.T(), err) - maticClient, err := backfill.DialBackend(l.GetTestContext(), maticRPCURL, l.metrics) - Nil(l.T(), err) - avaxClient, err := backfill.DialBackend(l.GetTestContext(), avaxRPCURL, l.metrics) - Nil(l.T(), err) - bscClient, err := backfill.DialBackend(l.GetTestContext(), bscRPCURL, l.metrics) - Nil(l.T(), err) - - ethID := uint32(1) - bscID := uint32(56) - arbID := uint32(42161) - maticID := uint32(137) - avaxID := uint32(43114) - chains := []uint32{ethID, bscID, arbID, maticID, avaxID} - - // Get the current block for each chain. - ethCurrentBlock, err := ethClient.BlockNumber(l.GetTestContext()) - Nil(l.T(), err) - arbCurrentBlock, err := arbClient.BlockNumber(l.GetTestContext()) - Nil(l.T(), err) - maticCurrentBlock, err := maticClient.BlockNumber(l.GetTestContext()) - Nil(l.T(), err) - avaxCurrentBlock, err := avaxClient.BlockNumber(l.GetTestContext()) - Nil(l.T(), err) - bscCurrentBlock, err := bscClient.BlockNumber(l.GetTestContext()) - Nil(l.T(), err) - - latestBlocks := map[uint32]uint64{ - ethID: ethCurrentBlock, - arbID: arbCurrentBlock, - maticID: maticCurrentBlock, - avaxID: avaxCurrentBlock, - bscID: bscCurrentBlock, - } - clients := map[uint32][]backfill.ScribeBackend{ - ethID: {ethClient, ethClient}, - bscID: {bscClient, bscClient}, - arbID: {arbClient, arbClient}, - maticID: {maticClient, maticClient}, - avaxID: {avaxClient, avaxClient}, - } - - apiURLs := map[uint32]string{ - ethID: "https://api.etherscan.io/api", - arbID: "https://api.arbiscan.io/api", - avaxID: "https://api.snowtrace.io/api", - bscID: "https://api.bscscan.com/api", - maticID: "https://api.polygonscan.com/api", - } - scribeConfig := config.Config{ - RefreshRate: 1, - Chains: []config.ChainConfig{ - { - ChainID: ethID, - ConfirmationConfig: config.ConfirmationConfig{ - ConfirmationThreshold: 10, - ConfirmationRefreshRate: 10, - RequiredConfirmations: 1, - }, - GetLogsRange: 1000, - GetLogsBatchAmount: 3, - GetBlockBatchAmount: 10, - ConcurrencyThreshold: 20000, - Contracts: []config.ContractConfig{ - { - Address: "0x2796317b0fF8538F253012862c06787Adfb8cEb6", - StartBlock: ethCurrentBlock - blockRange, - }, - { - Address: "0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8", - StartBlock: ethCurrentBlock - blockRange, - }, - }, - }, - { - ChainID: bscID, - ConfirmationConfig: config.ConfirmationConfig{ - ConfirmationThreshold: 10, - ConfirmationRefreshRate: 10, - RequiredConfirmations: 1, - }, - GetLogsRange: 256, - GetLogsBatchAmount: 2, - ConcurrencyThreshold: 256, - GetBlockBatchAmount: 10, - Contracts: []config.ContractConfig{ - { - Address: "0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13", - StartBlock: bscCurrentBlock - blockRange, - }, - { - Address: "0x930d001b7efb225613aC7F35911c52Ac9E111Fa9", - StartBlock: bscCurrentBlock - blockRange, - }, - }, - }, - { - ChainID: arbID, - ConfirmationConfig: config.ConfirmationConfig{ - ConfirmationThreshold: 10, - ConfirmationRefreshRate: 10, - RequiredConfirmations: 1, - }, - GetLogsRange: 1024, - GetLogsBatchAmount: 2, - ConcurrencyThreshold: 20000, - GetBlockBatchAmount: 10, - - Contracts: []config.ContractConfig{ - { - Address: "0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9", - StartBlock: arbCurrentBlock - blockRange, - }, - { - Address: "0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40", - StartBlock: arbCurrentBlock - blockRange, - }, - }, - }, - { - ChainID: maticID, - ConfirmationConfig: config.ConfirmationConfig{ - ConfirmationThreshold: 10, - ConfirmationRefreshRate: 10, - RequiredConfirmations: 1, - }, - GetLogsRange: 1000, - GetLogsBatchAmount: 2, - GetBlockBatchAmount: 10, - ConcurrencyThreshold: 1001, - Contracts: []config.ContractConfig{ - { - Address: "0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280", - StartBlock: maticCurrentBlock - blockRange, - }, - { - Address: "0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5", - StartBlock: maticCurrentBlock - blockRange, - }, - }, - }, - { - ChainID: avaxID, - ConfirmationConfig: config.ConfirmationConfig{ - ConfirmationThreshold: 10, - ConfirmationRefreshRate: 10, - RequiredConfirmations: 1, - }, - GetLogsRange: 256, - GetLogsBatchAmount: 1, - GetBlockBatchAmount: 10, - - ConcurrencyThreshold: 20000, - Contracts: []config.ContractConfig{ - { - Address: "0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE", - StartBlock: avaxCurrentBlock - blockRange, - }, - { - Address: "0x77a7e60555bC18B4Be44C181b2575eee46212d44", - StartBlock: avaxCurrentBlock - blockRange, - }, - }, - }, - }, - } - - scribe, err := node.NewScribe(l.testDB, clients, scribeConfig, l.metrics) - Nil(l.T(), err) - - killableContext, cancel := context.WithCancel(l.GetTestContext()) - - go func() { - _ = scribe.Start(killableContext) - }() - - doneChan := make(chan bool, len(chains)) - - for i := range chains { - go func(index int) { - for { - allContractsBackfilled := true - chain := scribeConfig.Chains[index] - for _, contract := range chain.Contracts { - currentBlock, err := l.testDB.RetrieveLastIndexed(l.GetTestContext(), common.HexToAddress(contract.Address), chain.ChainID) - Nil(l.T(), err) - if latestBlocks[chain.ChainID] > currentBlock { - allContractsBackfilled = false - } - } - if allContractsBackfilled { - doneChan <- true - return - } - time.Sleep(time.Second) - } - }(i) - } - - for range chains { - <-doneChan - } - cancel() - - for i := range chains { - chain := scribeConfig.Chains[i] - for _, contract := range chain.Contracts { - logFilter := db.LogFilter{ - ChainID: chains[i], - ContractAddress: contract.Address, - } - fromBlock := latestBlocks[chains[i]] - blockRange - toBlock := latestBlocks[chains[i]] - dbLogCount, err := getLogAmount(l.GetTestContext(), l.testDB, logFilter, fromBlock, toBlock) - Nil(l.T(), err) - - explorerLogCount, err := getLogs(l.GetTestContext(), contract.Address, fromBlock, toBlock, apiURLs[chain.ChainID]) - Nil(l.T(), err) - Equal(l.T(), dbLogCount, explorerLogCount) - } - } -} - -func createHTTPClient() *http.Client { - return &http.Client{ - Timeout: 10 * time.Second, - Transport: &http.Transport{ - ResponseHeaderTimeout: 10 * time.Second, - }, - } -} - -func processBatch(ctx context.Context, client *http.Client, url string) (int, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return 0, fmt.Errorf("error getting data: %w", err) - } - resRaw, err := client.Do(req) - if err != nil { - return 0, fmt.Errorf("could not get data from explorer %w", err) - } - - var decodedRes map[string]json.RawMessage - if err := json.NewDecoder(resRaw.Body).Decode(&decodedRes); err != nil { - return 0, fmt.Errorf("error decoding response: %w", err) - } - - var resultSlice []map[string]interface{} - if err := json.Unmarshal(decodedRes["result"], &resultSlice); err != nil { - return 0, fmt.Errorf("error unmarshaling result: %w", err) - } - - if err = resRaw.Body.Close(); err != nil { - log.Logger("synapse-scribe-node-test").Errorf("could not close response body: %v", err) - } - return len(resultSlice), nil -} - -func getLogs(ctx context.Context, contractAddress string, fromBlock uint64, toBlock uint64, apiURL string) (int, error) { - blockRange := toBlock - fromBlock - batchSize := uint64(600) - numBatches := blockRange/batchSize + 1 - client := createHTTPClient() - totalResults := 0 - - for i := uint64(0); i < numBatches; i++ { - startBlock := fromBlock + i*batchSize - endBlock := startBlock + batchSize - 1 - if endBlock > toBlock { - endBlock = toBlock - } - url := fmt.Sprintf("%s?module=logs&action=getLogs&address=%s&fromBlock=%d&toBlock=%d&page=1", - apiURL, contractAddress, startBlock, endBlock) - b := &backoff.Backoff{ - Factor: 2, - Jitter: true, - Min: 10 * time.Millisecond, - Max: 1 * time.Second, - } - timeout := time.Duration(0) - - RETRY: - select { - case <-ctx.Done(): - return 0, fmt.Errorf("context canceled: %w", ctx.Err()) - case <-time.After(timeout): - resultCount, err := processBatch(ctx, client, url) - if err != nil { - timeout = b.Duration() - goto RETRY - } - totalResults += resultCount - } - - if i < numBatches-1 { - time.Sleep(1 * time.Second) - } - } - - return totalResults, nil -} - -func getLogAmount(ctx context.Context, db db.EventDB, filter db.LogFilter, startBlock uint64, endBlock uint64) (int, error) { - page := 1 - var retrievedLogs []*types.Log - for { - logs, err := db.RetrieveLogsInRangeAsc(ctx, filter, startBlock, endBlock, page) - if err != nil { - return 0, fmt.Errorf("failure while retreiving logs from database %w", err) - } - retrievedLogs = append(retrievedLogs, logs...) - if len(logs) < base.PageSize { - break - } - page++ - } - return len(retrievedLogs), nil -} diff --git a/services/scribe/node/suite_test.go b/services/scribe/node/suite_test.go deleted file mode 100644 index 326b98e999..0000000000 --- a/services/scribe/node/suite_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package node_test - -import ( - "github.com/synapsecns/sanguine/core/metrics" - "github.com/synapsecns/sanguine/core/metrics/localmetrics" - "github.com/synapsecns/sanguine/services/scribe/metadata" - "testing" - "time" - - "github.com/Flaque/filet" - . "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "github.com/synapsecns/sanguine/core/testsuite" - "github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner" - "github.com/synapsecns/sanguine/ethergo/signer/wallet" - "github.com/synapsecns/sanguine/services/scribe/db" - "github.com/synapsecns/sanguine/services/scribe/db/datastore/sql/sqlite" - "github.com/synapsecns/sanguine/services/scribe/testutil" -) - -type LiveSuite struct { - *testsuite.TestSuite - testDB db.EventDB - manager *testutil.DeployManager - wallet wallet.Wallet - signer *localsigner.Signer - metrics metrics.Handler -} - -// NewLiveSuite creates a new live test suite. -func NewLiveSuite(tb testing.TB) *LiveSuite { - tb.Helper() - return &LiveSuite{ - TestSuite: testsuite.NewTestSuite(tb), - } -} - -func (l *LiveSuite) SetupSuite() { - l.TestSuite.SetupSuite() - localmetrics.SetupTestJaeger(l.GetSuiteContext(), l.T()) - var err error - - l.metrics, err = metrics.NewByType(l.GetSuiteContext(), metadata.BuildInfo(), metrics.Jaeger) - l.Require().Nil(err) -} - -func (l *LiveSuite) SetupTest() { - l.TestSuite.SetupTest() - l.SetTestTimeout(time.Minute * 3) - sqliteStore, err := sqlite.NewSqliteStore(l.GetTestContext(), filet.TmpDir(l.T(), ""), l.metrics, false) - Nil(l.T(), err) - l.testDB = sqliteStore - l.manager = testutil.NewDeployManager(l.T()) - l.wallet, err = wallet.FromRandom() - Nil(l.T(), err) - l.signer = localsigner.NewSigner(l.wallet.PrivateKey()) -} - -// TestLiveSuite tests the live suite. -func TestLiveSuite(t *testing.T) { - suite.Run(t, NewLiveSuite(t)) -} diff --git a/services/scribe/service/chain.go b/services/scribe/service/chain.go new file mode 100644 index 0000000000..6421ba6fa7 --- /dev/null +++ b/services/scribe/service/chain.go @@ -0,0 +1,447 @@ +package service + +import ( + "context" + "fmt" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/logger" + "github.com/synapsecns/sanguine/services/scribe/service/indexer" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + "math/big" + + "math" + "time" + + "github.com/ethereum/go-ethereum/common" + + "github.com/synapsecns/sanguine/core/metrics" + "go.opentelemetry.io/otel/metric" + + "github.com/jpillora/backoff" + "github.com/synapsecns/sanguine/services/scribe/config" + "github.com/synapsecns/sanguine/services/scribe/db" + "golang.org/x/sync/errgroup" +) + +// ChainIndexer is an indexer that fetches logs for a chain. It aggregates logs +// from a slice of ContractIndexers. +type ChainIndexer struct { + // chainID is the chain ID of the chain. + chainID uint32 + // eventDB is the database to store event data in. + eventDB db.EventDB + // client contains the clients used for indexing. + client []backend.ScribeBackend + // chainConfig is the config for the indexer. + chainConfig config.ChainConfig + // handler is the metrics handler for the scribe. + handler metrics.Handler + // blockHeightMeters is a map from address -> meter for block height. + blockHeightMeters map[common.Address]metric.Int64Histogram + // livefillContracts is a map from address -> livefill contract. + livefillContracts []config.ContractConfig + // readyForLivefill is a chan + readyForLivefill chan config.ContractConfig +} + +// Used for handling logging of various context types. +type contextKey int + +const maxBackoff = uint64(10) + +const ( + chainContextKey contextKey = iota +) + +// NewChainIndexer creates a new indexer for a chain. This is done by passing through all the function parameters +// into the ChainIndexer struct, as well as iterating through all the contracts in the chain config & creating +// ContractIndexers for each contract. +func NewChainIndexer(eventDB db.EventDB, client []backend.ScribeBackend, chainConfig config.ChainConfig, handler metrics.Handler) (*ChainIndexer, error) { + if chainConfig.GetLogsRange == 0 { + chainConfig.GetLogsRange = 600 + } + + if chainConfig.GetLogsBatchAmount == 0 { + chainConfig.GetLogsBatchAmount = 2 + } + + if chainConfig.StoreConcurrency == 0 { + chainConfig.StoreConcurrency = 20 + } + + if chainConfig.ConcurrencyThreshold == 0 { + chainConfig.ConcurrencyThreshold = 50000 + } + if chainConfig.LivefillRange == 0 { + chainConfig.LivefillRange = 100 + } + + if chainConfig.LivefillFlushInterval == 0 { + chainConfig.LivefillFlushInterval = 10800 + } + + blockHeightMeterMap := make(map[common.Address]metric.Int64Histogram) + for _, contract := range chainConfig.Contracts { + blockHeightMeter, err := handler.Metrics().NewHistogram(fmt.Sprintf("scribe_block_meter_%d_%s", chainConfig.ChainID, contract.Address), "block_histogram", "a block height meter", "blocks") + if err != nil { + return nil, fmt.Errorf("error creating otel histogram %w", err) + } + blockHeightMeterMap[common.HexToAddress(contract.Address)] = blockHeightMeter + } + + return &ChainIndexer{ + chainID: chainConfig.ChainID, + eventDB: eventDB, + client: client, + blockHeightMeters: blockHeightMeterMap, + chainConfig: chainConfig, + handler: handler, + readyForLivefill: make(chan config.ContractConfig), + }, nil +} + +// Index iterates over each contract indexer and calls Index concurrently on each one. +// If `onlyOneBlock` is true, the indexer will only index the block at `currentBlock`. +// +//nolint:gocognit,cyclop,unparam +func (c *ChainIndexer) Index(parentContext context.Context) error { + indexGroup, indexCtx := errgroup.WithContext(parentContext) + + latestBlock, err := c.getLatestBlock(indexCtx, scribeTypes.IndexingConfirmed) + if err != nil { + return fmt.Errorf("could not get current block number while indexing: %w", err) + } + + var contractAddresses []common.Address + for i := range c.chainConfig.Contracts { + contractAddresses = append(contractAddresses, common.HexToAddress(c.chainConfig.Contracts[i].Address)) + } + + // Gets all last indexed infos for the contracts on the current chain to determine which contracts need to be initially livefilled. + lastIndexedMap, err := c.eventDB.RetrieveLastIndexedMultiple(parentContext, contractAddresses, c.chainConfig.ChainID) + if err != nil { + return fmt.Errorf("could not get last indexed map: %w", err) + } + + for j := range c.chainConfig.Contracts { + contract := c.chainConfig.Contracts[j] + contractAddress := common.HexToAddress(contract.Address) + lastIndexed := lastIndexedMap[contractAddress] + + // Does not consider if the config's start block is within the livefill threshold for simplicity. + // In this case, an indexer will bring the contract to head, and it will be passed to livefill. + // If there is no last indexed info for the contract, it will not be passed to livefill. + if *latestBlock-c.chainConfig.LivefillThreshold > lastIndexed && lastIndexed > 0 { + c.livefillContracts = append(c.livefillContracts, contract) + continue + } + + // If current contract is not within the livefill threshold, start an indexer for it. + contractIndexer, err := indexer.NewIndexer(c.chainConfig, []common.Address{contractAddress}, c.eventDB, c.client, c.handler, c.blockHeightMeters[contractAddress], scribeTypes.IndexingConfirmed) + if err != nil { + return fmt.Errorf("could not create contract indexer: %w", err) + } + + // Check if a explicit backfill range has been set. + var configEnd *uint64 + if contract.EndBlock > contract.StartBlock { + configEnd = &contract.EndBlock + } + + indexGroup.Go(func() error { + err := c.IndexToBlock(indexCtx, contract.StartBlock, configEnd, contractIndexer) + if err != nil { + return fmt.Errorf("could not index to livefill: %w", err) + } + c.readyForLivefill <- contract + + // TODO make sure metrics are killed when indexing is done + return nil + }) + } + + // Livefill contracts that are within the livefill threshold and before the confirmation threshold. + indexGroup.Go(func() error { + return c.livefill(indexCtx) + }) + + // Index unconfirmed events to the head. + if c.chainConfig.Confirmations > 0 { + indexGroup.Go(func() error { + return c.livefillAtHead(indexCtx) + }) + } + + if err := indexGroup.Wait(); err != nil { + return fmt.Errorf("could not index: %w", err) + } + return nil // This shouldn't really ever be hit. +} + +// nolint:unparam +func (c *ChainIndexer) getLatestBlock(ctx context.Context, indexingUnconfirmed bool) (*uint64, error) { + var currentBlock uint64 + var err error + b := createBackoff() + timeout := time.Duration(0) + for { + select { + case <-ctx.Done(): + + return nil, fmt.Errorf("%s context canceled: %w", ctx.Value(chainContextKey), ctx.Err()) + case <-time.After(timeout): + currentBlock, err = c.client[0].BlockNumber(ctx) + + if err != nil { + timeout = b.Duration() + logger.ReportScribeError(err, c.chainID, logger.GetBlockError) + continue + } + if !indexingUnconfirmed { + currentBlock -= c.chainConfig.Confirmations + } + } + + return ¤tBlock, nil + } +} + +// IndexToBlock takes a contract indexer and indexs a contract up until it reaches the livefill threshold. This function should be generally used for calling a indexer with a single contract. +func (c *ChainIndexer) IndexToBlock(parentContext context.Context, configStart uint64, configEnd *uint64, indexer *indexer.Indexer) error { + timeout := time.Duration(0) + b := createBackoff() + for { + select { + case <-parentContext.Done(): + return fmt.Errorf("%s chain context canceled: %w", parentContext.Value(chainContextKey), parentContext.Err()) + case <-time.After(timeout): + var endHeight uint64 + var err error + startHeight, endHeight, err := c.getIndexingRange(parentContext, configStart, configEnd, indexer) + if err != nil { + return err + } + err = indexer.Index(parentContext, startHeight, endHeight) + if err != nil { + timeout = b.Duration() + // if the config has set the contract to refresh at a slower rate than the timeout, use the refresh rate instead. + if indexer.RefreshRate() > maxBackoff { + timeout = time.Duration(indexer.RefreshRate()) * time.Second + } + logger.ReportIndexerError(err, indexer.GetIndexerConfig(), logger.BackfillIndexerError) + continue + } + if configEnd != nil { + return nil + } + + livefillReady, err := c.isReadyForLivefill(parentContext, indexer) + if err != nil { + return fmt.Errorf("could not get last indexed: %w", err) + } + if livefillReady { + return nil + } + + timeout = time.Duration(indexer.RefreshRate()) * time.Second + } + } +} + +func getMinFromMap(inputMap map[common.Address]uint64) uint64 { + minValue := uint64(math.MaxUint64) + + for i := range inputMap { + if inputMap[i] < minValue { + minValue = inputMap[i] + } + } + + return minValue +} + +func getAddressesFromConfig(contractConfigs []config.ContractConfig) []common.Address { + var addresses []common.Address + for i := range contractConfigs { + contract := common.HexToAddress(contractConfigs[i].Address) + addresses = append(addresses, contract) + } + + return addresses +} + +func createBackoff() *backoff.Backoff { + return &backoff.Backoff{ + Factor: 2, + Jitter: true, + Min: 1 * time.Second, + Max: time.Duration(maxBackoff) * time.Second, + } +} + +func (c *ChainIndexer) isReadyForLivefill(parentContext context.Context, indexer *indexer.Indexer) (bool, error) { + // get last indexed to check livefill threshold + lastBlockIndexed, err := c.eventDB.RetrieveLastIndexed(parentContext, indexer.GetIndexerConfig().Addresses[0], c.chainConfig.ChainID, scribeTypes.IndexingConfirmed) + if err != nil { + return false, fmt.Errorf("could not get last indexed: %w", err) + } + endHeight, err := c.getLatestBlock(parentContext, scribeTypes.IndexingConfirmed) + if err != nil { + return false, fmt.Errorf("could not get current block number while indexing: %w", err) + } + return int64(lastBlockIndexed) >= int64(*endHeight)-int64(c.chainConfig.LivefillThreshold), nil +} + +func (c *ChainIndexer) getIndexingRange(parentContext context.Context, configStart uint64, configEnd *uint64, indexer *indexer.Indexer) (uint64, uint64, error) { + var endHeight uint64 + startHeight := configStart + + // If a range is set in the config, respect those values, + if configEnd != nil { + endHeight = *configEnd + indexer.SetToBackfill() + return startHeight, endHeight, nil + } + + // otherwise, get the last indexed block and start from the last indexed block + lastIndexed, err := c.eventDB.RetrieveLastIndexed(parentContext, indexer.GetIndexerConfig().Addresses[0], c.chainConfig.ChainID, scribeTypes.IndexingConfirmed) + if err != nil { + return 0, 0, fmt.Errorf("could not get last block indexed: %w", err) + } + if lastIndexed > startHeight { + startHeight = lastIndexed + 1 + } + latestBlock, err := c.getLatestBlock(parentContext, scribeTypes.IndexingConfirmed) + if err != nil { + return 0, 0, fmt.Errorf("could not get current block number while indexing: %w", err) + } + endHeight = *latestBlock + + return startHeight, endHeight, nil +} + +// LivefillAtHead stores data for all contracts all the way to the head in a separate table. +// +// nolint:cyclop +func (c *ChainIndexer) livefillAtHead(parentContext context.Context) error { + timeout := time.Duration(0) + b := createBackoff() + addresses := getAddressesFromConfig(c.chainConfig.Contracts) + tipLivefillBlockMeter, err := c.handler.Metrics().NewHistogram(fmt.Sprintf("scribe_block_meter_%d_tip_livefill", c.chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + if err != nil { + return fmt.Errorf("error creating otel histogram %w", err) + } + + tipLivefillIndexer, err := indexer.NewIndexer(c.chainConfig, addresses, c.eventDB, c.client, c.handler, tipLivefillBlockMeter, true) + if err != nil { + return fmt.Errorf("could not create contract indexer: %w", err) + } + flushDuration := time.Duration(c.chainConfig.LivefillFlushInterval) * time.Second + for { + select { + case <-parentContext.Done(): + logger.ReportScribeError(parentContext.Err(), c.chainID, logger.ContextCancelled) + return fmt.Errorf("context canceled: %w", parentContext.Err()) + case <-time.After(flushDuration): + logger.ReportScribeState(c.chainID, 0, addresses, logger.FlushingLivefillAtHead) + deleteBefore := time.Now().Add(-flushDuration).UnixNano() + err := c.eventDB.FlushFromHeadTables(parentContext, deleteBefore) + if err != nil { + return fmt.Errorf("could not flush logs from head: %w", err) + } + case <-time.After(timeout): + + endHeight, err := c.getLatestBlock(parentContext, scribeTypes.LivefillAtHead) + if err != nil { + logger.ReportIndexerError(err, tipLivefillIndexer.GetIndexerConfig(), logger.GetBlockError) + timeout = b.Duration() + continue + } + + tipLivefillLastIndexed, err := c.eventDB.RetrieveLastIndexed(parentContext, common.BigToAddress(big.NewInt(0)), c.chainConfig.ChainID, scribeTypes.LivefillAtHead) + if err != nil { + logger.ReportIndexerError(err, tipLivefillIndexer.GetIndexerConfig(), logger.LivefillIndexerError) + timeout = b.Duration() + continue + } + startHeight := tipLivefillLastIndexed + if startHeight == 0 { + startHeight = *endHeight - c.chainConfig.Confirmations + } + + err = tipLivefillIndexer.Index(parentContext, startHeight, *endHeight) + if err != nil { + timeout = b.Duration() + logger.ReportIndexerError(err, tipLivefillIndexer.GetIndexerConfig(), logger.LivefillIndexerError) + continue + } + + // Default refresh rate for livefill to tip is 1 second. + timeout = 1 * time.Second + } + } +} + +// nolint:cyclop +func (c *ChainIndexer) livefill(parentContext context.Context) error { + timeout := time.Duration(0) + b := createBackoff() + livefillBlockMeter, err := c.handler.Metrics().NewHistogram(fmt.Sprintf("scribe_block_meter_%d_livefill", c.chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + if err != nil { + return fmt.Errorf("error creating otel histogram %w", err) + } + + livefillIndexer, err := indexer.NewIndexer(c.chainConfig, getAddressesFromConfig(c.livefillContracts), c.eventDB, c.client, c.handler, livefillBlockMeter, scribeTypes.IndexingConfirmed) + if err != nil { + return fmt.Errorf("could not create contract indexer: %w", err) + } + for { + select { + case <-parentContext.Done(): + logger.ReportScribeError(parentContext.Err(), c.chainID, logger.ContextCancelled) + return fmt.Errorf("%s chain context canceled: %w", parentContext.Value(chainContextKey), parentContext.Err()) + case newLivefillContract := <-c.readyForLivefill: + c.livefillContracts = append(c.livefillContracts, newLivefillContract) + // Update indexer's config to include new contract. + livefillIndexer.UpdateAddress(getAddressesFromConfig(c.livefillContracts)) + case <-time.After(timeout): + if len(c.livefillContracts) == 0 { + timeout = b.Duration() + continue + } + var endHeight *uint64 + var err error + livefillLastIndexed, err := c.eventDB.RetrieveLastIndexedMultiple(parentContext, getAddressesFromConfig(c.livefillContracts), c.chainConfig.ChainID) + if err != nil { + logger.ReportIndexerError(err, livefillIndexer.GetIndexerConfig(), logger.LivefillIndexerError) + timeout = b.Duration() + continue + } + startHeight := getMinFromMap(livefillLastIndexed) + + endHeight, err = c.getLatestBlock(parentContext, scribeTypes.IndexingConfirmed) + if err != nil { + logger.ReportIndexerError(err, livefillIndexer.GetIndexerConfig(), logger.GetBlockError) + timeout = b.Duration() + continue + } + + // Don't reindex the head block. + if startHeight == *endHeight { + timeout = 1 * time.Second + continue + } + + err = livefillIndexer.Index(parentContext, startHeight, *endHeight) + if err != nil { + timeout = b.Duration() + logger.ReportIndexerError(err, livefillIndexer.GetIndexerConfig(), logger.LivefillIndexerError) + continue + } + + // Default refresh rate for livefill is 1 second. + // TODO add to config + timeout = 1 * time.Second + } + } +} diff --git a/services/scribe/service/chain_test.go b/services/scribe/service/chain_test.go new file mode 100644 index 0000000000..3742215798 --- /dev/null +++ b/services/scribe/service/chain_test.go @@ -0,0 +1,489 @@ +package service_test + +import ( + "context" + "fmt" + "github.com/brianvoe/gofakeit/v6" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + . "github.com/stretchr/testify/assert" + "github.com/synapsecns/sanguine/ethergo/backends/geth" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/config" + "github.com/synapsecns/sanguine/services/scribe/db" + "github.com/synapsecns/sanguine/services/scribe/service" + "github.com/synapsecns/sanguine/services/scribe/service/indexer" + "github.com/synapsecns/sanguine/services/scribe/testutil" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + "math" + "math/big" + "os" + "time" +) + +// TestIndexToBlock tests using an indexer for recording receipts and logs in a database. +func (s *ScribeSuite) TestIndexToBlock() { + // Get simulated blockchain, deploy the test contract, and set up test variables. + simulatedChain := geth.NewEmbeddedBackendForChainID(s.GetSuiteContext(), s.T(), big.NewInt(142)) + simulatedClient, err := backend.DialBackend(s.GetTestContext(), simulatedChain.RPCAddress(), s.nullMetrics) + Nil(s.T(), err) + + simulatedChain.FundAccount(s.GetTestContext(), s.wallet.Address(), *big.NewInt(params.Ether)) + testContract, testRef := s.manager.GetTestContract(s.GetTestContext(), simulatedChain) + transactOpts := simulatedChain.GetTxContext(s.GetTestContext(), nil) + + // Set config. + contractConfig := config.ContractConfig{ + Address: testContract.Address().String(), + StartBlock: 0, + } + + simulatedChainArr := []backend.ScribeBackend{simulatedClient, simulatedClient} + chainConfig := config.ChainConfig{ + ChainID: 142, + GetLogsBatchAmount: 1, + Confirmations: 0, + StoreConcurrency: 1, + GetLogsRange: 1, + ConcurrencyThreshold: 100, + Contracts: []config.ContractConfig{contractConfig}, + } + + chainIndexer, err := service.NewChainIndexer(s.testDB, simulatedChainArr, chainConfig, s.nullMetrics) + Nil(s.T(), err) + + // Emit events for the indexer to read. + tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(s.T(), err) + simulatedChain.WaitForConfirmation(s.GetTestContext(), tx) + + tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(s.T(), err) + + simulatedChain.WaitForConfirmation(s.GetTestContext(), tx) + tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) + Nil(s.T(), err) + simulatedChain.WaitForConfirmation(s.GetTestContext(), tx) + + // Emit two logs in one receipt. + tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) + Nil(s.T(), err) + + simulatedChain.WaitForConfirmation(s.GetTestContext(), tx) + + // Get the block that the last transaction was executed in. + txBlockNumber, err := testutil.GetTxBlockNumber(s.GetTestContext(), simulatedChain, tx) + Nil(s.T(), err) + + // TODO use no-op meter + blockHeightMeter, err := s.nullMetrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(s.T(), err) + + contracts := []common.Address{common.HexToAddress(contractConfig.Address)} + indexer, err := indexer.NewIndexer(chainConfig, contracts, s.testDB, simulatedChainArr, s.nullMetrics, blockHeightMeter, false) + Nil(s.T(), err) + + err = chainIndexer.IndexToBlock(s.GetTestContext(), uint64(0), nil, indexer) + Nil(s.T(), err) + + // Get all receipts. + receipts, err := s.testDB.RetrieveReceiptsWithFilter(s.GetTestContext(), db.ReceiptFilter{}, 1) + Nil(s.T(), err) + + // Check to see if 3 receipts were collected. + Equal(s.T(), 4, len(receipts)) + + // Get all logs. + logs, err := s.testDB.RetrieveLogsWithFilter(s.GetTestContext(), db.LogFilter{}, 1) + Nil(s.T(), err) + + // Check to see if 4 logs were collected. + Equal(s.T(), 5, len(logs)) + + // Check to see if the last receipt has two logs. + Equal(s.T(), 2, len(receipts[0].Logs)) + + // Ensure last indexed block is correct. + lastIndexed, err := s.testDB.RetrieveLastIndexed(s.GetTestContext(), testContract.Address(), uint32(testContract.ChainID().Uint64()), scribeTypes.IndexingConfirmed) + Nil(s.T(), err) + Equal(s.T(), txBlockNumber, lastIndexed) +} + +// TestChainIndexer tests that the ChainIndexer can index events from a chain. +func (s *ScribeSuite) TestChainIndexer() { + const numberOfContracts = 3 + const desiredBlockHeight = 20 + chainID := gofakeit.Uint32() + chainBackends := make(map[uint32]geth.Backend) + newBackend := geth.NewEmbeddedBackendForChainID(s.GetTestContext(), s.T(), big.NewInt(int64(chainID))) + chainBackends[chainID] = *newBackend + + // Create contract managers + managers := []*testutil.DeployManager{s.manager} + if numberOfContracts > 1 { + for i := 1; i < numberOfContracts; i++ { + managers = append(managers, testutil.NewDeployManager(s.T())) + } + } + + testChainHandlerMap, chainBackendMap, err := testutil.PopulateChainsWithLogs(s.GetTestContext(), s.T(), chainBackends, desiredBlockHeight, managers, s.nullMetrics) + Nil(s.T(), err) + + var contractConfigs []config.ContractConfig + addresses := testChainHandlerMap[chainID].Addresses + for i := range addresses { + contractConfig := config.ContractConfig{ + Address: addresses[i].String(), + } + contractConfigs = append(contractConfigs, contractConfig) + } + chainConfig := config.ChainConfig{ + ChainID: chainID, + Confirmations: 0, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + Contracts: contractConfigs, + } + killableContext, cancel := context.WithTimeout(s.GetTestContext(), 20*time.Second) + defer cancel() + chainIndexer, err := service.NewChainIndexer(s.testDB, chainBackendMap[chainID], chainConfig, s.nullMetrics) + Nil(s.T(), err) + _ = chainIndexer.Index(killableContext) + sum := uint64(0) + for _, value := range testChainHandlerMap[chainID].EventsEmitted { + sum += value + } + logs, err := s.testDB.RetrieveLogsWithFilter(s.GetTestContext(), db.LogFilter{}, 1) + Nil(s.T(), err) + Equal(s.T(), sum, uint64(len(logs))) + receipts, err := s.testDB.RetrieveReceiptsWithFilter(s.GetTestContext(), db.ReceiptFilter{}, 1) + Nil(s.T(), err) + Equal(s.T(), sum, uint64(len(receipts))) +} + +// TestChainIndexerLivefill tests a ChainIndexer's ability to livefill and handle passing events from index to livefill. +// +// nolint:cyclop +func (s *ScribeSuite) TestChainIndexerLivefill() { + if os.Getenv("CI") != "" || !s.runVolumeTest { + s.T().Skip("This is a long running test") + } + const numberOfContracts = 5 + currentBlockHeight := uint64(0) // starting with zero to emit events while indexing. + chainID := gofakeit.Uint32() + chainBackends := make(map[uint32]geth.Backend) + newBackend := geth.NewEmbeddedBackendForChainID(s.GetTestContext(), s.T(), big.NewInt(int64(chainID))) + chainBackends[chainID] = *newBackend + + // Create contract managers + deployManagers := []*testutil.DeployManager{s.manager} + if numberOfContracts > 1 { + for i := 1; i < numberOfContracts; i++ { + deployManagers = append(deployManagers, testutil.NewDeployManager(s.T())) + } + } + + testChainHandlerMap, chainBackendMap, err := testutil.PopulateChainsWithLogs(s.GetTestContext(), s.T(), chainBackends, currentBlockHeight, deployManagers, s.nullMetrics) + Nil(s.T(), err) + addresses := testChainHandlerMap[chainID].Addresses + // Differing start blocks and refresh rates to test contracts reaching livefill at different times. + contractConfig1 := config.ContractConfig{ + Address: addresses[0].String(), + StartBlock: 0, + RefreshRate: 4, + } + contractConfig2 := config.ContractConfig{ + Address: addresses[1].String(), + StartBlock: 25, + RefreshRate: 1, + } + contractConfig3 := config.ContractConfig{ + Address: addresses[2].String(), + StartBlock: 30, + RefreshRate: 3, + } + contractConfig4 := config.ContractConfig{ + Address: addresses[3].String(), + StartBlock: 30, + RefreshRate: 1, + } + contractConfig5 := config.ContractConfig{ + Address: addresses[4].String(), + StartBlock: 0, + RefreshRate: 3, + } + + contractConfigs := []config.ContractConfig{contractConfig1, contractConfig2, contractConfig3, contractConfig4, contractConfig5} + chainConfig := config.ChainConfig{ + ChainID: chainID, + Confirmations: 0, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + // livefill threshold kept small to ensure that the indexer does not reach the head before the continuous event emitting starts + LivefillThreshold: 0, + Contracts: contractConfigs, + } + + // Update start blocks + for i := range contractConfigs { + contract := contractConfigs[i] + contractAddress := common.HexToAddress(contract.Address) + testChainHandlerMap[chainID].ContractStartBlocks[contractAddress] = contract.StartBlock + } + + chainIndexer, err := service.NewChainIndexer(s.testDB, chainBackendMap[chainID], chainConfig, s.nullMetrics) + Nil(s.T(), err) + Equal(s.T(), 0, len(chainIndexer.GetLivefillContracts())) + currentBlockHeight = 30 + + emittingContext, cancelEmitting := context.WithTimeout(s.GetTestContext(), 60*time.Second) + defer cancelEmitting() + + // Emit an event for every contract every second. This will terminate 20 seconds before indexing terminates. + go func() { + for { + select { + case <-emittingContext.Done(): + return + case <-time.After(1 * time.Second): + currentBlockHeight += 2 + emitErr := testutil.EmitEvents(s.GetTestContext(), s.T(), newBackend, currentBlockHeight, testChainHandlerMap[chainID]) + Nil(s.T(), emitErr) + } + } + }() + + <-time.After(40 * time.Second) // wait for 40 seconds before indexing to get some events on chain before indexing. + + // Cap indexing for 30 seconds. + indexingContext, cancelIndexing := context.WithTimeout(s.GetTestContext(), 30*time.Second) + defer cancelIndexing() + + // Check that the number of livefill contracts is correct. + numberLivefillContracts := 0 + go func() { + currentLength := 0 + for { + select { + case <-indexingContext.Done(): + return + default: + contracts := chainIndexer.GetLivefillContracts() + if currentLength != len(contracts) { + currentLength = len(contracts) + newContract := contracts[currentLength-1] + + lastIndexed, indexErr := s.testDB.RetrieveLastIndexed(s.GetTestContext(), common.HexToAddress(newContract.Address), chainID, scribeTypes.IndexingConfirmed) + Nil(s.T(), indexErr) + numberLivefillContracts = len(contracts) + currentBlock, indexErr := newBackend.BlockNumber(s.GetTestContext()) + Nil(s.T(), indexErr) + // Check to ensure last indexed is within reasonable range to have triggered livefill for that contract + GreaterOrEqual(s.T(), float64(7), math.Abs(float64(lastIndexed)-(float64(currentBlock)-float64(chainConfig.LivefillThreshold)))) + } + } + } + }() + + // Index events + _ = chainIndexer.Index(indexingContext) + + <-indexingContext.Done() + sum := uint64(0) + for _, value := range testChainHandlerMap[chainID].EventsEmitted { + sum += value + } + + logs, err := testutil.GetLogsUntilNoneLeft(s.GetTestContext(), s.testDB, db.LogFilter{}) + Nil(s.T(), err) + Equal(s.T(), sum, uint64(len(logs))) + receipts, err := testutil.GetReceiptsUntilNoneLeft(s.GetTestContext(), s.testDB, db.ReceiptFilter{}) + Nil(s.T(), err) + Equal(s.T(), sum, uint64(len(receipts))) + Equal(s.T(), numberOfContracts, numberLivefillContracts) +} + +// TestLargeVolume tests that the ChainIndexer can index a large volume of events from a chain. +func (s *ScribeSuite) TestLargeVolume() { + if os.Getenv("CI") != "" || !s.runVolumeTest { + s.T().Skip("This is a long running test") + } + const runtime = 100 + desiredBlockHeight := uint64(1) + chainID := gofakeit.Uint32() + chainBackends := make(map[uint32]geth.Backend) + newBackend := geth.NewEmbeddedBackendForChainID(s.GetTestContext(), s.T(), big.NewInt(int64(chainID))) + chainBackends[chainID] = *newBackend + + // Create contract managers + managers := []*testutil.DeployManager{s.manager} + + testChainHandlerMap, chainBackendMap, err := testutil.PopulateChainsWithLogs(s.GetTestContext(), s.T(), chainBackends, desiredBlockHeight, managers, s.nullMetrics) + Nil(s.T(), err) + + var contractConfigs []config.ContractConfig + addresses := testChainHandlerMap[chainID].Addresses + for i := range addresses { + contractConfig := config.ContractConfig{ + Address: addresses[i].String(), + } + contractConfigs = append(contractConfigs, contractConfig) + } + chainConfig := config.ChainConfig{ + ChainID: chainID, + Confirmations: 0, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 2000, + Contracts: contractConfigs, + } + + // emit events for seconds + emittingContext, cancelEmitting := context.WithTimeout(s.GetTestContext(), runtime*time.Second) + defer cancelEmitting() + + go func() { + for { + // repeat until emittingContext is canceled + select { + case <-emittingContext.Done(): + return + default: + desiredBlockHeight += 10 + err = testutil.EmitEvents(emittingContext, s.T(), newBackend, desiredBlockHeight, testChainHandlerMap[chainID]) + if err != nil { + return + } + } + } + }() + // wait until done emitting + <-emittingContext.Done() + indexingContext, cancelIndexing := context.WithTimeout(s.GetTestContext(), 20*time.Second) + defer cancelIndexing() + chainIndexer, err := service.NewChainIndexer(s.testDB, chainBackendMap[chainID], chainConfig, s.nullMetrics) + Nil(s.T(), err) + _ = chainIndexer.Index(indexingContext) + sum := uint64(0) + for _, value := range testChainHandlerMap[chainID].EventsEmitted { + sum += value + } + logs, err := testutil.GetLogsUntilNoneLeft(s.GetTestContext(), s.testDB, db.LogFilter{}) + Nil(s.T(), err) + Equal(s.T(), sum, uint64(len(logs))) + receipts, err := testutil.GetReceiptsUntilNoneLeft(s.GetTestContext(), s.testDB, db.ReceiptFilter{}) + Nil(s.T(), err) + Equal(s.T(), sum, uint64(len(receipts))) +} + +// TestChainIndexerLivfillToTip tests that the ChainIndexer can livefill events to the head. +// +// nolint:cyclop +func (s *ScribeSuite) TestChainIndexerLivefillToTip() { + const numberOfContracts = 3 + currentBlockHeight := uint64(10) // starting with zero to emit events while indexing. + chainID := gofakeit.Uint32() + chainBackends := make(map[uint32]geth.Backend) + newBackend := geth.NewEmbeddedBackendForChainID(s.GetTestContext(), s.T(), big.NewInt(int64(chainID))) + chainBackends[chainID] = *newBackend + + // Create contract managers + deployManagers := []*testutil.DeployManager{s.manager} + if numberOfContracts > 1 { + for i := 1; i < numberOfContracts; i++ { + deployManagers = append(deployManagers, testutil.NewDeployManager(s.T())) + } + } + + testChainHandlerMap, chainBackendMap, err := testutil.PopulateChainsWithLogs(s.GetTestContext(), s.T(), chainBackends, currentBlockHeight, deployManagers, s.nullMetrics) + Nil(s.T(), err) + addresses := testChainHandlerMap[chainID].Addresses + // Differing start blocks and refresh rates to test contracts reaching livefill at different times. + contractConfig1 := config.ContractConfig{ + Address: addresses[0].String(), + StartBlock: 0, + RefreshRate: 4, + } + contractConfig2 := config.ContractConfig{ + Address: addresses[1].String(), + StartBlock: 25, + RefreshRate: 1, + } + contractConfig3 := config.ContractConfig{ + Address: addresses[2].String(), + StartBlock: 30, + RefreshRate: 3, + } + + contractConfigs := []config.ContractConfig{contractConfig1, contractConfig2, contractConfig3} + chainConfig := config.ChainConfig{ + ChainID: chainID, + Confirmations: 30, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + LivefillThreshold: 0, + Contracts: contractConfigs, + } + + // Update start blocks + for i := range contractConfigs { + contract := contractConfigs[i] + contractAddress := common.HexToAddress(contract.Address) + testChainHandlerMap[chainID].ContractStartBlocks[contractAddress] = contract.StartBlock + } + + chainIndexer, err := service.NewChainIndexer(s.testDB, chainBackendMap[chainID], chainConfig, s.nullMetrics) + Nil(s.T(), err) + + currentBlockHeight = 30 + emittingContext, cancelEmitting := context.WithTimeout(s.GetTestContext(), 30*time.Second) + defer cancelEmitting() + + // Emit an event for every contract every second. This will terminate 10 seconds before indexing terminates. + go func() { + for { + select { + case <-emittingContext.Done(): + return + case <-time.After(1 * time.Second): + currentBlockHeight += 2 + emitErr := testutil.EmitEvents(s.GetTestContext(), s.T(), newBackend, currentBlockHeight, testChainHandlerMap[chainID]) + Nil(s.T(), emitErr) + } + } + }() + + <-time.After(20 * time.Second) // wait for 20 seconds before indexing to get some events on chain before indexing. + + // Cap indexing for 30 seconds. + indexingContext, cancelIndexing := context.WithTimeout(s.GetTestContext(), 20*time.Second) + defer cancelIndexing() + // Index events + _ = chainIndexer.Index(indexingContext) + + <-indexingContext.Done() + sum := uint64(0) + for _, value := range testChainHandlerMap[chainID].EventsEmitted { + sum += value + } + + currentBlock, indexErr := newBackend.BlockNumber(s.GetTestContext()) + Nil(s.T(), indexErr) + logs, err := testutil.GetLogsUntilNoneLeft(s.GetTestContext(), s.testDB, db.LogFilter{}) + Nil(s.T(), err) + GreaterOrEqual(s.T(), sum, uint64(len(logs))) + receipts, err := testutil.GetReceiptsUntilNoneLeft(s.GetTestContext(), s.testDB, db.ReceiptFilter{}) + Nil(s.T(), err) + GreaterOrEqual(s.T(), sum, uint64(len(receipts))) + for _, contract := range contractConfigs { + unconfirmedLogs, err := s.testDB.RetrieveLogsFromHeadRangeQuery(s.GetTestContext(), db.LogFilter{ChainID: chainID, ContractAddress: contract.Address}, 1, currentBlock, 1) + Nil(s.T(), err) + GreaterOrEqual(s.T(), sum, uint64(len(unconfirmedLogs))) + unconfirmedReceipts, err := s.testDB.RetrieveReceiptsFromHeadRangeQuery(s.GetTestContext(), db.ReceiptFilter{ChainID: chainID, ContractAddress: contract.Address}, 1, currentBlock, 1) + Nil(s.T(), err) + GreaterOrEqual(s.T(), sum, uint64(len(unconfirmedReceipts))) + } +} diff --git a/services/scribe/service/doc.go b/services/scribe/service/doc.go new file mode 100644 index 0000000000..4a5069fbcb --- /dev/null +++ b/services/scribe/service/doc.go @@ -0,0 +1,2 @@ +// Package service runs the scribe service +package service diff --git a/services/scribe/service/export_test.go b/services/scribe/service/export_test.go new file mode 100644 index 0000000000..9364df40c5 --- /dev/null +++ b/services/scribe/service/export_test.go @@ -0,0 +1,10 @@ +package service + +import ( + "github.com/synapsecns/sanguine/services/scribe/config" +) + +// GetLivefillContracts returns the array of livefill contracts for testing. +func (c *ChainIndexer) GetLivefillContracts() []config.ContractConfig { + return c.livefillContracts +} diff --git a/services/scribe/service/indexer/doc.go b/services/scribe/service/indexer/doc.go new file mode 100644 index 0000000000..93c53910f1 --- /dev/null +++ b/services/scribe/service/indexer/doc.go @@ -0,0 +1,2 @@ +// Package indexer takes a range of blocks, fetches logs, gets txs, receipts, and block headers, and stores them. +package indexer diff --git a/services/scribe/backfill/fetcher.go b/services/scribe/service/indexer/fetcher.go similarity index 62% rename from services/scribe/backfill/fetcher.go rename to services/scribe/service/indexer/fetcher.go index 0cebe1f184..950e47d17d 100644 --- a/services/scribe/backfill/fetcher.go +++ b/services/scribe/service/indexer/fetcher.go @@ -1,16 +1,17 @@ -package backfill +package indexer import ( "context" "fmt" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/logger" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" "math/big" "time" "github.com/synapsecns/sanguine/ethergo/util" - ethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/synapsecns/sanguine/services/scribe/config" "github.com/jpillora/backoff" ) @@ -24,38 +25,44 @@ type LogFetcher struct { // for logging endBlock *big.Int // fetchedLogsChan is a channel with the fetched chunks of logs. - fetchedLogsChan chan []types.Log + fetchedLogsChan chan types.Log // backend is the ethereum backend used to fetch logs. - backend ScribeBackend - // contractAddress is the contractAddress that logs are fetched for. - contractAddress ethCommon.Address - // chainConfig holds the chain config (config data for the chain) - chainConfig *config.ChainConfig + backend backend.ScribeBackend + // indexerConfig holds the chain config (config data for the chain) + indexerConfig *scribeTypes.IndexerConfig + // bufferSize prevents from overloading the scribe indexer with too many logs as well as upstream RPCs with too many requests. + bufferSize int } -// bufferSize is how many getLogs*batch amount chunks ahead should be fetched. -const bufferSize = 3 - // NewLogFetcher creates a new filtering interface for a range of blocks. If reverse is not set, block heights are filtered from start->end. -func NewLogFetcher(address ethCommon.Address, backend ScribeBackend, startBlock, endBlock *big.Int, chainConfig *config.ChainConfig) *LogFetcher { +func NewLogFetcher(backend backend.ScribeBackend, startBlock, endBlock *big.Int, indexerConfig *scribeTypes.IndexerConfig) *LogFetcher { // The ChunkIterator is inclusive of the start and ending block resulting in potentially confusing behavior when // setting the range size in the config. For example, setting a range of 1 would result in two blocks being queried // instead of 1. This is accounted for by subtracting 1. - chunkSize := int(chainConfig.GetLogsRange) - 1 + chunkSize := int(indexerConfig.GetLogsRange) - 1 + + // Using the specified StoreConcurrency value from the config, as the buffer size for the fetchedLogsChan + bufferSize := indexerConfig.StoreConcurrency + if bufferSize > 100 { + bufferSize = 100 + } + if bufferSize == 0 { + bufferSize = 3 // default buffer size + } return &LogFetcher{ iterator: util.NewChunkIterator(startBlock, endBlock, chunkSize, true), startBlock: startBlock, endBlock: endBlock, - fetchedLogsChan: make(chan []types.Log, bufferSize), + fetchedLogsChan: make(chan types.Log, bufferSize), backend: backend, - contractAddress: address, - chainConfig: chainConfig, + indexerConfig: indexerConfig, + bufferSize: bufferSize, } } // GetChunkArr gets the appropriate amount of block chunks (getLogs ranges). func (f *LogFetcher) GetChunkArr() (chunkArr []*util.Chunk) { - for i := uint64(0); i < f.chainConfig.GetLogsBatchAmount; i++ { + for i := uint64(0); i < f.indexerConfig.GetLogsBatchAmount; i++ { chunk := f.iterator.NextChunk() if chunk == nil { return chunkArr @@ -63,7 +70,8 @@ func (f *LogFetcher) GetChunkArr() (chunkArr []*util.Chunk) { chunkArr = append(chunkArr, chunk) // Stop appending chunks if the max height of the current chunk exceeds the concurrency threshold - if chunk.EndBlock.Uint64() > f.endBlock.Uint64()-f.chainConfig.ConcurrencyThreshold { + if chunk.EndBlock.Uint64() > f.endBlock.Uint64()-f.indexerConfig.ConcurrencyThreshold { + logger.ReportScribeState(f.indexerConfig.ChainID, chunk.EndBlock.Uint64(), f.indexerConfig.Addresses, logger.ConcurrencyThresholdReached) return chunkArr } } @@ -85,7 +93,6 @@ func (f *LogFetcher) Start(ctx context.Context) error { select { case <-ctx.Done(): if ctx.Err() != nil { - LogEvent(WarnLevel, "could not finish filtering range", LogData{"ca": f.contractAddress, "sh": f.startBlock.String(), "eh": f.endBlock.String(), "cid": &f.chainConfig.ChainID}) return fmt.Errorf("could not finish filtering range: %w", ctx.Err()) } @@ -105,7 +112,12 @@ func (f *LogFetcher) Start(ctx context.Context) error { select { case <-ctx.Done(): return fmt.Errorf("context canceled while adding log to chan %w", ctx.Err()) - case f.fetchedLogsChan <- logs: + + default: + // insert logs into channel + for i := range logs { + f.fetchedLogsChan <- logs[i] + } } } } @@ -120,28 +132,28 @@ func (f *LogFetcher) FetchLogs(ctx context.Context, chunks []*util.Chunk) ([]typ Factor: 2, Jitter: true, Min: 1 * time.Second, - Max: 10 * time.Second, + Max: 8 * time.Second, } attempt := 0 timeout := time.Duration(0) - startHeight := chunks[0].StartBlock.Uint64() - endHeight := chunks[len(chunks)-1].EndBlock.Uint64() - for { select { case <-ctx.Done(): - return nil, fmt.Errorf("context was canceled before logs could be filtered") + logger.ReportIndexerError(ctx.Err(), *f.indexerConfig, logger.GetLogsError) + return nil, fmt.Errorf("context was canceled before logs could be fetched") case <-time.After(timeout): attempt++ if attempt > retryTolerance { - return nil, fmt.Errorf("maximum number of filter attempts exceeded") + logger.ReportIndexerError(fmt.Errorf("retry max reached"), *f.indexerConfig, logger.GetLogsError) + return nil, fmt.Errorf("maximum number of fetch logs attempts exceeded") } - logs, err := f.getAndUnpackLogs(ctx, chunks, backoffConfig, startHeight, endHeight) + logs, err := f.getAndUnpackLogs(ctx, chunks, backoffConfig) if err != nil { - LogEvent(WarnLevel, "Could not get and unpack logs for range, retrying", LogData{"sh": startHeight, "ca": f.contractAddress, "eh": endHeight, "cid": f.chainConfig.ChainID, "e": err}) + logger.ReportIndexerError(err, *f.indexerConfig, logger.GetLogsError) + timeout = backoffConfig.Duration() continue } @@ -150,23 +162,24 @@ func (f *LogFetcher) FetchLogs(ctx context.Context, chunks []*util.Chunk) ([]typ } } -func (f *LogFetcher) getAndUnpackLogs(ctx context.Context, chunks []*util.Chunk, backoffConfig *backoff.Backoff, startHeight, endHeight uint64) ([]types.Log, error) { - result, err := GetLogsInRange(ctx, f.backend, f.contractAddress, uint64(f.chainConfig.ChainID), chunks) +func (f *LogFetcher) getAndUnpackLogs(ctx context.Context, chunks []*util.Chunk, backoffConfig *backoff.Backoff) ([]types.Log, error) { + result, err := backend.GetLogsInRange(ctx, f.backend, f.indexerConfig.Addresses, uint64(f.indexerConfig.ChainID), chunks) if err != nil { backoffConfig.Duration() - LogEvent(WarnLevel, "Could not filter logs for range, retrying", LogData{"sh": startHeight, "ca": f.contractAddress, "eh": endHeight, "cid": f.chainConfig.ChainID, "e": err}) - return nil, err + return nil, fmt.Errorf("could not get logs: %w", err) } + var logs []types.Log resultIterator := result.Iterator() for !resultIterator.Done() { select { case <-ctx.Done(): + logger.ReportIndexerError(ctx.Err(), *f.indexerConfig, logger.GetLogsError) return nil, fmt.Errorf("context canceled while unpacking logs from request: %w", ctx.Err()) default: _, logChunk := resultIterator.Next() if logChunk == nil || len(*logChunk) == 0 { - LogEvent(WarnLevel, "empty subchunk", LogData{"sh": startHeight, "ca": f.contractAddress, "cid": f.chainConfig.ChainID, "eh": endHeight}) + logger.ReportIndexerError(fmt.Errorf("empty log chunk"), *f.indexerConfig, logger.EmptyGetLogsChunk) continue } @@ -176,3 +189,8 @@ func (f *LogFetcher) getAndUnpackLogs(ctx context.Context, chunks []*util.Chunk, return logs, nil } + +// GetFetchedLogsChan returns the fetchedLogsChan channel as a pointer for access by the indexer and tests. +func (f *LogFetcher) GetFetchedLogsChan() *chan types.Log { + return &f.fetchedLogsChan +} diff --git a/services/scribe/service/indexer/fetcher_test.go b/services/scribe/service/indexer/fetcher_test.go new file mode 100644 index 0000000000..04e66df423 --- /dev/null +++ b/services/scribe/service/indexer/fetcher_test.go @@ -0,0 +1,261 @@ +package indexer_test + +import ( + "context" + "fmt" + "github.com/brianvoe/gofakeit/v6" + "github.com/ethereum/go-ethereum/core/types" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/testutil" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + "time" + + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/synapsecns/sanguine/ethergo/backends/geth" + "github.com/synapsecns/sanguine/ethergo/chain/client/mocks" + etherMocks "github.com/synapsecns/sanguine/ethergo/mocks" + "github.com/synapsecns/sanguine/ethergo/util" + "github.com/synapsecns/sanguine/services/scribe/service/indexer" +) + +// TestFilterLogsMaxAttempts ensures after the maximum number of attempts, an error is returned. +func (x *IndexerSuite) TestFilterLogsMaxAttempts() { + x.T().Skip("flake") + chainID := big.NewInt(int64(1)) + simulatedChain := geth.NewEmbeddedBackendForChainID(x.GetTestContext(), x.T(), chainID) + simulatedClient, err := backend.DialBackend(x.GetTestContext(), simulatedChain.RPCAddress(), x.metrics) + Nil(x.T(), err) + mockFilterer := new(mocks.EVMClient) + contractAddress := etherMocks.MockAddress() + config := &scribeTypes.IndexerConfig{ + ChainID: 1, + GetLogsBatchAmount: 1, + GetLogsRange: 1, + Addresses: []common.Address{contractAddress}, + } + + rangeFilter := indexer.NewLogFetcher(simulatedClient, big.NewInt(1), big.NewInt(10), config) + + // Use the range filterer created above to create a mock log filter. + mockFilterer. + On("FilterLogs", mock.Anything, mock.Anything). + Return(nil, errors.New("I'm a test error")) + chunks := []*util.Chunk{{ + StartBlock: big.NewInt(1), + EndBlock: big.NewInt(10), + }} + logInfo, err := rangeFilter.FetchLogs(x.GetTestContext(), chunks) + Nil(x.T(), logInfo) + NotNil(x.T(), err) +} + +// TestGetChunkArr ensures that the batching orchestration function (collecting block range chunks into arrays) works properly. +func (x *IndexerSuite) TestGetChunkArr() { + chainID := big.NewInt(int64(1)) + simulatedChain := geth.NewEmbeddedBackendForChainID(x.GetTestContext(), x.T(), chainID) + simulatedClient, err := backend.DialBackend(x.GetTestContext(), simulatedChain.RPCAddress(), x.metrics) + Nil(x.T(), err) + contractAddress := etherMocks.MockAddress() + config := &scribeTypes.IndexerConfig{ + ChainID: 1, + ConcurrencyThreshold: 1, + GetLogsBatchAmount: 1, + GetLogsRange: 1, + Addresses: []common.Address{contractAddress}, + } + + startBlock := int64(1) + endBlock := int64(10) + + rangeFilter := indexer.NewLogFetcher(simulatedClient, big.NewInt(startBlock), big.NewInt(endBlock), config) + + numberOfRequests := int64(0) + for i := int64(0); i < endBlock; i++ { + chunks := rangeFilter.GetChunkArr() + if len(chunks) == 0 { + break + } + Equal(x.T(), len(chunks), int(config.GetLogsBatchAmount)) + numberOfRequests++ + } + Equal(x.T(), numberOfRequests, endBlock) + + // Test with a larger batch size + config.GetLogsBatchAmount = 4 + rangeFilter = indexer.NewLogFetcher(simulatedClient, big.NewInt(1), big.NewInt(10), config) + numberOfRequests = int64(0) + loopCount := endBlock/int64(config.GetLogsBatchAmount) + 1 + for i := int64(0); i < loopCount; i++ { + chunks := rangeFilter.GetChunkArr() + if len(chunks) == 0 { + break + } + if i < loopCount-1 { + Equal(x.T(), len(chunks), int(config.GetLogsBatchAmount)) + } else { + Equal(x.T(), len(chunks), int(endBlock%int64(config.GetLogsBatchAmount))) + } + numberOfRequests++ + } + Equal(x.T(), numberOfRequests, loopCount) + + // Test with a larger range size + config.GetLogsRange = 2 + rangeFilter = indexer.NewLogFetcher(simulatedClient, big.NewInt(1), big.NewInt(10), config) + numberOfRequests = int64(0) + loopCount = endBlock/int64(config.GetLogsBatchAmount*config.GetLogsRange) + 1 + for i := int64(0); i < loopCount; i++ { + chunks := rangeFilter.GetChunkArr() + if len(chunks) == 0 { + break + } + if i < loopCount-1 { + Equal(x.T(), len(chunks), int(config.GetLogsBatchAmount)) + } else { + Equal(x.T(), len(chunks), 1) + } + numberOfRequests++ + } + Equal(x.T(), numberOfRequests, loopCount) +} + +// TestGetChunkArr ensures that the batching orchestration function (collecting block range chunks into arrays) works properly. +func (x *IndexerSuite) TestFetchLogs() { + testBackend := geth.NewEmbeddedBackend(x.GetTestContext(), x.T()) + // start an omnirpc proxy and run 10 test transactions so we can batch call blocks 1-10 + var wg sync.WaitGroup + var testChainHandler *testutil.TestChainHandler + var err error + wg.Add(2) + + const desiredBlockHeight = 10 + + go func() { + defer wg.Done() + testChainHandler, err = testutil.PopulateWithLogs(x.GetTestContext(), x.T(), testBackend, desiredBlockHeight, []*testutil.DeployManager{x.manager}) + Nil(x.T(), err) + }() + + var host string + go func() { + defer wg.Done() + host = testutil.StartOmnirpcServer(x.GetTestContext(), x.T(), testBackend) + }() + + wg.Wait() + + scribeBackend, err := backend.DialBackend(x.GetTestContext(), host, x.metrics) + Nil(x.T(), err) + + chunks := []*util.Chunk{ + { + StartBlock: big.NewInt(1), + EndBlock: big.NewInt(2), + }, + { + StartBlock: big.NewInt(3), + EndBlock: big.NewInt(4), + }, + { + StartBlock: big.NewInt(5), + EndBlock: big.NewInt(6), + }, + { + StartBlock: big.NewInt(7), + EndBlock: big.NewInt(8), + }, + { + StartBlock: big.NewInt(9), + EndBlock: big.NewInt(10), + }, + } + chainID, err := scribeBackend.ChainID(x.GetTestContext()) + Nil(x.T(), err) + config := &scribeTypes.IndexerConfig{ + ChainID: uint32(chainID.Uint64()), + ConcurrencyThreshold: 1, + GetLogsBatchAmount: 1, + GetLogsRange: 2, + Addresses: testChainHandler.Addresses, + } + rangeFilter := indexer.NewLogFetcher(scribeBackend, big.NewInt(1), big.NewInt(desiredBlockHeight), config) + logs, err := rangeFilter.FetchLogs(x.GetTestContext(), chunks) + Nil(x.T(), err) + Equal(x.T(), 2, len(logs)) + + cancelCtx, cancel := context.WithCancel(x.GetTestContext()) + cancel() + + _, err = rangeFilter.FetchLogs(cancelCtx, chunks) + NotNil(x.T(), err) + Contains(x.T(), err.Error(), "context was canceled") +} + +// TestFetchLogsHighVolume tests the behavior of populating and consuming logs from the log fetcher in block ranges with many logs. +func (x *IndexerSuite) TestFetchLogsHighVolume() { + testBackend := geth.NewEmbeddedBackend(x.GetTestContext(), x.T()) + // start an omnirpc proxy and run 10 test transactions so we can batch call blocks 1-10 + var err error + host := testutil.StartOmnirpcServer(x.GetTestContext(), x.T(), testBackend) + + scribeBackend, err := backend.DialBackend(x.GetTestContext(), host, x.metrics) + Nil(x.T(), err) + + chainID, err := scribeBackend.ChainID(x.GetTestContext()) + Nil(x.T(), err) + config := &scribeTypes.IndexerConfig{ + ChainID: uint32(chainID.Uint64()), + ConcurrencyThreshold: 1, + GetLogsBatchAmount: 1, + GetLogsRange: 2, + StoreConcurrency: 6, + Addresses: []common.Address{common.BigToAddress(big.NewInt(1))}, + } + logFetcher := indexer.NewLogFetcher(scribeBackend, big.NewInt(1), big.NewInt(1000), config) + + logsChan := logFetcher.GetFetchedLogsChan() + + addContext, addCancel := context.WithTimeout(x.GetTestContext(), 20*time.Second) + defer addCancel() + numLogs := 0 + go func() { + for { + select { + case <-addContext.Done(): + // test done + close(*logsChan) + return + + case <-time.After(10 * time.Millisecond): + // add a log + randomTxHash := common.BigToHash(big.NewInt(gofakeit.Int64())) + randomLog := testutil.MakeRandomLog(randomTxHash) + *logsChan <- randomLog + numLogs++ + // check buffer + GreaterOrEqual(x.T(), config.StoreConcurrency, len(*logsChan)) + } + } + }() + var collectedLogs []types.Log + for { + select { + case <-x.GetTestContext().Done(): + Error(x.T(), fmt.Errorf("test context was canceled")) + case <-time.After(1000 * time.Millisecond): + log, ok := <-*logsChan + if !ok { + goto Done + } + collectedLogs = append(collectedLogs, log) + } + } +Done: + Equal(x.T(), numLogs, len(collectedLogs)) +} diff --git a/services/scribe/service/indexer/indexer.go b/services/scribe/service/indexer/indexer.go new file mode 100644 index 0000000000..a1f4ccec45 --- /dev/null +++ b/services/scribe/service/indexer/indexer.go @@ -0,0 +1,506 @@ +package indexer + +import ( + "context" + "errors" + "fmt" + "github.com/synapsecns/sanguine/services/scribe/backend" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + + "github.com/synapsecns/sanguine/services/scribe/logger" + "math/big" + "time" + + "github.com/lmittmann/w3" + "github.com/lmittmann/w3/module/eth" + "github.com/lmittmann/w3/w3types" + "github.com/synapsecns/sanguine/core/mapmutex" + "github.com/synapsecns/sanguine/core/metrics" + "go.opentelemetry.io/otel/attribute" + otelMetrics "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/trace" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + lru "github.com/hashicorp/golang-lru" + "github.com/jpillora/backoff" + "github.com/synapsecns/sanguine/services/scribe/config" + "github.com/synapsecns/sanguine/services/scribe/db" + "golang.org/x/sync/errgroup" +) + +// Indexer is a backfiller that fetches logs for a specific contract. +type Indexer struct { + // indexerConfig holds all the metadata needed for logging and indexing. + indexerConfig scribeTypes.IndexerConfig + // eventDB is the database to store event data in. + eventDB db.EventDB + // client is the client for filtering. + client []backend.ScribeBackend + // cache is a cache for txHashes. + cache *lru.Cache + // mux is the mutex used to prevent double inserting logs from the same tx + mux mapmutex.StringerMapMutex + // handler is the metrics handler for the scribe. + handler metrics.Handler + // blockMeter is an otel historgram for doing metrics on block heights by chain + blockMeter otelMetrics.Int64Histogram + // refreshRate is the rate at which the indexer will refresh when livefilling. + refreshRate uint64 + // toHead is a boolean signifying if the indexer is livefilling to the head. + toHead bool + // isBackfill is a boolean signifying if the indexer is backfilling (prevents last indexed from running) + isBackfill bool +} + +// retryTolerance is the number of times to retry a failed operation before rerunning the entire Backfill function. +const retryTolerance = 20 + +// txNotSupportedError is for handling the legacy Arbitrum tx type. +const txNotSupportedError = "transaction type not supported" + +// invalidTxVRSError is for handling Aurora VRS error. +const invalidTxVRSError = "invalid transaction v, r, s values" + +// txNotFoundError is for handling omniRPC errors for BSx. +const txNotFoundError = "not found" + +// txData returns the transaction data for a given transaction hash. +type txData struct { + receipt types.Receipt + transaction types.Transaction + blockHeader types.Header + success bool +} + +// errNoContinue indicates an error that is not recoverable, and should not be retried. +var errNoContinue = errors.New("encountered unreconcilable error, will not attempt to store tx") + +// errNoTx indicates a tx cannot be parsed, this is only returned when the tx doesn't match our data model. +var errNoTx = errors.New("tx is not supported by the client") + +// NewIndexer creates a new backfiller for a contract. +func NewIndexer(chainConfig config.ChainConfig, addresses []common.Address, eventDB db.EventDB, client []backend.ScribeBackend, handler metrics.Handler, blockMeter otelMetrics.Int64Histogram, toHead bool) (*Indexer, error) { + cache, err := lru.New(500) + if err != nil { + return nil, fmt.Errorf("could not initialize cache: %w", err) + } + + refreshRate := uint64(1) + if len(addresses) > 1 || len(addresses) == 0 { // livefill settings + chainConfig.GetLogsRange = chainConfig.LivefillRange + chainConfig.GetLogsBatchAmount = 1 + chainConfig.StoreConcurrency = 1 + chainConfig.ConcurrencyThreshold = 10000 + } else { + for i := range chainConfig.Contracts { // get the refresh rate for the contract + contract := chainConfig.Contracts[i] + // Refresh rate for more than one contract is 1 second, the refresh rate set in the config is used when it is the only contract. + if contract.Address == addresses[0].String() && contract.RefreshRate > 0 { + refreshRate = contract.RefreshRate + break + } + } + } + + indexerConfig := scribeTypes.IndexerConfig{ + Addresses: addresses, + GetLogsRange: chainConfig.GetLogsRange, + GetLogsBatchAmount: chainConfig.GetLogsBatchAmount, + StoreConcurrency: chainConfig.StoreConcurrency, + ChainID: chainConfig.ChainID, + ConcurrencyThreshold: chainConfig.ConcurrencyThreshold, + } + + return &Indexer{ + indexerConfig: indexerConfig, + eventDB: eventDB, + client: client, + cache: cache, + mux: mapmutex.NewStringerMapMutex(), + handler: handler, + blockMeter: blockMeter, + refreshRate: refreshRate, + toHead: toHead, + isBackfill: false, + }, nil +} + +// UpdateAddress updates the address arrays for the indexer. +func (x *Indexer) UpdateAddress(addresses []common.Address) { + x.indexerConfig.Addresses = addresses +} + +// SetToBackfill sets the indexer to backfill (will not update last indexed). +func (x *Indexer) SetToBackfill() { + x.isBackfill = true +} + +// GetIndexerConfig returns the indexer config. +func (x *Indexer) GetIndexerConfig() scribeTypes.IndexerConfig { + return x.indexerConfig +} + +// RefreshRate returns the refresh rate for the indexer. +func (x *Indexer) RefreshRate() uint64 { + return x.refreshRate +} + +// Index retrieves logs, receipts, and transactions for a contract from a given range and does so in the following manner. +// 1. Get logs for the contract in chunks of batch requests. +// 2. Iterate through each log's Tx Hash and performs the following +// - Get the receipt for each log and store it and all of its logs. +// - Get the transaction for each log and store it. +// +//nolint:gocognit, cyclop +func (x *Indexer) Index(parentCtx context.Context, startHeight uint64, endHeight uint64) (err error) { + ctx, span := x.handler.Tracer().Start(parentCtx, "contract.Backfill", trace.WithAttributes( + attribute.Int("chain", int(x.indexerConfig.ChainID)), + attribute.String("address", x.addressesToString(x.indexerConfig.Addresses)), + attribute.Int("start", int(startHeight)), + attribute.Int("end", int(endHeight)), + )) + + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + g, groupCtx := errgroup.WithContext(ctx) + + // For logging + x.indexerConfig.StartHeight = startHeight + x.indexerConfig.EndHeight = endHeight + + // Start fetching logs + logFetcher := NewLogFetcher(x.client[0], big.NewInt(int64(startHeight)), big.NewInt(int64(endHeight)), &x.indexerConfig) + logsChan := logFetcher.GetFetchedLogsChan() + g.Go(func() error { + return logFetcher.Start(groupCtx) + }) + // Reads from the local logsChan and stores the logs and associated receipts / txs. + g.Go(func() error { + concurrentCalls := 0 + gS, storeCtx := errgroup.WithContext(ctx) + // could change this to for - range + for { + select { + case <-groupCtx.Done(): + logger.ReportIndexerError(groupCtx.Err(), x.indexerConfig, logger.ContextCancelled) + return fmt.Errorf("context canceled while storing and retrieving logs: %w", groupCtx.Err()) + case log, ok := <-*logsChan: // empty log passed when ok is false. + if !ok { + return nil + } + concurrentCalls++ + gS.Go(func() error { + // another goroutine is already storing this receipt + locker, ok := x.mux.TryLock(log.TxHash) + if !ok { + return nil + } + defer locker.Unlock() + + // Check if the txHash has already been stored in the cache. + if _, ok := x.cache.Get(log.TxHash); ok { + return nil + } + + err := x.store(storeCtx, log) + if err != nil { + logger.ReportIndexerError(err, x.indexerConfig, logger.StoreError) + + return fmt.Errorf("could not store log: %w", err) + } + + return nil + }) + + // Checks if: + // 1. The number of concurrent calls is greater than the concurrency threshold. + // 2. The indexer's distance from the chaintip is within the concurrency ending threshold. + // If so, all the go routines are waited on and the last indexed block is stored. + if concurrentCalls >= x.indexerConfig.StoreConcurrency || x.indexerConfig.ConcurrencyThreshold > endHeight-log.BlockNumber { + if err = gS.Wait(); err != nil { + return fmt.Errorf("error waiting for go routines: %w", err) + } + + // reset group context and concurrent calls + gS, storeCtx = errgroup.WithContext(ctx) + concurrentCalls = 0 + + err = x.saveLastIndexed(storeCtx, log.BlockNumber) + if err != nil { + logger.ReportIndexerError(err, x.indexerConfig, logger.StoreError) + return fmt.Errorf("could not store last indexed: %w", err) + } + + x.blockMeter.Record(ctx, int64(log.BlockNumber), otelMetrics.WithAttributeSet( + attribute.NewSet(attribute.Int64("start_block", int64(startHeight)), attribute.Int64("chain_id", int64(x.indexerConfig.ChainID)))), + ) + } + } + } + }) + + err = g.Wait() + + if err != nil { + return fmt.Errorf("could not backfill contract: %w \nChain: %d\nLog 's Contract Address: %s\n ", err, x.indexerConfig.ChainID, x.indexerConfig.Addresses) + } + + err = x.saveLastIndexed(ctx, endHeight) + if err != nil { + logger.ReportIndexerError(err, x.indexerConfig, logger.StoreError) + return fmt.Errorf("could not store last indexed: %w", err) + } + + x.blockMeter.Record(ctx, int64(endHeight), otelMetrics.WithAttributeSet( + attribute.NewSet(attribute.Int64("start_block", int64(startHeight)), attribute.Int64("chain_id", int64(x.indexerConfig.ChainID)))), + ) + + return nil +} + +// TODO split two goroutines into sep functions for maintainability +// store stores the logs, receipts, and transactions for a tx hash. +// +//nolint:cyclop,gocognit,maintidx +func (x *Indexer) store(parentCtx context.Context, log types.Log) (err error) { + ctx, span := x.handler.Tracer().Start(parentCtx, "store", trace.WithAttributes( + attribute.String("contract", x.addressesToString(x.indexerConfig.Addresses)), + attribute.String("tx", log.TxHash.Hex()), + attribute.String("block", fmt.Sprintf("%d", log.BlockNumber)), + )) + + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + b := &backoff.Backoff{ + Factor: 2, + Jitter: true, + Min: 3 * time.Millisecond, + Max: 2 * time.Second, + } + + timeout := time.Duration(0) + tryCount := 0 + + var tx *txData + hasTX := true + +OUTER: + for { + select { + case <-ctx.Done(): + return fmt.Errorf("context canceled while storing logs/receipts: %w", ctx.Err()) + case <-time.After(timeout): + tryCount++ + + tx, err = x.fetchEventData(ctx, log.TxHash, log.BlockNumber) + if err != nil { + if errors.Is(err, errNoContinue) { + logger.ReportIndexerError(err, x.indexerConfig, logger.GetTxError) + return nil + } + + if errors.Is(err, errNoTx) { + logger.ReportIndexerError(err, x.indexerConfig, logger.GetTxError) + hasTX = false + break OUTER + } + + if tryCount > retryTolerance { + return fmt.Errorf("retry tolerance exceeded: %w", err) + } + + timeout = b.Duration() + continue + } + + break OUTER + } + } + + g, groupCtx := errgroup.WithContext(ctx) + g.Go(func() error { + // Store receipt in the EventDB. + if x.toHead { + err = x.eventDB.StoreReceiptAtHead(groupCtx, x.indexerConfig.ChainID, tx.receipt) + } else { + err = x.eventDB.StoreReceipt(groupCtx, x.indexerConfig.ChainID, tx.receipt) + } + if err != nil { + return fmt.Errorf("could not store receipt: %w", err) + } + return nil + }) + + if hasTX { + g.Go(func() error { + if x.toHead { + err = x.eventDB.StoreEthTxAtHead(groupCtx, &tx.transaction, x.indexerConfig.ChainID, log.BlockHash, log.BlockNumber, uint64(log.TxIndex)) + } else { + err = x.eventDB.StoreEthTx(groupCtx, &tx.transaction, x.indexerConfig.ChainID, log.BlockHash, log.BlockNumber, uint64(log.TxIndex)) + } + if err != nil { + return fmt.Errorf("could not store tx: %w", err) + } + return nil + }) + } + + g.Go(func() error { + logs, err := x.prunedReceiptLogs(tx.receipt) + if err != nil { + return err + } + if x.toHead { + err = x.eventDB.StoreLogsAtHead(groupCtx, x.indexerConfig.ChainID, logs...) + } else { + err = x.eventDB.StoreLogs(groupCtx, x.indexerConfig.ChainID, logs...) + } + if err != nil { + return fmt.Errorf("could not store receipt logs: %w", err) + } + + return nil + }) + + g.Go(func() error { + err := x.eventDB.StoreBlockTime(groupCtx, x.indexerConfig.ChainID, tx.blockHeader.Number.Uint64(), tx.blockHeader.Time) + if err != nil { + return fmt.Errorf("could not store receipt logs: %w", err) + } + return nil + }) + + err = g.Wait() + if err != nil { + return fmt.Errorf("could not store data: %w\n%s on chain %d from %d to %s", err, x.addressesToString(x.indexerConfig.Addresses), x.indexerConfig.ChainID, log.BlockNumber, log.TxHash.String()) + } + + x.cache.Add(log.TxHash, true) + return nil +} + +// prunedReceiptLogs gets all logs from a receipt and prunes null logs. +func (x *Indexer) prunedReceiptLogs(receipt types.Receipt) (logs []types.Log, err error) { + for i := range receipt.Logs { + log := receipt.Logs[i] + if log == nil { + return nil, fmt.Errorf("log is nil\nChain: %d\nTxHash: %s\nLog BlockNumber: %d\nLog 's Contract Address: %s\nContract Address: %s", x.indexerConfig.ChainID, log.TxHash.String(), log.BlockNumber, log.Address.String(), x.addressesToString(x.indexerConfig.Addresses)) + } + logs = append(logs, *log) + } + return logs, nil +} + +// fetchEventData tries to fetch a transaction from the cache, if it's not there it tries to fetch it from the database. +// nolint: cyclop +func (x *Indexer) fetchEventData(parentCtx context.Context, txhash common.Hash, blockNumber uint64) (tx *txData, err error) { + ctx, span := x.handler.Tracer().Start(parentCtx, "fetchEventData", trace.WithAttributes( + attribute.String("tx", txhash.Hex()), + attribute.String("block", fmt.Sprintf("%d", blockNumber)), + )) + + defer func() { + metrics.EndSpanWithErr(span, err) + }() + +OUTER: + // increasing this across more clients puts too much load on the server, results in failed requests. TODO investigate + for i := range x.client[0:1] { + tx = &txData{} + + calls := make([]w3types.Caller, 3) + + // setup referencable indexes so we can access errors from the calls + const ( + receiptIndex = 0 + txIndex = 1 + headerIndex = 2 + ) + + // get the transaction receipt + calls[receiptIndex] = eth.TxReceipt(txhash).Returns(&tx.receipt) + + // get the raw transaction + calls[txIndex] = eth.Tx(txhash).Returns(&tx.transaction) + + // get the block number + calls[headerIndex] = eth.HeaderByNumber(new(big.Int).SetUint64(blockNumber)).Returns(&tx.blockHeader) + + //nolint: nestif + if err := x.client[i].BatchWithContext(ctx, calls...); err != nil { + //nolint: errorlint + callErr, ok := err.(w3.CallErrors) + if !ok { + return nil, fmt.Errorf("could not parse errors: %w", err) + } + + if callErr[receiptIndex] != nil { + if callErr[receiptIndex].Error() == txNotFoundError { + logger.ReportIndexerError(fmt.Errorf(txNotFoundError), x.indexerConfig, logger.GetTxError) + continue OUTER + } + } + + if callErr[txIndex] != nil { + switch callErr[txIndex].Error() { + case txNotSupportedError: + logger.ReportIndexerError(fmt.Errorf(txNotSupportedError), x.indexerConfig, logger.GetTxError) + return tx, errNoTx + case invalidTxVRSError: + logger.ReportIndexerError(fmt.Errorf(invalidTxVRSError), x.indexerConfig, logger.GetTxError) + return tx, errNoTx + case txNotFoundError: + logger.ReportIndexerError(fmt.Errorf(txNotFoundError), x.indexerConfig, logger.GetTxError) + continue OUTER + } + } + + return nil, fmt.Errorf("could not get tx receipt: %w", err) + } + + tx.success = true + } + + if tx == nil || !tx.success { + return nil, fmt.Errorf("could not get tx data: %w", err) + } + + return tx, nil +} + +// addressesToString is a helper function for logging events. +func (x *Indexer) addressesToString(addresses []common.Address) string { + var output string + for i := range addresses { + if i == 0 { + output = addresses[i].String() + } else { + output = output + "," + addresses[i].String() + } + } + return output +} + +func (x *Indexer) saveLastIndexed(parentCtx context.Context, blockNumber uint64) error { + if !x.isBackfill { + var err error + var errMessage string + if x.toHead { + err = x.eventDB.StoreLastIndexed(parentCtx, common.Address{}, x.indexerConfig.ChainID, blockNumber, scribeTypes.LivefillAtHead) + errMessage = "could not store last indexed block while livefilling at head" + } else { + err = x.eventDB.StoreLastIndexedMultiple(parentCtx, x.indexerConfig.Addresses, x.indexerConfig.ChainID, blockNumber) + errMessage = "could not store last indexed blocks" + } + if err != nil { + logger.ReportIndexerError(err, x.indexerConfig, logger.StoreError) + return fmt.Errorf("%s: %w", errMessage, err) + } + } + return nil +} diff --git a/services/scribe/service/indexer/indexer_test.go b/services/scribe/service/indexer/indexer_test.go new file mode 100644 index 0000000000..864e3cc4bb --- /dev/null +++ b/services/scribe/service/indexer/indexer_test.go @@ -0,0 +1,617 @@ +package indexer_test + +import ( + "context" + "fmt" + "github.com/brianvoe/gofakeit/v6" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/synapsecns/sanguine/ethergo/backends/geth" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/config" + "github.com/synapsecns/sanguine/services/scribe/service/indexer" + "github.com/synapsecns/sanguine/services/scribe/testutil" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + "os" + "time" + + "sync" + + "github.com/synapsecns/sanguine/services/scribe/db" + "github.com/synapsecns/sanguine/services/scribe/db/mocks" + + "math/big" +) + +// TestFailedStore tests that the ChainBackfiller continues backfilling after a failed store. + +func (x *IndexerSuite) TestFailedStore() { + mockDB := new(mocks.EventDB) + mockDB. + // on a store receipt call + On("StoreReceipt", mock.Anything, mock.Anything, mock.Anything). + Return(fmt.Errorf("failed to store receipt")) + mockDB. + // on a store transaction call + On("StoreEthTx", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(fmt.Errorf("failed to store transaction")) + mockDB. + // on a store log call + On("StoreLogs", mock.Anything, mock.Anything, mock.Anything). + Return(fmt.Errorf("failed to store log")) + mockDB. + // on retrieve last indexed call + On("RetrieveLastIndexed", mock.Anything, mock.Anything, mock.Anything). + Return(uint64(0), nil) + + mockDB.On("StoreBlockTime", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + chainID := gofakeit.Uint32() + + simulatedChain := geth.NewEmbeddedBackendForChainID(x.GetTestContext(), x.T(), big.NewInt(int64(chainID))) + simulatedClient, err := backend.DialBackend(x.GetTestContext(), simulatedChain.RPCAddress(), x.metrics) + Nil(x.T(), err) + + simulatedChain.FundAccount(x.GetTestContext(), x.wallet.Address(), *big.NewInt(params.Ether)) + testContract, testRef := x.manager.GetTestContract(x.GetTestContext(), simulatedChain) + transactOpts := simulatedChain.GetTxContext(x.GetTestContext(), nil) + contractConfig := config.ContractConfig{ + Address: testContract.Address().String(), + StartBlock: 0, + } + simulatedChainArr := []backend.ScribeBackend{simulatedClient, simulatedClient} + chainConfig := config.ChainConfig{ + Confirmations: 1, + ChainID: chainID, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + Contracts: []config.ContractConfig{contractConfig}, + } + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contracts := []common.Address{common.HexToAddress(contractConfig.Address)} + indexer, err := indexer.NewIndexer(chainConfig, contracts, mockDB, simulatedChainArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Get the block that the last transaction was executed in. + txBlockNumber, err := testutil.GetTxBlockNumber(x.GetTestContext(), simulatedChain, tx) + Nil(x.T(), err) + err = indexer.Index(x.GetTestContext(), contractConfig.StartBlock, txBlockNumber) + NotNil(x.T(), err) + + // Check to ensure that StoreLastIndexed was never called. + mockDB.AssertNotCalled(x.T(), "StoreLastIndexed", mock.Anything, mock.Anything, mock.Anything, mock.Anything) +} + +// TestGetLogsSimulated tests the GetLogs function using a simulated blockchain. +// +//nolint:cyclop +func (x *IndexerSuite) TestGetLogsSimulated() { + // Get simulated blockchain, deploy the test contract, and set up test variables. + simulatedChain := geth.NewEmbeddedBackendForChainID(x.GetSuiteContext(), x.T(), big.NewInt(3)) + simulatedClient, err := backend.DialBackend(x.GetTestContext(), simulatedChain.RPCAddress(), x.metrics) + Nil(x.T(), err) + + simulatedChain.FundAccount(x.GetTestContext(), x.wallet.Address(), *big.NewInt(params.Ether)) + testContract, testRef := x.manager.GetTestContract(x.GetTestContext(), simulatedChain) + transactOpts := simulatedChain.GetTxContext(x.GetTestContext(), nil) + contractConfig := config.ContractConfig{ + Address: testContract.Address().String(), + StartBlock: 0, + } + simulatedChainArr := []backend.ScribeBackend{simulatedClient, simulatedClient} + chainConfig := config.ChainConfig{ + Confirmations: 1, + ChainID: 3, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + Contracts: []config.ContractConfig{contractConfig}, + } + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contracts := []common.Address{common.HexToAddress(contractConfig.Address)} + contractIndexer, err := indexer.NewIndexer(chainConfig, contracts, x.testDB, simulatedChainArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + // Emit five events, and then fetch them with GetLogs. The first two will be fetched first, + // then the last three after. + tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Get the block that the second transaction was executed in. + txBlockNumberA, err := testutil.GetTxBlockNumber(x.GetTestContext(), simulatedChain, tx) + Nil(x.T(), err) + + tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{10}, big.NewInt(11), big.NewInt(12)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(13), big.NewInt(14), big.NewInt(15)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Get the block that the last transaction was executed in. + txBlockNumberB, err := testutil.GetTxBlockNumber(x.GetTestContext(), simulatedChain, tx) + Nil(x.T(), err) + + // Get the logs for the first two events. + collectedLogs := []types.Log{} + indexerConfig := contractIndexer.GetIndexerConfig() + logFetcher := indexer.NewLogFetcher(simulatedChainArr[0], big.NewInt(int64(contractConfig.StartBlock)), big.NewInt(int64(txBlockNumberA)), &indexerConfig) + logsChan := logFetcher.GetFetchedLogsChan() + + fetchingContext, cancelFetching := context.WithTimeout(x.GetTestContext(), 10*time.Second) + + go func() { + _ = logFetcher.Start(fetchingContext) + }() + for { + select { + case <-x.GetTestContext().Done(): + x.T().Error("test timed out") + case log, ok := <-*logsChan: + if !ok { + goto Done + } + collectedLogs = append(collectedLogs, log) + } + } +Done: + cancelFetching() + // Check to see if 2 logs were collected. + Equal(x.T(), 2, len(collectedLogs)) + + // Get the logs for the last three events. + collectedLogs = []types.Log{} + logFetcher = indexer.NewLogFetcher(simulatedChainArr[0], big.NewInt(int64(txBlockNumberA+1)), big.NewInt(int64(txBlockNumberB)), &indexerConfig) + logsChan = logFetcher.GetFetchedLogsChan() + + fetchingContext, cancelFetching = context.WithTimeout(x.GetTestContext(), 10*time.Second) + go func() { + _ = logFetcher.Start(fetchingContext) + }() + for { + select { + case <-x.GetTestContext().Done(): + x.T().Error("test timed out") + case log, ok := <-*logsChan: + if !ok { + goto Done2 + } + collectedLogs = append(collectedLogs, log) + } + } +Done2: + cancelFetching() + // Check to see if 3 logs were collected. + Equal(x.T(), 3, len(collectedLogs)) +} + +// TestContractBackfill tests using a contractBackfiller for recording receipts and logs in a database. +func (x *IndexerSuite) TestContractBackfill() { + // Get simulated blockchain, deploy the test contract, and set up test variables. + simulatedChain := geth.NewEmbeddedBackendForChainID(x.GetSuiteContext(), x.T(), big.NewInt(142)) + simulatedClient, err := backend.DialBackend(x.GetTestContext(), simulatedChain.RPCAddress(), x.metrics) + Nil(x.T(), err) + + simulatedChain.FundAccount(x.GetTestContext(), x.wallet.Address(), *big.NewInt(params.Ether)) + testContract, testRef := x.manager.GetTestContract(x.GetTestContext(), simulatedChain) + transactOpts := simulatedChain.GetTxContext(x.GetTestContext(), nil) + + // Set config. + contractConfig := config.ContractConfig{ + Address: testContract.Address().String(), + StartBlock: 0, + } + + simulatedChainArr := []backend.ScribeBackend{simulatedClient, simulatedClient} + chainConfig := config.ChainConfig{ + ChainID: 142, + GetLogsBatchAmount: 1, + Confirmations: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + ConcurrencyThreshold: 100, + Contracts: []config.ContractConfig{contractConfig}, + } + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + contracts := []common.Address{common.HexToAddress(contractConfig.Address)} + contractIndexer, err := indexer.NewIndexer(chainConfig, contracts, + x.testDB, simulatedChainArr, x.metrics, blockHeightMeter, false) + x.Require().NoError(err) + + // Emit events for the backfiller to read. + tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(x.T(), err) + + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Emit two logs in one receipt. + tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) + Nil(x.T(), err) + + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Get the block that the last transaction was executed in. + txBlockNumber, err := testutil.GetTxBlockNumber(x.GetTestContext(), simulatedChain, tx) + Nil(x.T(), err) + + // Backfill the events. The `0` will be replaced with the startBlock from the config. + err = contractIndexer.Index(x.GetTestContext(), contractConfig.StartBlock, txBlockNumber) + Nil(x.T(), err) + + // Get all receipts. + receipts, err := x.testDB.RetrieveReceiptsWithFilter(x.GetTestContext(), db.ReceiptFilter{}, 1) + Nil(x.T(), err) + + // Check to see if 3 receipts were collected. + Equal(x.T(), 4, len(receipts)) + + // Get all logs. + logs, err := x.testDB.RetrieveLogsWithFilter(x.GetTestContext(), db.LogFilter{}, 1) + Nil(x.T(), err) + + // Check to see if 4 logs were collected. + Equal(x.T(), 5, len(logs)) + + // Check to see if the last receipt has two logs. + Equal(x.T(), 2, len(receipts[0].Logs)) + + // Ensure last indexed block is correct. + lastIndexed, err := x.testDB.RetrieveLastIndexed(x.GetTestContext(), testContract.Address(), uint32(testContract.ChainID().Uint64()), scribeTypes.IndexingConfirmed) + Nil(x.T(), err) + Equal(x.T(), txBlockNumber, lastIndexed) +} + +// TestContractBackfill tests using a contractBackfiller for recording receipts and logs in a database. +func (x *IndexerSuite) TestContractBackfillFromPreIndexed() { + // Get simulated blockchain, deploy the test contract, and set up test variables. + simulatedChain := geth.NewEmbeddedBackendForChainID(x.GetSuiteContext(), x.T(), big.NewInt(142)) + simulatedClient, err := backend.DialBackend(x.GetTestContext(), simulatedChain.RPCAddress(), x.metrics) + Nil(x.T(), err) + + simulatedChain.FundAccount(x.GetTestContext(), x.wallet.Address(), *big.NewInt(params.Ether)) + testContract, testRef := x.manager.GetTestContract(x.GetTestContext(), simulatedChain) + transactOpts := simulatedChain.GetTxContext(x.GetTestContext(), nil) + + // Set config. + contractConfig := config.ContractConfig{ + Address: testContract.Address().String(), + StartBlock: 0, + } + + simulatedChainArr := []backend.ScribeBackend{simulatedClient, simulatedClient} + chainConfig := config.ChainConfig{ + ChainID: 142, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + Confirmations: 1, + GetLogsRange: 1, + ConcurrencyThreshold: 1, + Contracts: []config.ContractConfig{contractConfig}, + } + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contracts := []common.Address{common.HexToAddress(contractConfig.Address)} + backfiller, err := indexer.NewIndexer(chainConfig, contracts, x.testDB, simulatedChainArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + // 1 log 1 receipt: r:1 l:1 + tx, err := testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // 1 log 1 receipt: r:2 l:2 + tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{4}, big.NewInt(5), big.NewInt(6)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // 2 logs 1 receipt: r:3 l:4 + tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(7), big.NewInt(8), big.NewInt(9)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Get the block that the last transaction was executed in. + txBlockNumber, err := testutil.GetTxBlockNumber(x.GetTestContext(), simulatedChain, tx) + Nil(x.T(), err) + err = x.testDB.StoreLastIndexed(x.GetTestContext(), common.HexToAddress(contractConfig.Address), chainConfig.ChainID, txBlockNumber, false) + Nil(x.T(), err) + + // 1 log 1 receipt: r:4 l:5 + tx, err = testRef.EmitEventA(transactOpts.TransactOpts, big.NewInt(10), big.NewInt(11), big.NewInt(12)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // 1 log 1 receipt: r:5 l:6 + tx, err = testRef.EmitEventB(transactOpts.TransactOpts, []byte{13}, big.NewInt(14), big.NewInt(15)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // 2 logs 1 receipt: r:6 l:8 + tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(16), big.NewInt(17), big.NewInt(18)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // 2 logs 1 receipt: r:7 l:10 + tx, err = testRef.EmitEventAandB(transactOpts.TransactOpts, big.NewInt(19), big.NewInt(20), big.NewInt(21)) + Nil(x.T(), err) + simulatedChain.WaitForConfirmation(x.GetTestContext(), tx) + + // Get the block that the last transaction was executed in. + txBlockNumber, err = testutil.GetTxBlockNumber(x.GetTestContext(), simulatedChain, tx) + Nil(x.T(), err) + + err = backfiller.Index(x.GetTestContext(), contractConfig.StartBlock, txBlockNumber) + Nil(x.T(), err) + + // Get all receipts. + receipts, err := x.testDB.RetrieveReceiptsWithFilter(x.GetTestContext(), db.ReceiptFilter{}, 1) + Nil(x.T(), err) + Equal(x.T(), 7, len(receipts)) + + // Get all logs. + logs, err := x.testDB.RetrieveLogsWithFilter(x.GetTestContext(), db.LogFilter{}, 1) + Nil(x.T(), err) + + Equal(x.T(), 10, len(logs)) + + // Check to see if the last receipt has two logs (emit a and b). + Equal(x.T(), 2, len(receipts[0].Logs)) + + // Ensure last indexed block is correct. + lastIndexed, err := x.testDB.RetrieveLastIndexed(x.GetTestContext(), testContract.Address(), uint32(testContract.ChainID().Uint64()), scribeTypes.IndexingConfirmed) + Nil(x.T(), err) + Equal(x.T(), txBlockNumber, lastIndexed) +} + +func (x *IndexerSuite) TestGetLogs() { + const desiredBlockHeight = 10 + + var testChainHandler *testutil.TestChainHandler + var err error + var wg sync.WaitGroup + + wg.Add(2) + testBackend := geth.NewEmbeddedBackend(x.GetTestContext(), x.T()) + + go func() { + defer wg.Done() + testChainHandler, err = testutil.PopulateWithLogs(x.GetTestContext(), x.T(), testBackend, desiredBlockHeight, []*testutil.DeployManager{x.manager}) + Nil(x.T(), err) + }() + + var host string + go func() { + defer wg.Done() + host = testutil.StartOmnirpcServer(x.GetTestContext(), x.T(), testBackend) + }() + + wg.Wait() + + scribeBackend, err := backend.DialBackend(x.GetTestContext(), host, x.metrics) + Nil(x.T(), err) + simulatedChainArr := []backend.ScribeBackend{scribeBackend, scribeBackend} + + chainID, err := scribeBackend.ChainID(x.GetTestContext()) + Nil(x.T(), err) + + var contractConfigs []config.ContractConfig + addresses := testChainHandler.Addresses + for _, address := range addresses { + contractConfig := config.ContractConfig{ + Address: address.String(), + } + contractConfigs = append(contractConfigs, contractConfig) + } + + chainConfig := config.ChainConfig{ + ChainID: uint32(chainID.Uint64()), + Confirmations: 1, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + Contracts: contractConfigs, + } + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contractBackfiller, err := indexer.NewIndexer(chainConfig, addresses, x.testDB, simulatedChainArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + startHeight, endHeight := uint64(1), uint64(10) + err = contractBackfiller.Index(x.GetTestContext(), startHeight, endHeight) + Nil(x.T(), err) + + logs, err := x.testDB.RetrieveLogsWithFilter(x.GetTestContext(), db.LogFilter{}, 1) + Equal(x.T(), 2, len(logs)) + + // test error handling + cancelCtx, cancel := context.WithCancel(x.GetTestContext()) + cancel() + err = contractBackfiller.Index(cancelCtx, endHeight, endHeight+10) + NotNil(x.T(), err) +} + +// TestTxTypeNotSupported tests how the contract backfiller handles a transaction type that is not supported. +// +// nolint:dupl +func (x *IndexerSuite) TestTxTypeNotSupported() { + if os.Getenv("CI") != "" { + x.T().Skip("Network test flake") + } + + var backendClient backend.ScribeBackend + omnirpcURL := "https://rpc.interoperability.institute/confirmations/1/rpc/42161" + backendClient, err := backend.DialBackend(x.GetTestContext(), omnirpcURL, x.metrics) + Nil(x.T(), err) + + // This config is using this block https://arbiscan.io/block/6262099 + // and this tx https://arbiscan.io/tx/0x8800222adf9578fb576db0bd7fb4860fe89932549be084a3313939c03e4d279d + // with a unique Arbitrum type to verify that anomalous tx type is handled correctly. + contractConfig := config.ContractConfig{ + Address: "0xA67b7147DcE20D6F25Fd9ABfBCB1c3cA74E11f0B", + StartBlock: 6262099, + } + + chainConfig := config.ChainConfig{ + ChainID: 42161, + Confirmations: 1, + GetLogsRange: 1, + GetLogsBatchAmount: 1, + Contracts: []config.ContractConfig{contractConfig}, + } + + addresses := []common.Address{common.HexToAddress(contractConfig.Address)} + backendClientArr := []backend.ScribeBackend{backendClient, backendClient} + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contractIndexer, err := indexer.NewIndexer(chainConfig, addresses, x.testDB, backendClientArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + err = contractIndexer.Index(x.GetTestContext(), contractConfig.StartBlock, contractConfig.StartBlock+1) + Nil(x.T(), err) + + logs, err := x.testDB.RetrieveLogsWithFilter(x.GetTestContext(), db.LogFilter{}, 1) + Nil(x.T(), err) + Equal(x.T(), 4, len(logs)) + receipts, err := x.testDB.RetrieveReceiptsWithFilter(x.GetTestContext(), db.ReceiptFilter{}, 1) + Nil(x.T(), err) + Equal(x.T(), 1, len(receipts)) +} + +// TestTxTypeNotSupported tests how the contract indexerer handles a transaction type that is not supported. +// +// nolint:dupl +func (x IndexerSuite) TestInvalidTxVRS() { + if os.Getenv("CI") != "" { + x.T().Skip("Network test flake") + } + + var backendClient backend.ScribeBackend + omnirpcURL := "https://rpc.interoperability.institute/confirmations/1/rpc/1313161554" + backendClient, err := backend.DialBackend(x.GetTestContext(), omnirpcURL, x.metrics) + Nil(x.T(), err) + + // This config is using this block https://aurorascan.dev/block/58621373 + // and this tx https://aurorascan.dev/tx/0x687282d7bd6c3d591f9ad79784e0983afabcac2a9074d368b7ca3d7caf4edee5 + // to test handling of the v,r,s tx not found error. + contractConfig := config.ContractConfig{ + Address: "0xaeD5b25BE1c3163c907a471082640450F928DDFE", + StartBlock: 58621373, + } + + chainConfig := config.ChainConfig{ + ChainID: 1313161554, + Confirmations: 1, + GetLogsRange: 1, + GetLogsBatchAmount: 1, + Contracts: []config.ContractConfig{contractConfig}, + } + addresses := []common.Address{common.HexToAddress(contractConfig.Address)} + + backendClientArr := []backend.ScribeBackend{backendClient, backendClient} + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contractIndexer, err := indexer.NewIndexer(chainConfig, addresses, x.testDB, backendClientArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + err = contractIndexer.Index(x.GetTestContext(), contractConfig.StartBlock, contractConfig.StartBlock+1) + Nil(x.T(), err) + + logs, err := x.testDB.RetrieveLogsWithFilter(x.GetTestContext(), db.LogFilter{}, 1) + Nil(x.T(), err) + Equal(x.T(), 9, len(logs)) + receipts, err := x.testDB.RetrieveReceiptsWithFilter(x.GetTestContext(), db.ReceiptFilter{}, 1) + Nil(x.T(), err) + Equal(x.T(), 1, len(receipts)) +} + +func (x *IndexerSuite) TestLargeVolumeIndexer() { + if os.Getenv("CI") != "" { + x.T().Skip("Long running test") + } + const desiredBlockHeight = 20 + var testChainHandler *testutil.TestChainHandler + var err error + var wg sync.WaitGroup + + wg.Add(2) + testBackend := geth.NewEmbeddedBackend(x.GetTestContext(), x.T()) + + go func() { + defer wg.Done() + testChainHandler, err = testutil.PopulateWithLogs(x.GetTestContext(), x.T(), testBackend, desiredBlockHeight, []*testutil.DeployManager{x.manager}) + Nil(x.T(), err) + }() + + var host string + go func() { + defer wg.Done() + host = testutil.StartOmnirpcServer(x.GetTestContext(), x.T(), testBackend) + }() + + wg.Wait() + + scribeBackend, err := backend.DialBackend(x.GetTestContext(), host, x.metrics) + Nil(x.T(), err) + simulatedChainArr := []backend.ScribeBackend{scribeBackend, scribeBackend} + + chainID, err := scribeBackend.ChainID(x.GetTestContext()) + Nil(x.T(), err) + + contractAddress := testChainHandler.Addresses[0] + contractConfigs := []config.ContractConfig{ + {Address: contractAddress.String()}, + } + addresses := testChainHandler.Addresses + + chainConfig := config.ChainConfig{ + ChainID: uint32(chainID.Uint64()), + Confirmations: 1, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + Contracts: contractConfigs, + } + blockHeightMeter, err := x.metrics.Metrics().NewHistogram(fmt.Sprint("scribe_block_meter", chainConfig.ChainID), "block_histogram", "a block height meter", "blocks") + Nil(x.T(), err) + + contractBackfiller, err := indexer.NewIndexer(chainConfig, addresses, x.testDB, simulatedChainArr, x.metrics, blockHeightMeter, false) + Nil(x.T(), err) + + endHeight, err := scribeBackend.BlockNumber(x.GetTestContext()) + Nil(x.T(), err) + err = contractBackfiller.Index(x.GetTestContext(), uint64(1), endHeight) + Nil(x.T(), err) + + logs, err := testutil.GetLogsUntilNoneLeft(x.GetTestContext(), x.testDB, db.LogFilter{}) + Equal(x.T(), int(testChainHandler.EventsEmitted[contractAddress]), len(logs)) +} diff --git a/services/scribe/service/indexer/suite_test.go b/services/scribe/service/indexer/suite_test.go new file mode 100644 index 0000000000..ef6b11c148 --- /dev/null +++ b/services/scribe/service/indexer/suite_test.go @@ -0,0 +1,63 @@ +package indexer_test + +import ( + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/core/metrics/localmetrics" + "github.com/synapsecns/sanguine/services/scribe/metadata" + "testing" + "time" + + "github.com/Flaque/filet" + . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/synapsecns/sanguine/core/testsuite" + "github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner" + "github.com/synapsecns/sanguine/ethergo/signer/wallet" + "github.com/synapsecns/sanguine/services/scribe/db" + "github.com/synapsecns/sanguine/services/scribe/db/datastore/sql/sqlite" + "github.com/synapsecns/sanguine/services/scribe/testutil" +) + +type IndexerSuite struct { + *testsuite.TestSuite + testDB db.EventDB + manager *testutil.DeployManager + wallet wallet.Wallet + signer *localsigner.Signer + metrics metrics.Handler +} + +// NewIndexerSuite creates a new indexer test suite. +func NewIndexerSuite(tb testing.TB) *IndexerSuite { + tb.Helper() + return &IndexerSuite{ + TestSuite: testsuite.NewTestSuite(tb), + } +} + +// SetupTest sets up the test suite. +func (x *IndexerSuite) SetupTest() { + x.TestSuite.SetupTest() + x.SetTestTimeout(time.Minute * 10) + sqliteStore, err := sqlite.NewSqliteStore(x.GetTestContext(), filet.TmpDir(x.T(), ""), x.metrics, false) + Nil(x.T(), err) + x.testDB = sqliteStore + x.manager = testutil.NewDeployManager(x.T()) + x.wallet, err = wallet.FromRandom() + Nil(x.T(), err) + x.signer = localsigner.NewSigner(x.wallet.PrivateKey()) +} + +func (x *IndexerSuite) SetupSuite() { + x.TestSuite.SetupSuite() + localmetrics.SetupTestJaeger(x.GetSuiteContext(), x.T()) + + var err error + x.metrics, err = metrics.NewByType(x.GetSuiteContext(), metadata.BuildInfo(), metrics.Jaeger) + Nil(x.T(), err) +} + +// TestIndexerSuite tests the indexer suite. +func TestIndexerSuite(t *testing.T) { + suite.Run(t, NewIndexerSuite(t)) +} diff --git a/services/scribe/service/scribe.go b/services/scribe/service/scribe.go new file mode 100644 index 0000000000..c19b5ddc97 --- /dev/null +++ b/services/scribe/service/scribe.go @@ -0,0 +1,107 @@ +package service + +import ( + "context" + "fmt" + "github.com/jpillora/backoff" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/config" + "github.com/synapsecns/sanguine/services/scribe/db" + "github.com/synapsecns/sanguine/services/scribe/logger" + otelMetrics "go.opentelemetry.io/otel/metric" + "time" + + "golang.org/x/sync/errgroup" +) + +// Scribe is a live scribe that logs all event data. +type Scribe struct { + // eventDB is the database to store event data in. + eventDB db.EventDB + // clients is a mapping of chain IDs -> clients. + clients map[uint32][]backend.ScribeBackend + // chainIndexers are the indexers for the scribe. + chainIndexers map[uint32]*ChainIndexer + // config is the config for the scribe. + config config.Config + // handler is the metrics handler for the scribe. + handler metrics.Handler + // reorgMeters holds a otel counter meter for reorgs for each chain + reorgMeters map[uint32]otelMetrics.Int64Counter +} + +// NewScribe creates a new scribe. +func NewScribe(eventDB db.EventDB, clients map[uint32][]backend.ScribeBackend, config config.Config, handler metrics.Handler) (*Scribe, error) { + chainIndexers := make(map[uint32]*ChainIndexer) + for i := range config.Chains { + chainConfig := config.Chains[i] + chainIndexer, err := NewChainIndexer(eventDB, clients[chainConfig.ChainID], chainConfig, handler) + if err != nil { + return nil, fmt.Errorf("could not create chain indexer: %w", err) + } + chainIndexers[chainConfig.ChainID] = chainIndexer + } + + return &Scribe{ + eventDB: eventDB, + clients: clients, + chainIndexers: chainIndexers, + config: config, + handler: handler, + reorgMeters: make(map[uint32]otelMetrics.Int64Counter), + }, nil +} + +// Start starts the scribe. A chain indexer is spun up for each chain, and a indexer is spun up for +// each contract on that chain. There is an indexer for livefilling all contracts and indexer for livefilling at the tip as well. +// +//nolint:cyclop +func (s Scribe) Start(ctx context.Context) error { + g, groupCtx := errgroup.WithContext(ctx) + b := backoff.Backoff{ + Factor: 2, + Jitter: true, + Min: 1 * time.Second, + Max: 10 * time.Second, + } + retryRate := time.Second * 0 + for i := range s.config.Chains { + chainConfig := s.config.Chains[i] + chainID := chainConfig.ChainID + + // Run chain indexer for each chain + g.Go(func() error { + // Each chain gets its own context so it can retry on its own if there is a fatal error. + // If the global scribe context fails, all chains will fail. + chainCtx, cancelChain := context.WithCancel(ctx) + defer cancelChain() + for { + select { + case <-groupCtx.Done(): // Global context cancel, destroy all chain indexers. + cancelChain() // redundant, but clean. + return fmt.Errorf("global scribe context cancel %w", groupCtx.Err()) + case <-chainCtx.Done(): // Chain level context cancel, retry and recreate context. + logger.ReportScribeError(fmt.Errorf("chain level scribe context cancel"), chainID, logger.ContextCancelled) + chainCtx, cancelChain = context.WithCancel(ctx) + retryRate = b.Duration() + continue + case <-time.After(retryRate): + err := s.chainIndexers[chainID].Index(groupCtx) + if err != nil { + logger.ReportScribeError(fmt.Errorf("error running chain indexer"), chainID, logger.FatalScribeError) + retryRate = b.Duration() + continue + } + cancelChain() + return nil // This shouldn't really ever be hit + } + } + }) + } + if err := g.Wait(); err != nil { + return fmt.Errorf("scribe failed: %w", err) + } + + return nil +} diff --git a/services/scribe/service/scribe_test.go b/services/scribe/service/scribe_test.go new file mode 100644 index 0000000000..1121c70d47 --- /dev/null +++ b/services/scribe/service/scribe_test.go @@ -0,0 +1,477 @@ +package service_test + +import ( + "context" + "encoding/json" + "fmt" + "github.com/brianvoe/gofakeit/v6" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/jpillora/backoff" + . "github.com/stretchr/testify/assert" + "github.com/synapsecns/sanguine/ethergo/backends/geth" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/config" + "github.com/synapsecns/sanguine/services/scribe/db" + "github.com/synapsecns/sanguine/services/scribe/service" + scribeTypes "github.com/synapsecns/sanguine/services/scribe/types" + + "github.com/synapsecns/sanguine/services/scribe/db/datastore/sql/base" + "github.com/synapsecns/sanguine/services/scribe/logger" + "github.com/synapsecns/sanguine/services/scribe/testutil" + "math/big" + "net/http" + "os" + "strconv" + "strings" + "time" +) + +// Spins up three chains with three contracts on each. Each contract emits events across a span of 20 blocks. +// The generated chains and contracts are fed into a new scribe instance, which is then queried for logs. +func (s *ScribeSuite) TestSimulatedScribe() { + if os.Getenv("CI") != "" { + s.T().Skip("Test flake: 20 sec of livefilling may fail on CI") + } + const numberOfContracts = 3 + const desiredBlockHeight = 20 + chainIDs := []uint32{gofakeit.Uint32(), gofakeit.Uint32(), gofakeit.Uint32()} + chainBackends := make(map[uint32]geth.Backend) + for i := range chainIDs { + newBackend := geth.NewEmbeddedBackendForChainID(s.GetTestContext(), s.T(), big.NewInt(int64(chainIDs[i]))) + chainBackends[chainIDs[i]] = *newBackend + } + + managers := []*testutil.DeployManager{s.manager} + if numberOfContracts > 1 { + for i := 1; i < numberOfContracts; i++ { + managers = append(managers, testutil.NewDeployManager(s.T())) + } + } + + testChainHandlerMap, chainBackendMap, err := testutil.PopulateChainsWithLogs(s.GetTestContext(), s.T(), chainBackends, desiredBlockHeight, managers, s.nullMetrics) + Nil(s.T(), err) + + // Build scribe config + var chainConfigs []config.ChainConfig + for chainID, testChainHandler := range testChainHandlerMap { + contractConfigs := config.ContractConfigs{} + for i := range testChainHandler.Addresses { + contractConfig := config.ContractConfig{ + Address: testChainHandler.Addresses[i].String(), + } + contractConfigs = append(contractConfigs, contractConfig) + } + + chainConfig := config.ChainConfig{ + ChainID: chainID, + Confirmations: 0, + GetLogsBatchAmount: 1, + StoreConcurrency: 1, + GetLogsRange: 1, + Contracts: contractConfigs, + } + chainConfigs = append(chainConfigs, chainConfig) + } + + scribeConfig := config.Config{ + Chains: chainConfigs, + } + + scribe, err := service.NewScribe(s.testDB, chainBackendMap, scribeConfig, s.nullMetrics) + Nil(s.T(), err) + killableContext, cancel := context.WithTimeout(s.GetTestContext(), 20*time.Second) + defer cancel() + _ = scribe.Start(killableContext) + + // Check that the events were recorded. + for _, chainConfig := range scribeConfig.Chains { + for _, contractConfig := range chainConfig.Contracts { + // Check the storage of logs. + logFilter := db.LogFilter{ + ChainID: chainConfig.ChainID, + ContractAddress: contractConfig.Address, + } + logs, err := s.testDB.RetrieveLogsWithFilter(s.GetTestContext(), logFilter, 1) + Nil(s.T(), err) + Equal(s.T(), 4, len(logs)) + lastIndexed, err := s.testDB.RetrieveLastIndexed(s.GetTestContext(), common.HexToAddress(contractConfig.Address), chainConfig.ChainID, scribeTypes.IndexingConfirmed) + Nil(s.T(), err) + LessOrEqual(s.T(), desiredBlockHeight, int(lastIndexed)) + } + // Check the storage of receipts. + receiptFilter := db.ReceiptFilter{ + ChainID: chainConfig.ChainID, + } + receipts, err := s.testDB.RetrieveReceiptsWithFilter(s.GetTestContext(), receiptFilter, 1) + Nil(s.T(), err) + Equal(s.T(), 12, len(receipts)) + } +} + +// TestLivefillParity runs livefill on certain prod chains. Then it checks parity with that chain's block explorer API. +// +// nolint:gocognit,cyclop,maintidx +func (s *ScribeSuite) TestLivefillParity() { + if os.Getenv("CI") != "" { + s.T().Skip("Network test flake") + } + const blockRange = uint64(100) + const globalConfirmations = uint64(200) + // ethRPCURL := "https://1rpc.io/eth" + // arbRPCURL := "https://endpoints.omniatech.io/v1/arbitrum/one/public" + // avaxRPCURL := "https://avalanche.public-rpc.com" + + ethRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/1" + arbRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/42161" + maticRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/137" + avaxRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/43114" + bscRPCURL := "https://rpc.interoperability.institute/confirmations/1/rpc/56" + + ethClient, err := backend.DialBackend(s.GetTestContext(), ethRPCURL, s.nullMetrics) + Nil(s.T(), err) + arbClient, err := backend.DialBackend(s.GetTestContext(), arbRPCURL, s.nullMetrics) + Nil(s.T(), err) + maticClient, err := backend.DialBackend(s.GetTestContext(), maticRPCURL, s.nullMetrics) + Nil(s.T(), err) + avaxClient, err := backend.DialBackend(s.GetTestContext(), avaxRPCURL, s.nullMetrics) + Nil(s.T(), err) + bscClient, err := backend.DialBackend(s.GetTestContext(), bscRPCURL, s.nullMetrics) + Nil(s.T(), err) + + ethID := uint32(1) + bscID := uint32(56) + arbID := uint32(42161) + maticID := uint32(137) + avaxID := uint32(43114) + chains := []uint32{ethID, bscID, arbID, maticID, avaxID} + + // Get the current block for each chain. + ethCurrentBlock, err := ethClient.BlockNumber(s.GetTestContext()) + Nil(s.T(), err) + ethCurrentBlock -= globalConfirmations + arbCurrentBlock, err := arbClient.BlockNumber(s.GetTestContext()) + Nil(s.T(), err) + arbCurrentBlock -= globalConfirmations + maticCurrentBlock, err := maticClient.BlockNumber(s.GetTestContext()) + Nil(s.T(), err) + maticCurrentBlock -= globalConfirmations + avaxCurrentBlock, err := avaxClient.BlockNumber(s.GetTestContext()) + Nil(s.T(), err) + avaxCurrentBlock -= globalConfirmations + bscCurrentBlock, err := bscClient.BlockNumber(s.GetTestContext()) + Nil(s.T(), err) + bscCurrentBlock -= globalConfirmations + + latestBlocks := map[uint32]uint64{ + ethID: ethCurrentBlock, + arbID: arbCurrentBlock, + maticID: maticCurrentBlock, + avaxID: avaxCurrentBlock, + bscID: bscCurrentBlock, + } + clients := map[uint32][]backend.ScribeBackend{ + ethID: {ethClient, ethClient}, + bscID: {bscClient, bscClient}, + arbID: {arbClient, arbClient}, + maticID: {maticClient, maticClient}, + avaxID: {avaxClient, avaxClient}, + } + + apiURLs := map[uint32]string{ + ethID: "https://api.etherscan.io/api", + arbID: "https://api.arbiscan.io/api", + avaxID: "https://api.snowtrace.io/api", + bscID: "https://api.bscscan.com/api", + maticID: "https://api.polygonscan.com/api", + } + scribeConfig := config.Config{ + RefreshRate: 1, + Chains: []config.ChainConfig{ + { + ChainID: ethID, + Confirmations: 0, + GetLogsRange: 50, + GetLogsBatchAmount: 3, + GetBlockBatchAmount: 10, + ConcurrencyThreshold: 20000, + LivefillThreshold: 100, + Contracts: []config.ContractConfig{ + { + Address: "0x2796317b0fF8538F253012862c06787Adfb8cEb6", + StartBlock: ethCurrentBlock - blockRange, + }, + { + Address: "0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8", + StartBlock: ethCurrentBlock - blockRange, + }, + }, + }, + { + ChainID: bscID, + Confirmations: 0, + GetLogsRange: 50, + GetLogsBatchAmount: 3, + GetBlockBatchAmount: 10, + ConcurrencyThreshold: 20000, + LivefillThreshold: 100, + Contracts: []config.ContractConfig{ + { + Address: "0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13", + StartBlock: bscCurrentBlock - blockRange, + }, + { + Address: "0x930d001b7efb225613aC7F35911c52Ac9E111Fa9", + StartBlock: bscCurrentBlock - blockRange, + }, + }, + }, + { + ChainID: arbID, + Confirmations: 0, + GetLogsRange: 50, + GetLogsBatchAmount: 3, + GetBlockBatchAmount: 10, + ConcurrencyThreshold: 20000, + LivefillThreshold: 100, + Contracts: []config.ContractConfig{ + { + Address: "0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9", + StartBlock: arbCurrentBlock - blockRange, + }, + { + Address: "0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40", + StartBlock: arbCurrentBlock - blockRange, + }, + }, + }, + { + ChainID: maticID, + Confirmations: 0, + GetLogsRange: 50, + GetLogsBatchAmount: 3, + GetBlockBatchAmount: 10, + ConcurrencyThreshold: 20000, + LivefillThreshold: 100, + Contracts: []config.ContractConfig{ + { + Address: "0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280", + StartBlock: maticCurrentBlock - blockRange, + }, + { + Address: "0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5", + StartBlock: maticCurrentBlock - blockRange, + }, + }, + }, + { + ChainID: avaxID, + Confirmations: 0, + GetLogsRange: 50, + GetLogsBatchAmount: 3, + GetBlockBatchAmount: 10, + ConcurrencyThreshold: 20000, + LivefillThreshold: 100, + Contracts: []config.ContractConfig{ + { + Address: "0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE", + StartBlock: avaxCurrentBlock - blockRange, + }, + { + Address: "0x77a7e60555bC18B4Be44C181b2575eee46212d44", + StartBlock: avaxCurrentBlock - blockRange, + }, + }, + }, + }, + } + + scribe, err := service.NewScribe(s.testDB, clients, scribeConfig, s.nullMetrics) + Nil(s.T(), err) + + killableContext, cancel := context.WithCancel(s.GetTestContext()) + + go func() { + _ = scribe.Start(killableContext) + }() + + doneChan := make(chan bool, len(chains)) + + for i := range chains { + go func(index int) { + for { + allContractsBackfilled := true + chain := scribeConfig.Chains[index] + for _, contract := range chain.Contracts { + currentBlock, err := s.testDB.RetrieveLastIndexed(s.GetTestContext(), common.HexToAddress(contract.Address), chain.ChainID, scribeTypes.IndexingConfirmed) + + Nil(s.T(), err) + if currentBlock <= latestBlocks[chain.ChainID] { + allContractsBackfilled = false + } + } + if allContractsBackfilled { + doneChan <- true + fmt.Println("Done with chain", chain.ChainID, "index", index, "of", len(chains), "chains") + + return + } + time.Sleep(time.Second) + } + }(i) + } + + for range chains { + <-doneChan + } + cancel() + for i := range chains { + chain := scribeConfig.Chains[i] + for _, contract := range chain.Contracts { + logFilter := db.LogFilter{ + ChainID: chains[i], + ContractAddress: contract.Address, + } + fromBlock := latestBlocks[chains[i]] - blockRange + toBlock := latestBlocks[chains[i]] + dbLogCount := 0 + var dbLogs []*types.Log + dbLogCount, dbLogs, err = getLogAmount(s.GetTestContext(), s.testDB, logFilter, fromBlock, toBlock) + Nil(s.T(), err) + + txs := make(map[int64]string) + explorerLogCount := 0 + explorerLogCount, err = getLogs(s.GetTestContext(), contract.Address, fromBlock, toBlock, apiURLs[chain.ChainID], &txs) + Nil(s.T(), err) + + for k := range dbLogs { + logBlockNumber := int64(dbLogs[k].BlockNumber) + + txLog := txs[logBlockNumber] + if dbLogs[k].TxHash.String() != txLog { + Error(s.T(), fmt.Errorf("mismatched TX\nchainid %d\nstart %d end %d\ndb txhash %s\nexplorer tx %s", chain.ChainID, contract.StartBlock, dbLogs[k].BlockNumber, dbLogs[k].TxHash.String(), txLog)) + } + } + // fmt.Println("chain", chain.ChainID, "contract", contract.Address, "dbLogCount", dbLogCount, "explorerLogCount", explorerLogCount) + if dbLogCount != explorerLogCount { + fmt.Println("chain", chain.ChainID, "contract", contract.Address, "dbLogCount", dbLogCount, "explorerLogCount", explorerLogCount) + } + Equal(s.T(), dbLogCount, explorerLogCount) + } + } +} + +func createHTTPClient() *http.Client { + return &http.Client{ + Timeout: 10 * time.Second, + Transport: &http.Transport{ + ResponseHeaderTimeout: 10 * time.Second, + }, + } +} + +func processBatch(ctx context.Context, client *http.Client, url string, txs *map[int64]string) (int, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return 0, fmt.Errorf("error getting data: %w", err) + } + resRaw, err := client.Do(req) + if err != nil { + return 0, fmt.Errorf("could not get data from explorer %w", err) + } + + var decodedRes map[string]json.RawMessage + if err := json.NewDecoder(resRaw.Body).Decode(&decodedRes); err != nil { + return 0, fmt.Errorf("error decoding response: %w", err) + } + + var resultSlice []map[string]interface{} + if err := json.Unmarshal(decodedRes["result"], &resultSlice); err != nil { + return 0, fmt.Errorf("error unmarshaling result: %w", err) + } + + if err = resRaw.Body.Close(); err != nil { + logger.ReportScribeError(err, 0, logger.TestError) + } + + for _, result := range resultSlice { + hexBlock, ok := result["blockNumber"].(string) + if !ok { + return 0, fmt.Errorf("error parsing block number: %w", err) + } + + txHashStr, ok := result["transactionHash"].(string) + if !ok { + return 0, fmt.Errorf("error parsing transaction hash: %w", err) + } + + key, err := strconv.ParseInt(strings.TrimPrefix(hexBlock, "0x"), 16, 64) + if err != nil { + return 0, fmt.Errorf("error parsing block number: %w", err) + } + (*txs)[key] = txHashStr + } + return len(resultSlice), nil +} + +func getLogs(ctx context.Context, contractAddress string, fromBlock uint64, toBlock uint64, apiURL string, txs *map[int64]string) (int, error) { + blockRange := toBlock - fromBlock + batchSize := uint64(400) + numBatches := blockRange/batchSize + 1 + client := createHTTPClient() + totalResults := 0 + + for i := uint64(0); i < numBatches; i++ { + startBlock := fromBlock + i*batchSize + endBlock := startBlock + batchSize - 1 + if endBlock > toBlock { + endBlock = toBlock + } + url := fmt.Sprintf("%s?module=logs&action=getLogs&address=%s&fromBlock=%d&toBlock=%d&page=1", + apiURL, contractAddress, startBlock, endBlock) + b := &backoff.Backoff{ + Factor: 2, + Jitter: true, + Min: 5 * time.Second, + Max: 10 * time.Second, + } + timeout := 3 * time.Second + + RETRY: + select { + case <-ctx.Done(): + return 0, fmt.Errorf("context canceled: %w", ctx.Err()) + case <-time.After(timeout): + resultCount, err := processBatch(ctx, client, url, txs) + if err != nil { + fmt.Println("error getting explorer logs", err) + timeout = b.Duration() + goto RETRY + } + totalResults += resultCount + } + + if i < numBatches-1 { + time.Sleep(3 * time.Second) + } + } + + return totalResults, nil +} + +func getLogAmount(ctx context.Context, db db.EventDB, filter db.LogFilter, startBlock uint64, endBlock uint64) (int, []*types.Log, error) { + page := 1 + var retrievedLogs []*types.Log + for { + logs, err := db.RetrieveLogsInRangeAsc(ctx, filter, startBlock, endBlock, page) + if err != nil { + return 0, nil, fmt.Errorf("failure while retreiving logs from database %w", err) + } + retrievedLogs = append(retrievedLogs, logs...) + if len(logs) < base.PageSize { + break + } + page++ + } + return len(retrievedLogs), retrievedLogs, nil +} diff --git a/services/scribe/service/suite_test.go b/services/scribe/service/suite_test.go new file mode 100644 index 0000000000..9fd29145dd --- /dev/null +++ b/services/scribe/service/suite_test.go @@ -0,0 +1,69 @@ +package service_test + +import ( + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/core/metrics/localmetrics" + "github.com/synapsecns/sanguine/services/scribe/metadata" + "testing" + "time" + + "github.com/Flaque/filet" + . "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/synapsecns/sanguine/core/testsuite" + "github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner" + "github.com/synapsecns/sanguine/ethergo/signer/wallet" + "github.com/synapsecns/sanguine/services/scribe/db" + "github.com/synapsecns/sanguine/services/scribe/db/datastore/sql/sqlite" + "github.com/synapsecns/sanguine/services/scribe/testutil" +) + +type ScribeSuite struct { + *testsuite.TestSuite + testDB db.EventDB + manager *testutil.DeployManager + wallet wallet.Wallet + signer *localsigner.Signer + metrics metrics.Handler + nullMetrics metrics.Handler + runVolumeTest bool +} + +// NewScribeSuite creates a new scribe test suite. +func NewScribeSuite(tb testing.TB) *ScribeSuite { + tb.Helper() + return &ScribeSuite{ + TestSuite: testsuite.NewTestSuite(tb), + runVolumeTest: true, + } +} + +// SetupTest sets up the test suite. +func (s *ScribeSuite) SetupTest() { + s.TestSuite.SetupTest() + s.SetTestTimeout(time.Minute * 10) + sqliteStore, err := sqlite.NewSqliteStore(s.GetTestContext(), filet.TmpDir(s.T(), ""), s.metrics, false) + Nil(s.T(), err) + s.testDB = sqliteStore + s.manager = testutil.NewDeployManager(s.T()) + s.wallet, err = wallet.FromRandom() + Nil(s.T(), err) + s.signer = localsigner.NewSigner(s.wallet.PrivateKey()) +} + +func (s *ScribeSuite) SetupSuite() { + s.TestSuite.SetupSuite() + localmetrics.SetupTestJaeger(s.GetSuiteContext(), s.T()) + + var err error + s.metrics, err = metrics.NewByType(s.GetSuiteContext(), metadata.BuildInfo(), metrics.Jaeger) + Nil(s.T(), err) + + s.nullMetrics, err = metrics.NewByType(s.GetSuiteContext(), metadata.BuildInfo(), metrics.Null) + Nil(s.T(), err) +} + +// TestScribeSuite tests the scribe suite. +func TestScribeSuite(t *testing.T) { + suite.Run(t, NewScribeSuite(t)) +} diff --git a/services/scribe/testhelper/scribe.go b/services/scribe/testhelper/scribe.go index 347cbf7b4b..b67af9c3bf 100644 --- a/services/scribe/testhelper/scribe.go +++ b/services/scribe/testhelper/scribe.go @@ -12,11 +12,11 @@ import ( "github.com/synapsecns/sanguine/ethergo/contracts" "github.com/synapsecns/sanguine/services/omnirpc/testhelper" scribeAPI "github.com/synapsecns/sanguine/services/scribe/api" - "github.com/synapsecns/sanguine/services/scribe/backfill" + "github.com/synapsecns/sanguine/services/scribe/backend" "github.com/synapsecns/sanguine/services/scribe/client" "github.com/synapsecns/sanguine/services/scribe/config" "github.com/synapsecns/sanguine/services/scribe/metadata" - "github.com/synapsecns/sanguine/services/scribe/node" + "github.com/synapsecns/sanguine/services/scribe/service" "testing" ) @@ -39,27 +39,27 @@ func NewTestScribe(ctx context.Context, tb testing.TB, deployedContracts map[uin eventDB, err := scribeAPI.InitDB(ctx, "sqlite", dbPath, metricsProvider, false) assert.Nil(tb, err) - scribeClients := make(map[uint32][]backfill.ScribeBackend) + scribeClients := make(map[uint32][]backend.ScribeBackend) var chainConfigs []config.ChainConfig - for _, backend := range backends { + for i := range backends { // this backends chain id - chainID := uint32(backend.GetChainID()) + chainID := uint32(backends[i].GetChainID()) // create the scribe backend client - backendClient, err := backfill.DialBackend(ctx, testhelper.GetURL(omnirpcURL, backend), metricsProvider) + backendClient, err := backend.DialBackend(ctx, testhelper.GetURL(omnirpcURL, backends[i]), metricsProvider) assert.Nil(tb, err) // creat ethe scribe client for this chain - scribeClients[chainID] = []backfill.ScribeBackend{backendClient} + scribeClients[chainID] = []backend.ScribeBackend{backendClient} // loop through all deployed contracts for this chainid adding them to our config contractConfigs := getContractConfig(deployedContracts[chainID]) // add the chain config to the list chainConfigs = append(chainConfigs, config.ChainConfig{ - ChainID: uint32(backend.GetChainID()), + ChainID: uint32(backends[i].GetChainID()), Contracts: contractConfigs, }) } @@ -69,7 +69,7 @@ func NewTestScribe(ctx context.Context, tb testing.TB, deployedContracts map[uin RPCURL: omnirpcURL, } - scribe, err := node.NewScribe(eventDB, scribeClients, scribeConfig, metricsProvider) + scribe, err := service.NewScribe(eventDB, scribeClients, scribeConfig, metricsProvider) assert.Nil(tb, err) go func() { diff --git a/services/scribe/testhelper/scribe_test.go b/services/scribe/testhelper/scribe_test.go index 027e62400b..c48d9bdb94 100644 --- a/services/scribe/testhelper/scribe_test.go +++ b/services/scribe/testhelper/scribe_test.go @@ -2,9 +2,12 @@ package testhelper_test import ( "github.com/brianvoe/gofakeit/v6" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" . "github.com/stretchr/testify/assert" pbscribe "github.com/synapsecns/sanguine/services/scribe/grpc/types/types/v1" "github.com/synapsecns/sanguine/services/scribe/testhelper" + "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -16,16 +19,20 @@ func (s *TestHelperSuite) TestEmbeddedScribe() { // let's send some messages on each domain g, gctx := errgroup.WithContext(s.GetTestContext()) - for _, backend := range s.testBackends { - backend := backend // capture func literal - _, testContract := s.deployManager.GetTestContract(gctx, backend) - for i := 0; i < 10; i++ { + for i := range s.testBackends { + chainBackend := s.testBackends[i] // capture func literal + _, testContract := s.deployManager.GetTestContract(gctx, chainBackend) + + for j := 0; j < 10; j++ { + randomAddress := common.BigToAddress(big.NewInt(int64(j))) + chainBackend.FundAccount(s.GetTestContext(), randomAddress, *big.NewInt(params.Wei)) + g.Go(func() error { - txContext := backend.GetTxContext(gctx, nil) + txContext := chainBackend.GetTxContext(gctx, nil) tx, err := testContract.EmitEventAandB(txContext.TransactOpts, big.NewInt(gofakeit.Int64()), big.NewInt(gofakeit.Int64()), big.NewInt(gofakeit.Int64())) Nil(s.T(), err) - backend.WaitForConfirmation(gctx, tx) + chainBackend.WaitForConfirmation(gctx, tx) return nil }) diff --git a/services/scribe/testutil/contracttype.go b/services/scribe/testutil/contracttype.go index 2829461d96..d6cebbef1c 100644 --- a/services/scribe/testutil/contracttype.go +++ b/services/scribe/testutil/contracttype.go @@ -36,7 +36,7 @@ type contractTypeImpl int const ( // TestContractType is the type of the test contract. - TestContractType contractTypeImpl = 0 // TestContract + TestContractType contractTypeImpl = iota ) // ID gets the contract type as an id. diff --git a/services/scribe/testutil/contracttypeimpl_string.go b/services/scribe/testutil/contracttypeimpl_string.go index 9ac210aa43..39af0889d3 100644 --- a/services/scribe/testutil/contracttypeimpl_string.go +++ b/services/scribe/testutil/contracttypeimpl_string.go @@ -11,9 +11,9 @@ func _() { _ = x[TestContractType-0] } -const _contractTypeImpl_name = "TestContract" +const _contractTypeImpl_name = "TestContractType" -var _contractTypeImpl_index = [...]uint8{0, 12} +var _contractTypeImpl_index = [...]uint8{0, 16} func (i contractTypeImpl) String() string { if i < 0 || i >= contractTypeImpl(len(_contractTypeImpl_index)-1) { diff --git a/services/scribe/testutil/deployers.go b/services/scribe/testutil/deployers.go index 8d3b9f2e9e..1dbd63955d 100644 --- a/services/scribe/testutil/deployers.go +++ b/services/scribe/testutil/deployers.go @@ -2,7 +2,6 @@ package testutil import ( "context" - "github.com/synapsecns/sanguine/ethergo/contracts" "github.com/ethereum/go-ethereum/accounts/abi/bind" diff --git a/services/scribe/testutil/manager.go b/services/scribe/testutil/manager.go index f0d3067e88..f4e3a7897d 100644 --- a/services/scribe/testutil/manager.go +++ b/services/scribe/testutil/manager.go @@ -11,6 +11,7 @@ func NewDeployManager(t *testing.T) *DeployManager { t.Helper() parentManager := manager.NewDeployerManager(t, NewTestContractDeployer) + return &DeployManager{parentManager} } diff --git a/services/scribe/testutil/utils.go b/services/scribe/testutil/utils.go new file mode 100644 index 0000000000..0a890602f6 --- /dev/null +++ b/services/scribe/testutil/utils.go @@ -0,0 +1,291 @@ +package testutil + +import ( + "context" + "fmt" + "github.com/brianvoe/gofakeit/v6" + "github.com/synapsecns/sanguine/services/scribe/db" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/ethergo/backends" + "github.com/synapsecns/sanguine/ethergo/backends/geth" + "github.com/synapsecns/sanguine/ethergo/contracts" + "github.com/synapsecns/sanguine/services/omnirpc/testhelper" + "github.com/synapsecns/sanguine/services/scribe/backend" + "github.com/synapsecns/sanguine/services/scribe/testutil/testcontract" + "golang.org/x/sync/errgroup" +) + +// TestChainHandler is a handler for interacting with test contracts on a chain to aid in building extensive tests. +// It is returned when emitting events with the test contracts in the PopulateWithLogs function. +type TestChainHandler struct { + Addresses []common.Address + ContractStartBlocks map[common.Address]uint64 + ContractRefs map[common.Address]*testcontract.TestContractRef + EventsEmitted map[common.Address]uint64 +} + +type chainBackendPair struct { + chainID uint32 + backend backend.ScribeBackend +} + +type chainContractPair struct { + chainID uint32 + chainHandler *TestChainHandler +} + +// PopulateChainsWithLogs creates scribe backends for each chain backend and emits events from various contracts on each chain. +func PopulateChainsWithLogs(ctx context.Context, t *testing.T, chainBackends map[uint32]geth.Backend, desiredBlockHeight uint64, managers []*DeployManager, handler metrics.Handler) (map[uint32]*TestChainHandler, map[uint32][]backend.ScribeBackend, error) { + t.Helper() + addressChan := make(chan chainContractPair, len(chainBackends)) + scribeBackendChan := make(chan chainBackendPair, len(chainBackends)) + g, groupCtx := errgroup.WithContext(ctx) + for k, v := range chainBackends { + chain := k + chainBackend := v + + g.Go(func() error { + contractHandler, err := PopulateWithLogs(groupCtx, t, &chainBackend, desiredBlockHeight, managers) + + if err != nil { + return err + } + + addressChan <- chainContractPair{chain, contractHandler} + + return nil + }) + g.Go(func() error { + host := StartOmnirpcServer(groupCtx, t, &chainBackend) + scribeBackend, err := backend.DialBackend(ctx, host, handler) + + if err != nil { + return err + } + + scribeBackendChan <- chainBackendPair{chain, scribeBackend} + + return nil + }) + } + + if err := g.Wait(); err != nil { + return nil, nil, fmt.Errorf("error populating chains with logs: %w", err) + } + close(addressChan) // Close the channels after writing to them + close(scribeBackendChan) + // Unpack channels + chainMap := make(map[uint32]*TestChainHandler) + scribeBackendMap := make(map[uint32][]backend.ScribeBackend) + for pair := range addressChan { + chainMap[pair.chainID] = pair.chainHandler + } + + for pair := range scribeBackendChan { + scribeBackendMap[pair.chainID] = []backend.ScribeBackend{pair.backend} + } + + return chainMap, scribeBackendMap, nil +} + +// PopulateWithLogs populates a backend with logs until it reaches a desired block height. +// +// nolint:cyclop +func PopulateWithLogs(ctx context.Context, t *testing.T, backend backends.SimulatedTestBackend, desiredBlockHeight uint64, managers []*DeployManager) (*TestChainHandler, error) { + t.Helper() + + startBlocks := map[common.Address]uint64{} + contracts := map[common.Address]contracts.DeployedContract{} + contractRefs := map[common.Address]*testcontract.TestContractRef{} + eventsEmitted := map[common.Address]uint64{} + // Get all the test contracts + for j := range managers { + manager := managers[j] + testContract, testRef := manager.GetTestContract(ctx, backend) + contracts[testContract.Address()] = testContract + contractRefs[testContract.Address()] = testRef + eventsEmitted[testContract.Address()] = 0 + } + + // Get start blocks for the deployed contracts + for address := range contracts { + deployTxHash := contracts[address].DeployTx().Hash() + receipt, err := backend.TransactionReceipt(ctx, deployTxHash) + if err != nil { + return nil, fmt.Errorf("error getting receipt for tx: %w", err) + } + startBlocks[address] = receipt.BlockNumber.Uint64() + } + // Iterate and emit events until we reach the desired block height + + testChainHandler := &TestChainHandler{ + Addresses: dumpAddresses(contracts), + ContractStartBlocks: startBlocks, + ContractRefs: contractRefs, + EventsEmitted: eventsEmitted, + } + err := EmitEvents(ctx, t, backend, desiredBlockHeight, testChainHandler) + if err != nil { + return nil, fmt.Errorf("error emitting events: %w", err) + } + return testChainHandler, nil +} + +// EmitEvents emits events from the test contracts until the desired block height is reached. +func EmitEvents(ctx context.Context, t *testing.T, backend backends.SimulatedTestBackend, desiredBlockHeight uint64, testChainHandler *TestChainHandler) error { + t.Helper() + i := 0 + for { + select { + case <-ctx.Done(): + t.Log(ctx.Err()) + return nil + default: + i++ + randomAddress := common.BigToAddress(big.NewInt(int64(i))) + backend.FundAccount(ctx, randomAddress, *big.NewInt(params.Wei)) + latestBlock, err := backend.BlockNumber(ctx) + if err != nil { + return err + } + + if latestBlock >= desiredBlockHeight { + return nil + } + // Emit EventA for each contract + g, groupCtx := errgroup.WithContext(ctx) + transactOpts := backend.GetTxContext(groupCtx, nil) + for k, v := range testChainHandler.ContractRefs { + address := k + ref := v + // Pass if the contract's specified start block is greater than the current block height. + // Used for testing livefill passing. + if latestBlock <= testChainHandler.ContractStartBlocks[address] { + continue + } + // Update number of events emitted + testChainHandler.EventsEmitted[address]++ + + g.Go(func() error { + tx, err := ref.EmitEventA(transactOpts.TransactOpts, big.NewInt(1), big.NewInt(2), big.NewInt(3)) + if err != nil { + return fmt.Errorf("error emitting event a for contract %s: %w", address.String(), err) + } + backend.WaitForConfirmation(groupCtx, tx) + + return nil + }) + } + err = g.Wait() + if err != nil { + return fmt.Errorf("error emitting events: %w", err) + } + } + } +} + +// GetTxBlockNumber gets the block number of a transaction. +func GetTxBlockNumber(ctx context.Context, chain backends.SimulatedTestBackend, tx *types.Transaction) (uint64, error) { + receipt, err := chain.TransactionReceipt(ctx, tx.Hash()) + if err != nil { + return 0, fmt.Errorf("error getting receipt for tx: %w", err) + } + return receipt.BlockNumber.Uint64(), nil +} + +// StartOmnirpcServer starts an omnirpc server and returns the url to it. +func StartOmnirpcServer(ctx context.Context, t *testing.T, backend backends.SimulatedTestBackend) string { + t.Helper() + baseHost := testhelper.NewOmnirpcServer(ctx, t, backend) + return testhelper.GetURL(baseHost, backend) +} + +// ReachBlockHeight reaches a block height on a backend. +func ReachBlockHeight(ctx context.Context, t *testing.T, backend backends.SimulatedTestBackend, desiredBlockHeight uint64) error { + t.Helper() + i := 0 + for { + select { + case <-ctx.Done(): + t.Log(ctx.Err()) + return nil + default: + // continue + } + i++ + backend.FundAccount(ctx, common.BigToAddress(big.NewInt(int64(i))), *big.NewInt(params.Wei)) + + latestBlock, err := backend.BlockNumber(ctx) + if err != nil { + return fmt.Errorf("error getting latest block number: %w", err) + } + + if latestBlock >= desiredBlockHeight { + return nil + } + } +} + +// dumpAddresses is a helper function to return all the addresses from a deployed contract. +func dumpAddresses(contracts map[common.Address]contracts.DeployedContract) []common.Address { + var addresses []common.Address + for address := range contracts { + addresses = append(addresses, address) + } + return addresses +} + +// GetLogsUntilNoneLeft gets all receipts from the database until there are none left (iterates page num). +func GetLogsUntilNoneLeft(ctx context.Context, testDB db.EventDB, filter db.LogFilter) ([]*types.Log, error) { + var logs []*types.Log + page := 0 + for { + page++ + newLogs, err := testDB.RetrieveLogsWithFilter(ctx, filter, page) + if err != nil { + return nil, fmt.Errorf("error getting logs: %w", err) + } + if len(newLogs) == 0 { + return logs, nil + } + logs = append(logs, newLogs...) + } +} + +// GetReceiptsUntilNoneLeft gets all receipts from the database until there are none left (iterates page num). +func GetReceiptsUntilNoneLeft(ctx context.Context, testDB db.EventDB, filter db.ReceiptFilter) ([]types.Receipt, error) { + var receipts []types.Receipt + page := 0 + for { + page++ + newReceipts, err := testDB.RetrieveReceiptsWithFilter(ctx, filter, page) + if err != nil { + return nil, fmt.Errorf("error getting receipts: %w", err) + } + if len(newReceipts) == 0 { + return receipts, nil + } + receipts = append(receipts, newReceipts...) + } +} + +// MakeRandomLog makes a random log. +func MakeRandomLog(txHash common.Hash) types.Log { + return types.Log{ + Address: common.BigToAddress(big.NewInt(gofakeit.Int64())), + Topics: []common.Hash{common.BigToHash(big.NewInt(gofakeit.Int64())), common.BigToHash(big.NewInt(gofakeit.Int64())), common.BigToHash(big.NewInt(gofakeit.Int64()))}, + Data: []byte(gofakeit.Sentence(10)), + BlockNumber: gofakeit.Uint64(), + TxHash: txHash, + TxIndex: uint(gofakeit.Uint64()), + BlockHash: common.BigToHash(big.NewInt(gofakeit.Int64())), + Index: uint(gofakeit.Uint64()), + Removed: gofakeit.Bool(), + } +} diff --git a/services/scribe/types/config.go b/services/scribe/types/config.go new file mode 100644 index 0000000000..1b628be846 --- /dev/null +++ b/services/scribe/types/config.go @@ -0,0 +1,15 @@ +package types + +import "github.com/ethereum/go-ethereum/common" + +// IndexerConfig holds metadata for the indexer. It is used to pass data uniformly and used in logging. +type IndexerConfig struct { + Addresses []common.Address + GetLogsRange uint64 + GetLogsBatchAmount uint64 + StoreConcurrency int + ChainID uint32 + StartHeight uint64 + EndHeight uint64 + ConcurrencyThreshold uint64 +} diff --git a/services/scribe/types/doc.go b/services/scribe/types/doc.go new file mode 100644 index 0000000000..ccef3b2f82 --- /dev/null +++ b/services/scribe/types/doc.go @@ -0,0 +1,2 @@ +// Package types holds various types used throughout the scribe package. +package types diff --git a/services/scribe/types/indexing.go b/services/scribe/types/indexing.go new file mode 100644 index 0000000000..afd694e795 --- /dev/null +++ b/services/scribe/types/indexing.go @@ -0,0 +1,9 @@ +package types + +// helper enum for readability. +const ( + // LivefillAtHead signals that the param is livefilling at the head of the chain. + LivefillAtHead = true + // IndexingConfirmed signals that the param is indexing before the confirmation threshold. + IndexingConfirmed = false +) diff --git a/tools/go.mod b/tools/go.mod index 6aef808ea4..8020fdfbb8 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -15,10 +15,10 @@ require ( github.com/stretchr/testify v1.8.4 github.com/synapsecns/sanguine/core v0.0.0-00010101000000-000000000000 github.com/thoas/go-funk v0.9.0 - github.com/urfave/cli/v2 v2.24.4 + github.com/urfave/cli/v2 v2.25.5 golang.org/x/exp v0.0.0-20230127193734-31bee513bff7 - golang.org/x/mod v0.9.0 - golang.org/x/tools v0.7.0 + golang.org/x/mod v0.10.0 + golang.org/x/tools v0.9.3 ) require ( @@ -69,7 +69,7 @@ require ( github.com/rjeczalik/notify v0.9.2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/skeema/knownhosts v1.1.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 7dd80241c7..9635184409 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -308,8 +308,8 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -339,8 +339,8 @@ github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03O github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -423,8 +423,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -617,8 +617,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= @@ -728,6 +728,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=