mirror of
https://github.com/grassrootseconomics/cic-custodial.git
synced 2025-04-19 15:01:01 +02:00
Compare commits
86 Commits
v0.1.1-alp
...
master
Author | SHA1 | Date | |
---|---|---|---|
cb40a6482a | |||
b244fc7dca | |||
5d0099f136 | |||
|
8b116f8a0f | ||
|
888fb4ce11 | ||
|
27b300db93 | ||
|
788cf66fc7 | ||
|
d2cb0796c8 | ||
9e1d62a014 | |||
98ff897049 | |||
8dc0dcf12d | |||
|
f84b90f411 | ||
|
83113c8411 | ||
|
6ccc27685c | ||
|
e821da13d4 | ||
|
2b42da206b | ||
b9d3c219c8 | |||
8ef2311d8e | |||
e91f82c08a | |||
|
223656b2bc | ||
|
0218831bd9 | ||
|
b2df62f0df | ||
|
d9acaa9583 | ||
d51e74883d | |||
0e5db7f06f | |||
b137088d38 | |||
21a17d2735 | |||
eba329eefa | |||
82294b96f8 | |||
e67a42ede3 | |||
a98fe958a3 | |||
e203c49049 | |||
448b142f7c | |||
750fdd9c1f | |||
5f5678e25b | |||
ef5bd1860f | |||
ac5567bc56 | |||
2d5e41eb81 | |||
|
fc49f2387d | ||
|
81a3bb7e3c | ||
0235f25f81 | |||
04e5d3c20f | |||
ea53214e2b | |||
ee07e61457 | |||
fe9d217b60 | |||
bbd4b57fa8 | |||
b1572cfba7 | |||
0fd35f9668 | |||
5a94a38805 | |||
0ef18b4e3e | |||
341a760f02 | |||
ec14328d49 | |||
70419a9b19 | |||
a47e44f262 | |||
a1b6cb08d8 | |||
1ddff06502 | |||
|
885163e48a | ||
|
d9806ca02c | ||
|
4dac0e8413 | ||
4dfc1725cb | |||
4372b9dbf1 | |||
010ba8c2cb | |||
add7f2a442 | |||
ce6bdbf4ed | |||
b5b2f89ad3 | |||
40cb86f522 | |||
|
140c887341 | ||
3d9c4390a7 | |||
cf1f9f34c3 | |||
55a560ffde | |||
fb5ab81be5 | |||
|
87813c5557 | ||
4d13a14dc2 | |||
2a5c87b22c | |||
2b0813519f | |||
952535ca2f | |||
|
e53cb8a6c1 | ||
|
7520ba11f4 | ||
|
80f2245ecb | ||
|
5600eeb07e | ||
b4ffaf484d | |||
962077f2e7 | |||
611addbf64 | |||
4b180ea285 | |||
dbfe3e95c8 | |||
742095c71f |
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@ -0,0 +1,10 @@
|
||||
.github
|
||||
.deepsource.toml
|
||||
.goreleaser.yaml
|
||||
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
13
.env.example
13
.env.example
@ -1,3 +1,10 @@
|
||||
POSTGRES_DSN=postgres://postgres:postgres@postgres:5432/cic_custodial
|
||||
REDIS_DSN=redis://redis:6379/1
|
||||
ASYNQ_DSN=redis://redis:6379/0
|
||||
CUSTODIAL_CHAIN__RPC_ENDPOINT=
|
||||
CUSTODIAL_SYSTEM__GAS_FAUCET_ADDRESS=
|
||||
CUSTODIAL_SYSTEM__GIFTABLE_TOKEN_ADDRESS=
|
||||
CUSTODIAL_SYSTEM__PRIVATE_KEY=
|
||||
CUSTODIAL_SYSTEM__PUBLIC_KEY=
|
||||
CUSTODIAL_SYSTEM__ACCOUNT_INDEX_ADDRESS=
|
||||
CUSTODIAL_POSTGRES__DSN=
|
||||
CUSTODIAL_REDIS__DSN=
|
||||
CUSTODIAL_ASYNQ__DSN=
|
||||
CUSTODIAL_JETSTREAM__ENDPOINT=
|
37
.github/workflows/binary_release.yaml
vendored
37
.github/workflows/binary_release.yaml
vendored
@ -1,37 +0,0 @@
|
||||
name: binary_release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: goreleaser/goreleaser-cross
|
||||
environment: build
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
29
.github/workflows/docker_release.yaml
vendored
29
.github/workflows/docker_release.yaml
vendored
@ -1,29 +0,0 @@
|
||||
name: docker_release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
build-publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: docker/login-action@v1.14.1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: docker compose build push
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
COMPOSE_DOCKER_CLI_BUILD: 1
|
||||
run: |
|
||||
export TAG=$GITHUB_REF_NAME
|
||||
docker-compose -f docker-compose.build.yaml build --progress plain
|
||||
docker-compose -f docker-compose.build.yaml push
|
||||
export TAG=latest
|
||||
docker-compose -f docker-compose.build.yaml build --progress plain
|
||||
docker-compose -f docker-compose.build.yaml push
|
100
.github/workflows/release.yaml
vendored
Normal file
100
.github/workflows/release.yaml
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Login to GHCR Docker registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set outputs
|
||||
run: |
|
||||
echo "RELEASE_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV \
|
||||
&& echo "RELEASE_SHORT_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
BUILD_COMMIT=${{ env.RELEASE_SHORT_COMMIT }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
tags: |
|
||||
ghcr.io/grassrootseconomics/cic-custodial/cic-custodial:latest
|
||||
ghcr.io/grassrootseconomics/cic-custodial/cic-custodial:${{ env.RELEASE_TAG }}
|
||||
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: goreleaser/goreleaser-cross
|
||||
environment: build
|
||||
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
~/Library/Caches/go-build
|
||||
%LocalAppData%\go-build
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Workaround Git Security Warning
|
||||
run: |
|
||||
# Workaround a bug in github actions:
|
||||
# https://github.com/actions/runner-images/issues/6775.
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 'stable'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
cic-custodial
|
||||
dist
|
||||
.env
|
||||
**/*.env
|
||||
covprofile
|
@ -8,12 +8,14 @@ builds:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: ./cmd
|
||||
main: ./cmd/service
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.build={{.ShortCommit}} -s -w
|
||||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- config.toml
|
||||
- queries.sql
|
||||
- migrations
|
||||
|
27
Dockerfile
27
Dockerfile
@ -1,22 +1,33 @@
|
||||
FROM golang:1.19-bullseye as build
|
||||
FROM golang:1-bullseye as build
|
||||
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
|
||||
ENV BUILD_COMMIT=${BUILD_COMMIT}
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY go.* .
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o cic-custodial -ldflags="-s -w" cmd/*.go
|
||||
RUN go build -o cic-custodial -ldflags="-X main.build=${BUILD_COMMIT} -s -w" cmd/service/*
|
||||
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN set -x && \
|
||||
apt-get update && \
|
||||
apt-get install -y ca-certificates && \
|
||||
rm -rf /var/cache/apt/archives /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /service
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /build/cic-custodial .
|
||||
COPY migrations migrations/
|
||||
COPY config.toml .
|
||||
|
||||
CMD ["./cic-custodial"]
|
||||
COPY queries.sql .
|
||||
COPY LICENSE .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["./cic-custodial"]
|
||||
|
21
Makefile
Normal file
21
Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
BIN := cic-custodial
|
||||
BUILD_CONF := CGO_ENABLED=1 GOOS=linux GOARCH=amd64
|
||||
BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null)
|
||||
|
||||
.PHONY: build run run-debug docs
|
||||
|
||||
clean:
|
||||
rm ${BIN}
|
||||
|
||||
build:
|
||||
${BUILD_CONF} go build -ldflags="-X main.build=${BUILD_COMMIT} -s -w" -o ${BIN} cmd/service/*
|
||||
|
||||
docs:
|
||||
swag fmt --dir internal/api/
|
||||
swag init --dir internal/api/ -g swagger.go
|
||||
|
||||
run:
|
||||
${BUILD_CONF} go run cmd/service/*
|
||||
|
||||
run-debug:
|
||||
${BUILD_CONF} go run cmd/service/* -debug
|
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# cic-custodial
|
||||
|
||||

|
||||

|
||||
[](https://goreportcard.com/report/github.com/grassrootseconomics/cic-custodial)
|
||||
|
||||
> CIC Custodial
|
||||
|
||||
Experimental custodial API.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Usage](docs/usage.md)
|
||||
|
||||
## License
|
||||
|
||||
[AGPL-3.0](LICENSE).
|
@ -1,40 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/arl/statsviz"
|
||||
"github.com/go-playground/validator"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/api"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func initApiServer() *echo.Echo {
|
||||
server := echo.New()
|
||||
server.HideBanner = true
|
||||
server.HidePort = true
|
||||
|
||||
if ko.Bool("service.statsviz_debug") {
|
||||
statsVizMux := http.NewServeMux()
|
||||
_ = statsviz.Register(statsVizMux)
|
||||
server.GET("/debug/statsviz/", echo.WrapHandler(statsVizMux))
|
||||
server.GET("/debug/statsviz/*", echo.WrapHandler(statsVizMux))
|
||||
}
|
||||
|
||||
server.Validator = &api.CustomValidator{
|
||||
Validator: validator.New(),
|
||||
}
|
||||
|
||||
apiRoute := server.Group("/api")
|
||||
|
||||
apiRoute.POST("/register", api.RegistrationHandler(
|
||||
taskerClient,
|
||||
postgresKeystore,
|
||||
))
|
||||
|
||||
apiRoute.POST("/transfer", api.TransferHandler(
|
||||
taskerClient,
|
||||
))
|
||||
|
||||
return server
|
||||
}
|
140
cmd/init_core.go
140
cmd/init_core.go
@ -1,140 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
celo "github.com/grassrootseconomics/cic-celo-sdk"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/keystore"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/logg"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/postgres"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/knadh/koanf"
|
||||
"github.com/knadh/koanf/parsers/toml"
|
||||
"github.com/knadh/koanf/providers/env"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
func initConfig(configFilePath string) *koanf.Koanf {
|
||||
var (
|
||||
ko = koanf.New(".")
|
||||
)
|
||||
|
||||
confFile := file.Provider(configFilePath)
|
||||
if err := ko.Load(confFile, toml.Parser()); err != nil {
|
||||
log.Fatalf("could not load config file: %v", err)
|
||||
}
|
||||
|
||||
if err := ko.Load(env.Provider("", ".", func(s string) string {
|
||||
return strings.ReplaceAll(strings.ToLower(
|
||||
strings.TrimPrefix(s, "")), "_", ".")
|
||||
}), nil); err != nil {
|
||||
log.Fatalf("could not override config from env vars: %v", err)
|
||||
}
|
||||
|
||||
return ko
|
||||
}
|
||||
|
||||
func initLogger() logf.Logger {
|
||||
return logg.NewLogg(logg.LoggOpts{
|
||||
Debug: ko.Bool("logg.debug"),
|
||||
Color: ko.Bool("logg.color"),
|
||||
Caller: ko.Bool("logg.caller"),
|
||||
})
|
||||
}
|
||||
|
||||
func initCeloProvider() *celo.Provider {
|
||||
providerOpts := celo.ProviderOpts{
|
||||
RpcEndpoint: ko.MustString("chain.rpc_endpoint"),
|
||||
}
|
||||
|
||||
if ko.Bool("chain.testnet") {
|
||||
providerOpts.ChainId = celo.TestnetChainId
|
||||
} else {
|
||||
providerOpts.ChainId = celo.MainnetChainId
|
||||
}
|
||||
|
||||
provider, err := celo.NewProvider(providerOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("initChainProvider", "error", err)
|
||||
}
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
func initPostgresPool() *pgxpool.Pool {
|
||||
poolOpts := postgres.PostgresPoolOpts{
|
||||
DSN: ko.MustString("postgres.dsn"),
|
||||
}
|
||||
|
||||
pool, err := postgres.NewPostgresPool(poolOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("initPostgresPool", "error", err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
func initKeystore() keystore.Keystore {
|
||||
keystore, err := keystore.NewPostgresKeytore(keystore.Opts{
|
||||
PostgresPool: postgresPool,
|
||||
Logg: lo,
|
||||
})
|
||||
if err != nil {
|
||||
lo.Fatal("initKeystore", "error", err)
|
||||
}
|
||||
|
||||
return keystore
|
||||
}
|
||||
|
||||
func initAsynqRedisPool() *redis.RedisPool {
|
||||
poolOpts := redis.RedisPoolOpts{
|
||||
DSN: ko.MustString("asynq.dsn"),
|
||||
MinIdleConns: ko.MustInt("redis.minconn"),
|
||||
}
|
||||
|
||||
pool, err := redis.NewRedisPool(poolOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("initAsynqRedisPool", "error", err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
func initCommonRedisPool() *redis.RedisPool {
|
||||
poolOpts := redis.RedisPoolOpts{
|
||||
DSN: ko.MustString("redis.dsn"),
|
||||
MinIdleConns: ko.MustInt("redis.minconn"),
|
||||
}
|
||||
|
||||
pool, err := redis.NewRedisPool(poolOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("initCommonRedisPool", "error", err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
func initRedisNoncestore() nonce.Noncestore {
|
||||
return nonce.NewRedisNoncestore(nonce.Opts{
|
||||
RedisPool: commonRedisPool,
|
||||
ChainProvider: celoProvider,
|
||||
})
|
||||
}
|
||||
|
||||
func initLockProvider() *redislock.Client {
|
||||
return redislock.New(commonRedisPool.Client)
|
||||
}
|
||||
|
||||
func initTaskerClient() *tasker.TaskerClient {
|
||||
return tasker.NewTaskerClient(tasker.TaskerClientOpts{
|
||||
RedisPool: asynqRedisPool,
|
||||
TaskRetention: time.Hour * 12,
|
||||
})
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
)
|
||||
|
||||
func initSystemContainer() *tasker.SystemContainer {
|
||||
return &tasker.SystemContainer{
|
||||
GasRefillThreshold: big.NewInt(ko.MustInt64("system.gas_refill_threshold")),
|
||||
GasRefillValue: big.NewInt(ko.MustInt64("system.gas_refill_value")),
|
||||
GiftableGasValue: big.NewInt(ko.MustInt64("system.giftable_gas_value")),
|
||||
GiftableToken: w3.A(ko.MustString("system.giftable_token_address")),
|
||||
GiftableTokenValue: big.NewInt(ko.MustInt64("system.giftable_token_value")),
|
||||
LockPrefix: ko.MustString("system.lock_prefix"),
|
||||
LockTimeout: 1 * time.Second,
|
||||
PrivateKey: initSystemKey(),
|
||||
PublicKey: ko.MustString("system.public_key"),
|
||||
TokenDecimals: ko.MustInt("system.token_decimals"),
|
||||
TokenTransferGasLimit: uint64(ko.MustInt64("system.token_transfer_gas_limit")),
|
||||
}
|
||||
}
|
||||
|
||||
func initSystemKey() *ecdsa.PrivateKey {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
currentSystemNonce, err := redisNoncestore.Peek(ctx, ko.MustString("system.public_key"))
|
||||
lo.Debug("initNoncestore: loaded (noncestore) system nonce", "nonce", currentSystemNonce)
|
||||
if err != nil {
|
||||
nonce, err := redisNoncestore.SyncNetworkNonce(ctx, ko.MustString("system.public_key"))
|
||||
lo.Debug("initNoncestore: syncing system nonce", "nonce", nonce)
|
||||
if err != nil {
|
||||
lo.Fatal("initNonceStore", "error", "system account nonce sync failed")
|
||||
}
|
||||
}
|
||||
|
||||
loadedPrivateKey, err := eth_crypto.HexToECDSA(ko.MustString("system.private_key"))
|
||||
if err != nil {
|
||||
lo.Fatal("Failed to load system private key", "error", err)
|
||||
}
|
||||
|
||||
return loadedPrivateKey
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker/task"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
func initTasker() *tasker.TaskerServer {
|
||||
taskerServerOpts := tasker.TaskerServerOpts{
|
||||
Concurrency: ko.MustInt("asynq.concurrency"),
|
||||
Logg: lo,
|
||||
RedisPool: asynqRedisPool,
|
||||
SystemContainer: nil,
|
||||
TaskerClient: taskerClient,
|
||||
}
|
||||
|
||||
if ko.Bool("asynq.debug") {
|
||||
taskerServerOpts.LogLevel = asynq.DebugLevel
|
||||
}
|
||||
|
||||
taskerServer := tasker.NewTaskerServer(taskerServerOpts)
|
||||
|
||||
taskerServer.RegisterHandlers(tasker.PrepareAccountTask, task.PrepareAccount(
|
||||
redisNoncestore,
|
||||
taskerClient,
|
||||
))
|
||||
taskerServer.RegisterHandlers(tasker.GiftGasTask, task.GiftGasProcessor(
|
||||
celoProvider,
|
||||
redisNoncestore,
|
||||
lockProvider,
|
||||
system,
|
||||
taskerClient,
|
||||
))
|
||||
taskerServer.RegisterHandlers(tasker.GiftTokenTask, task.GiftTokenProcessor(
|
||||
celoProvider,
|
||||
redisNoncestore,
|
||||
lockProvider,
|
||||
system,
|
||||
taskerClient,
|
||||
))
|
||||
taskerServer.RegisterHandlers(tasker.RefillGasTask, task.RefillGasProcessor(
|
||||
celoProvider,
|
||||
redisNoncestore,
|
||||
lockProvider,
|
||||
system,
|
||||
taskerClient,
|
||||
))
|
||||
taskerServer.RegisterHandlers(tasker.TransferTokenTask, task.TransferToken(
|
||||
celoProvider,
|
||||
redisNoncestore,
|
||||
postgresKeystore,
|
||||
lockProvider,
|
||||
system,
|
||||
taskerClient,
|
||||
))
|
||||
taskerServer.RegisterHandlers(tasker.TxDispatchTask, task.TxDispatch(
|
||||
celoProvider,
|
||||
))
|
||||
|
||||
return taskerServer
|
||||
}
|
110
cmd/service.go
110
cmd/service.go
@ -1,110 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
celo "github.com/grassrootseconomics/cic-celo-sdk"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/keystore"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/knadh/koanf"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
taskerServer *tasker.TaskerServer
|
||||
apiServer *echo.Echo
|
||||
}
|
||||
|
||||
var (
|
||||
asynqRedisPool *redis.RedisPool
|
||||
celoProvider *celo.Provider
|
||||
commonRedisPool *redis.RedisPool
|
||||
lo logf.Logger
|
||||
lockProvider *redislock.Client
|
||||
ko *koanf.Koanf
|
||||
postgresPool *pgxpool.Pool
|
||||
postgresKeystore keystore.Keystore
|
||||
redisNoncestore nonce.Noncestore
|
||||
system *tasker.SystemContainer
|
||||
taskerClient *tasker.TaskerClient
|
||||
)
|
||||
|
||||
func init() {
|
||||
ko = initConfig("config.toml")
|
||||
lo = initLogger()
|
||||
|
||||
celoProvider = initCeloProvider()
|
||||
postgresPool = initPostgresPool()
|
||||
postgresKeystore = initKeystore()
|
||||
asynqRedisPool = initAsynqRedisPool()
|
||||
commonRedisPool = initCommonRedisPool()
|
||||
redisNoncestore = initRedisNoncestore()
|
||||
lockProvider = initLockProvider()
|
||||
taskerClient = initTaskerClient()
|
||||
system = initSystemContainer()
|
||||
}
|
||||
|
||||
func main() {
|
||||
service := &service{}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
service.taskerServer = initTasker()
|
||||
service.apiServer = initApiServer()
|
||||
|
||||
startServices(service, ctx)
|
||||
}
|
||||
|
||||
func startServices(serviceContainer *service, ctx context.Context) {
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if err := serviceContainer.taskerServer.Start(); err != nil {
|
||||
lo.Fatal("Could not start task server", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if err := serviceContainer.apiServer.Start(ko.MustString("service.address")); err != nil {
|
||||
if strings.Contains(err.Error(), "Server closed") {
|
||||
lo.Info("Shutting down server")
|
||||
} else {
|
||||
lo.Fatal("Could not start api server", "err", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
gracefulShutdown(serviceContainer, ctx)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func gracefulShutdown(serviceContainer *service, ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
lo.Info("Graceful shutdown triggered")
|
||||
|
||||
lo.Info("Stopping tasker dequeue")
|
||||
serviceContainer.taskerServer.Stop()
|
||||
lo.Info("Stopped tasker")
|
||||
|
||||
if err := serviceContainer.apiServer.Shutdown(ctx); err != nil {
|
||||
lo.Error("Could not gracefully shutdown api server", "err", err)
|
||||
}
|
||||
lo.Info("Stopped API server")
|
||||
}
|
105
cmd/service/api.go
Normal file
105
cmd/service/api.go
Normal file
@ -0,0 +1,105 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/go-playground/validator/v10"
|
||||
_ "github.com/grassrootseconomics/cic-custodial/docs"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/api"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/util"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
echoSwagger "github.com/swaggo/echo-swagger"
|
||||
)
|
||||
|
||||
const (
|
||||
systemGlobalLockKey = "system:global_lock"
|
||||
)
|
||||
|
||||
// Bootstrap API server.
|
||||
func initApiServer(custodialContainer *custodial.Custodial) *echo.Echo {
|
||||
customValidator := validator.New()
|
||||
|
||||
server := echo.New()
|
||||
server.HideBanner = true
|
||||
server.HidePort = true
|
||||
server.Validator = &api.Validator{
|
||||
ValidatorProvider: customValidator,
|
||||
}
|
||||
server.HTTPErrorHandler = customHTTPErrorHandler
|
||||
|
||||
server.Use(middleware.Recover())
|
||||
server.Use(middleware.BodyLimit("1M"))
|
||||
server.Use(middleware.ContextTimeout(util.SLATimeout))
|
||||
|
||||
if ko.Bool("service.metrics") {
|
||||
server.GET("/metrics", func(c echo.Context) error {
|
||||
metrics.WritePrometheus(c.Response(), true)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if ko.Bool("service.docs") {
|
||||
server.GET("/docs/*", echoSwagger.WrapHandler)
|
||||
}
|
||||
|
||||
apiRoute := server.Group("/api", systemGlobalLock(custodialContainer))
|
||||
|
||||
apiRoute.POST("/account/create", api.HandleAccountCreate(custodialContainer))
|
||||
apiRoute.GET("/account/status/:address", api.HandleNetworkAccountStatus(custodialContainer))
|
||||
apiRoute.POST("/sign/transfer", api.HandleSignTransfer(custodialContainer))
|
||||
apiRoute.POST("/sign/transferAuth", api.HandleSignTranserAuthorization(custodialContainer))
|
||||
apiRoute.GET("/track/:trackingId", api.HandleTrackTx(custodialContainer))
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func customHTTPErrorHandler(err error, c echo.Context) {
|
||||
if c.Response().Committed {
|
||||
return
|
||||
}
|
||||
|
||||
if he, ok := err.(*echo.HTTPError); ok {
|
||||
var errorMsg string
|
||||
|
||||
if m, ok := he.Message.(error); ok {
|
||||
errorMsg = m.Error()
|
||||
} else if m, ok := he.Message.(string); ok {
|
||||
errorMsg = m
|
||||
}
|
||||
|
||||
c.JSON(he.Code, api.ErrResp{
|
||||
Ok: false,
|
||||
Message: errorMsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
lo.Error("api: echo error", "path", c.Path(), "err", err)
|
||||
c.JSON(http.StatusInternalServerError, api.ErrResp{
|
||||
Ok: false,
|
||||
Message: "Internal server error.",
|
||||
})
|
||||
}
|
||||
|
||||
func systemGlobalLock(cu *custodial.Custodial) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
locked, err := cu.RedisClient.Get(c.Request().Context(), systemGlobalLockKey).Bool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if locked {
|
||||
return c.JSON(http.StatusServiceUnavailable, api.ErrResp{
|
||||
Ok: false,
|
||||
Message: "System manually locked.",
|
||||
})
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
175
cmd/service/init.go
Normal file
175
cmd/service/init.go
Normal file
@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/sub"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/logg"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/knadh/koanf/parsers/toml"
|
||||
"github.com/knadh/koanf/providers/env"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
// Load logger.
|
||||
func initLogger() logf.Logger {
|
||||
loggOpts := logg.LoggOpts{}
|
||||
|
||||
if debugFlag {
|
||||
loggOpts.Color = true
|
||||
loggOpts.Caller = true
|
||||
loggOpts.Debug = true
|
||||
}
|
||||
|
||||
return logg.NewLogg(loggOpts)
|
||||
}
|
||||
|
||||
// Load config file.
|
||||
func initConfig() *koanf.Koanf {
|
||||
var (
|
||||
ko = koanf.New(".")
|
||||
)
|
||||
|
||||
confFile := file.Provider(confFlag)
|
||||
if err := ko.Load(confFile, toml.Parser()); err != nil {
|
||||
lo.Fatal("init: could not load config file", "error", err)
|
||||
}
|
||||
|
||||
if err := ko.Load(env.Provider("CUSTODIAL_", ".", func(s string) string {
|
||||
return strings.ReplaceAll(strings.ToLower(
|
||||
strings.TrimPrefix(s, "CUSTODIAL_")), "__", ".")
|
||||
}), nil); err != nil {
|
||||
lo.Fatal("init: could not override config from env vars", "error", err)
|
||||
}
|
||||
|
||||
if debugFlag {
|
||||
ko.Print()
|
||||
}
|
||||
|
||||
return ko
|
||||
}
|
||||
|
||||
// Load Celo chain provider.
|
||||
func initCeloProvider() *celoutils.Provider {
|
||||
providerOpts := celoutils.ProviderOpts{
|
||||
RpcEndpoint: ko.MustString("chain.rpc_endpoint"),
|
||||
}
|
||||
|
||||
if ko.Bool("chain.testnet") {
|
||||
providerOpts.ChainId = celoutils.TestnetChainId
|
||||
} else {
|
||||
providerOpts.ChainId = celoutils.MainnetChainId
|
||||
}
|
||||
|
||||
provider, err := celoutils.NewProvider(providerOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error loading chain provider", "error", err)
|
||||
}
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
// Load separate redis connection for the tasker on a reserved db namespace.
|
||||
func initAsynqRedisPool() *redis.RedisPool {
|
||||
poolOpts := redis.RedisPoolOpts{
|
||||
DSN: ko.MustString("asynq.dsn"),
|
||||
MinIdleConns: ko.MustInt("redis.min_idle_conn"),
|
||||
}
|
||||
|
||||
pool, err := redis.NewRedisPool(context.Background(), poolOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error connecting to asynq redis db", "error", err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
// Common redis connection on a different db namespace from the takser.
|
||||
func initCommonRedisPool() *redis.RedisPool {
|
||||
poolOpts := redis.RedisPoolOpts{
|
||||
DSN: ko.MustString("redis.dsn"),
|
||||
MinIdleConns: ko.MustInt("redis.min_idle_conn"),
|
||||
}
|
||||
|
||||
pool, err := redis.NewRedisPool(context.Background(), poolOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error connecting to common redis db", "error", err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
// Load redis backed noncestore.
|
||||
func initRedisNoncestore(redisPool *redis.RedisPool, chainProvider *celoutils.Provider, store store.Store) nonce.Noncestore {
|
||||
return nonce.NewRedisNoncestore(nonce.Opts{
|
||||
ChainProvider: chainProvider,
|
||||
RedisPool: redisPool,
|
||||
Store: store,
|
||||
})
|
||||
}
|
||||
|
||||
// Load global lock provider.
|
||||
func initLockProvider(redisPool redislock.RedisClient) *redislock.Client {
|
||||
return redislock.New(redisPool)
|
||||
}
|
||||
|
||||
// Load tasker client.
|
||||
func initTaskerClient(redisPool *redis.RedisPool) *tasker.TaskerClient {
|
||||
return tasker.NewTaskerClient(tasker.TaskerClientOpts{
|
||||
RedisPool: redisPool,
|
||||
})
|
||||
}
|
||||
|
||||
// Load Postgres store.
|
||||
func initPgStore() store.Store {
|
||||
store, err := store.NewPgStore(store.Opts{
|
||||
DSN: ko.MustString("postgres.dsn"),
|
||||
MigrationsFolderPath: migrationsFolderFlag,
|
||||
QueriesFolderPath: queriesFlag,
|
||||
})
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error loading Postgres store", "error", err)
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
// Init JetStream context for both pub/sub.
|
||||
func initJetStream() (*nats.Conn, nats.JetStreamContext) {
|
||||
natsConn, err := nats.Connect(ko.MustString("jetstream.endpoint"))
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error connecting to NATS", "error", err)
|
||||
}
|
||||
|
||||
js, err := natsConn.JetStream()
|
||||
if err != nil {
|
||||
lo.Fatal("init: bad JetStream opts", "error", err)
|
||||
|
||||
}
|
||||
|
||||
return natsConn, js
|
||||
}
|
||||
|
||||
func initSub(natsConn *nats.Conn, jsCtx nats.JetStreamContext, cu *custodial.Custodial) *sub.Sub {
|
||||
sub, err := sub.NewSub(sub.SubOpts{
|
||||
CustodialContainer: cu,
|
||||
JsCtx: jsCtx,
|
||||
Logg: lo,
|
||||
NatsConn: natsConn,
|
||||
})
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error bootstrapping sub", "error", err)
|
||||
}
|
||||
|
||||
return sub
|
||||
}
|
122
cmd/service/main.go
Normal file
122
cmd/service/main.go
Normal file
@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/sub"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type internalServicesContainer struct {
|
||||
apiService *echo.Echo
|
||||
jetstreamSub *sub.Sub
|
||||
taskerService *tasker.TaskerServer
|
||||
}
|
||||
|
||||
var (
|
||||
build string
|
||||
|
||||
confFlag string
|
||||
debugFlag bool
|
||||
migrationsFolderFlag string
|
||||
queriesFlag string
|
||||
|
||||
lo logf.Logger
|
||||
ko *koanf.Koanf
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&confFlag, "config", "config.toml", "Config file location")
|
||||
flag.BoolVar(&debugFlag, "debug", false, "Enable debug logging")
|
||||
flag.StringVar(&migrationsFolderFlag, "migrations", "migrations/", "Migrations folder location")
|
||||
flag.StringVar(&queriesFlag, "queries", "queries.sql", "Queries file location")
|
||||
flag.Parse()
|
||||
|
||||
lo = initLogger()
|
||||
ko = initConfig()
|
||||
}
|
||||
|
||||
func main() {
|
||||
lo.Info("main: starting cic-custodial", "build", build)
|
||||
|
||||
celoProvider := initCeloProvider()
|
||||
asynqRedisPool := initAsynqRedisPool()
|
||||
redisPool := initCommonRedisPool()
|
||||
|
||||
store := initPgStore()
|
||||
redisNoncestore := initRedisNoncestore(redisPool, celoProvider, store)
|
||||
lockProvider := initLockProvider(redisPool.Client)
|
||||
taskerClient := initTaskerClient(asynqRedisPool)
|
||||
|
||||
natsConn, jsCtx := initJetStream()
|
||||
|
||||
custodial, err := custodial.NewCustodial(custodial.Opts{
|
||||
ApprovalTimeout: ko.MustDuration("system.approve_timeout"),
|
||||
CeloProvider: celoProvider,
|
||||
LockProvider: lockProvider,
|
||||
Logg: lo,
|
||||
Noncestore: redisNoncestore,
|
||||
Store: store,
|
||||
RedisClient: redisPool.Client,
|
||||
RegistryAddress: ko.MustString("chain.registry_address"),
|
||||
SystemPrivateKey: ko.MustString("system.private_key"),
|
||||
SystemPublicKey: ko.MustString("system.public_key"),
|
||||
TaskerClient: taskerClient,
|
||||
})
|
||||
if err != nil {
|
||||
lo.Fatal("main: crtical error loading custodial container", "error", err)
|
||||
}
|
||||
|
||||
internalServices := &internalServicesContainer{}
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
signalCh, closeCh := createSigChannel()
|
||||
defer closeCh()
|
||||
|
||||
internalServices.apiService = initApiServer(custodial)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
host := ko.MustString("service.address")
|
||||
lo.Info("main: starting API server", "host", host)
|
||||
if err := internalServices.apiService.Start(host); err != nil {
|
||||
if strings.Contains(err.Error(), "Server closed") {
|
||||
lo.Info("main: shutting down server")
|
||||
} else {
|
||||
lo.Fatal("main: critical error shutting down server", "err", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
internalServices.taskerService = initTasker(custodial, asynqRedisPool)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
lo.Info("main: starting tasker")
|
||||
if err := internalServices.taskerService.Start(); err != nil {
|
||||
lo.Fatal("main: could not start task server", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
internalServices.jetstreamSub = initSub(natsConn, jsCtx, custodial)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
lo.Info("main: starting jetstream sub")
|
||||
if err := internalServices.jetstreamSub.Process(); err != nil {
|
||||
lo.Fatal("main: error running jetstream sub", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
lo.Info("main: graceful shutdown triggered", "signal", <-signalCh)
|
||||
startGracefulShutdown(context.Background(), internalServices)
|
||||
|
||||
wg.Wait()
|
||||
}
|
81
cmd/service/tasker.go
Normal file
81
cmd/service/tasker.go
Normal file
@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker/task"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
const (
|
||||
fixedRetryCount = 25
|
||||
fixedRetryPeriod = time.Second * 1
|
||||
)
|
||||
|
||||
// Load tasker handlers, injecting any necessary handler dependencies from the system container.
|
||||
func initTasker(custodialContainer *custodial.Custodial, redisPool *redis.RedisPool) *tasker.TaskerServer {
|
||||
taskerServerOpts := tasker.TaskerServerOpts{
|
||||
Concurrency: ko.MustInt("asynq.worker_count"),
|
||||
IsFailureHandler: isFailureHandler,
|
||||
Logg: lo,
|
||||
LogLevel: asynq.InfoLevel,
|
||||
RedisPool: redisPool,
|
||||
RetryHandler: retryHandler,
|
||||
}
|
||||
|
||||
taskerServer := tasker.NewTaskerServer(taskerServerOpts)
|
||||
|
||||
taskerServer.RegisterMiddlewareStack([]asynq.MiddlewareFunc{
|
||||
observibilityMiddleware(),
|
||||
})
|
||||
|
||||
taskerServer.RegisterHandlers(tasker.AccountRegisterTask, task.AccountRegisterOnChainProcessor(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.AccountRefillGasTask, task.AccountRefillGasProcessor(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.SignTransferTask, task.SignTransfer(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.SignTransferTaskAuth, task.SignTransferAuthorizationProcessor(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.DispatchTxTask, task.DispatchTx(custodialContainer))
|
||||
|
||||
return taskerServer
|
||||
}
|
||||
|
||||
func isFailureHandler(err error) bool {
|
||||
switch err {
|
||||
// Ignore lock contention errors; retry until lock obtain.
|
||||
case redislock.ErrNotObtained:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func retryHandler(count int, err error, task *asynq.Task) time.Duration {
|
||||
if count < fixedRetryCount {
|
||||
return fixedRetryPeriod
|
||||
} else {
|
||||
return asynq.DefaultRetryDelayFunc(count, err, task)
|
||||
}
|
||||
}
|
||||
|
||||
func observibilityMiddleware() asynq.MiddlewareFunc {
|
||||
return func(handler asynq.Handler) asynq.Handler {
|
||||
return asynq.HandlerFunc(func(ctx context.Context, task *asynq.Task) error {
|
||||
taskId, _ := asynq.GetTaskID(ctx)
|
||||
|
||||
err := handler.ProcessTask(ctx, task)
|
||||
if err != nil && isFailureHandler(err) {
|
||||
lo.Error("tasker: handler error", "err", err, "task_type", task.Type(), "task_id", taskId)
|
||||
} else if asynq.IsPanicError(err) {
|
||||
lo.Error("tasker: handler panic", "err", err, "task_type", task.Type(), "task_id", taskId)
|
||||
} else {
|
||||
lo.Info("tasker: process task", "task_type", task.Type(), "task_id", taskId)
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
31
cmd/service/utils.go
Normal file
31
cmd/service/utils.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func createSigChannel() (chan os.Signal, func()) {
|
||||
signalCh := make(chan os.Signal, 1)
|
||||
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
return signalCh, func() {
|
||||
close(signalCh)
|
||||
}
|
||||
}
|
||||
|
||||
func startGracefulShutdown(ctx context.Context, internalServices *internalServicesContainer) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
internalServices.jetstreamSub.Close()
|
||||
|
||||
if err := internalServices.apiService.Shutdown(ctx); err != nil {
|
||||
lo.Fatal("Could not gracefully shutdown api server", "err", err)
|
||||
}
|
||||
|
||||
internalServices.taskerService.Stop()
|
||||
}
|
51
config.toml
51
config.toml
@ -1,38 +1,35 @@
|
||||
[service]
|
||||
address = ":5000"
|
||||
statsviz_debug = true
|
||||
|
||||
[system]
|
||||
gas_refill_threshold = 100000000000000
|
||||
gas_refill_value = 100000000000000
|
||||
giftable_gas_value = 200000000000000
|
||||
giftable_token_address = "0x486aD10d70107900546455F7a0e022c300F157Bf"
|
||||
giftable_token_value = 5000000000000000000
|
||||
private_key = "a6af6c597c614e3c8ee4b7638ab7c3f737aece3773a5413ca8caf4338e6b06d1"
|
||||
lock_prefix = "lock:"
|
||||
public_key = "0x80097c773B3E83472FC7952c5206a7DB35d42bEF"
|
||||
token_decimals = 18
|
||||
token_transfer_gas_limit = 100000
|
||||
|
||||
[logg]
|
||||
caller = true
|
||||
color = true
|
||||
debug = true
|
||||
# Exposes Go process Prometheus metrics
|
||||
# /metrics endpoint
|
||||
metrics = true
|
||||
docs = false
|
||||
|
||||
[chain]
|
||||
rpc_endpoint = "https://alfajores-forno.celo-testnet.org"
|
||||
rpc_endpoint = ""
|
||||
testnet = true
|
||||
registry_address = ""
|
||||
|
||||
[system]
|
||||
private_key = ""
|
||||
public_key = ""
|
||||
approve_timeout = "30m"
|
||||
|
||||
[postgres]
|
||||
debug = false
|
||||
dsn = "postgres://postgres:postgres@localhost:5432/cic_custodial"
|
||||
dsn = ""
|
||||
|
||||
[redis]
|
||||
debug = false
|
||||
dsn = "redis://localhost:6379/1"
|
||||
minconn = 5
|
||||
# Used for locks and the Noncestore
|
||||
# Ideally use DB 1
|
||||
dsn = ""
|
||||
min_idle_conn = 5
|
||||
|
||||
[asynq]
|
||||
concurrency = 25
|
||||
debug = false
|
||||
dsn = "redis://localhost:6379/0"
|
||||
# Exclusively used by the asynq tasker
|
||||
# Ideally use DB 0
|
||||
dsn = ""
|
||||
task_retention_hrs = 24
|
||||
worker_count = 15
|
||||
|
||||
[jetstream]
|
||||
endpoint = ""
|
68
dev/docker-compose.yaml
Normal file
68
dev/docker-compose.yaml
Normal file
@ -0,0 +1,68 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --save 60 1 --loglevel warning
|
||||
volumes:
|
||||
- cic-custodial-redis:/data
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
postgres:
|
||||
image: postgres:14-alpine
|
||||
restart: unless-stopped
|
||||
user: postgres
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
volumes:
|
||||
- cic-custodial-pg:/var/lib/postgresql/data
|
||||
- ./init_db.sql:/docker-entrypoint-initdb.d/init_db.sql
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
nats:
|
||||
image: nats:2.9
|
||||
restart: unless-stopped
|
||||
command: "-js -sd /nats/data"
|
||||
volumes:
|
||||
- cic-custodial-nats:/nats/data
|
||||
ports:
|
||||
- "127.0.0.1:4222:4222"
|
||||
- "127.0.0.1:8222:8222"
|
||||
asynqmon:
|
||||
image: hibiken/asynqmon
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- REDIS_ADDR=redis:6379
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
cic-chain-events:
|
||||
image: ghcr.io/grassrootseconomics/cic-chain-events/cic-chain-events:latest
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- events.env
|
||||
ports:
|
||||
- '127.0.0.1:5001:5000'
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
cic-custodial-pg:
|
||||
driver: local
|
||||
cic-custodial-redis:
|
||||
driver: local
|
||||
cic-custodial-nats:
|
||||
driver: local
|
2
dev/init_db.sql
Normal file
2
dev/init_db.sql
Normal file
@ -0,0 +1,2 @@
|
||||
CREATE DATABASE cic_custodial;
|
||||
CREATE DATABASE cic_chain_events;
|
@ -1,10 +0,0 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
cic-custodial:
|
||||
image: ${IMAGE_BASE_URL:-ghcr.io/grassrootseconomics/cic-custodial}/cic-custodial:${TAG:-latest}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
cache_from:
|
||||
- ${IMAGE_BASE_URL:-ghcr.io/grassrootseconomics/cic-custodial}/cic-cache:latest
|
||||
|
@ -1,53 +0,0 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
redis:
|
||||
image: redis:6-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --loglevel warning
|
||||
ports:
|
||||
- '6379:6379'
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
postgres:
|
||||
image: postgres:14-alpine
|
||||
restart: unless-stopped
|
||||
user: postgres
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=cic_custodial
|
||||
volumes:
|
||||
- cic-custodial:/var/lib/postgresql/data
|
||||
ports:
|
||||
- '5432:5432'
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
asynqmon:
|
||||
image: hibiken/asynqmon
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '8080:8080'
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
cic-custodial:
|
||||
image: ghcr.io/grassrootseconomics/cic-custodial/cic-custodial:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- '5000:5000'
|
||||
volumes:
|
||||
cic-custodial:
|
||||
driver: local
|
312
docs/docs.go
Normal file
312
docs/docs.go
Normal file
@ -0,0 +1,312 @@
|
||||
// Code generated by swaggo/swag. DO NOT EDIT.
|
||||
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"termsOfService": "https://grassecon.org/pages/terms-and-conditions.html",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "https://grassecon.org/pages/contact-us",
|
||||
"email": "devops@grassecon.org"
|
||||
},
|
||||
"license": {
|
||||
"name": "AGPL-3.0",
|
||||
"url": "https://www.gnu.org/licenses/agpl-3.0.en.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/account/create": {
|
||||
"post": {
|
||||
"description": "Create a new custodial account.",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"account"
|
||||
],
|
||||
"summary": "Create a new custodial account.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/account/status/{address}": {
|
||||
"get": {
|
||||
"description": "Return network balance and nonce.",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"network"
|
||||
],
|
||||
"summary": "Get an address's network balance and nonce.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Account Public Key",
|
||||
"name": "address",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sign/transfer": {
|
||||
"post": {
|
||||
"description": "Sign and dispatch a transfer request.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"network"
|
||||
],
|
||||
"summary": "Sign and dispatch transfer request.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Sign Transfer Request",
|
||||
"name": "signTransferRequest",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
},
|
||||
"voucherAddress": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sign/transferAuth": {
|
||||
"post": {
|
||||
"description": "Sign and dispatch a transfer authorization (approve) request.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"network"
|
||||
],
|
||||
"summary": "Sign and dispatch a transfer authorization (approve) request.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Sign Transfer Authorization (approve) Request",
|
||||
"name": "signTransferAuthorzationRequest",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"authorizedAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"authorizer": {
|
||||
"type": "string"
|
||||
},
|
||||
"voucherAddress": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/track/{trackingId}": {
|
||||
"get": {
|
||||
"description": "Track an OTX (Origin transaction) status.",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"track"
|
||||
],
|
||||
"summary": "Track an OTX (Origin transaction) status.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Tracking Id",
|
||||
"name": "trackingId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.H": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"api.OkResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"result": {
|
||||
"$ref": "#/definitions/api.H"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "",
|
||||
BasePath: "/api",
|
||||
Schemes: []string{},
|
||||
Title: "CIC Custodial API",
|
||||
Description: "Interact with CIC Custodial API",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
286
docs/swagger.json
Normal file
286
docs/swagger.json
Normal file
@ -0,0 +1,286 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "Interact with CIC Custodial API",
|
||||
"title": "CIC Custodial API",
|
||||
"termsOfService": "https://grassecon.org/pages/terms-and-conditions.html",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "https://grassecon.org/pages/contact-us",
|
||||
"email": "devops@grassecon.org"
|
||||
},
|
||||
"license": {
|
||||
"name": "AGPL-3.0",
|
||||
"url": "https://www.gnu.org/licenses/agpl-3.0.en.html"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"basePath": "/api",
|
||||
"paths": {
|
||||
"/account/create": {
|
||||
"post": {
|
||||
"description": "Create a new custodial account.",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"account"
|
||||
],
|
||||
"summary": "Create a new custodial account.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/account/status/{address}": {
|
||||
"get": {
|
||||
"description": "Return network balance and nonce.",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"network"
|
||||
],
|
||||
"summary": "Get an address's network balance and nonce.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Account Public Key",
|
||||
"name": "address",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sign/transfer": {
|
||||
"post": {
|
||||
"description": "Sign and dispatch a transfer request.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"network"
|
||||
],
|
||||
"summary": "Sign and dispatch transfer request.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Sign Transfer Request",
|
||||
"name": "signTransferRequest",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"from": {
|
||||
"type": "string"
|
||||
},
|
||||
"to": {
|
||||
"type": "string"
|
||||
},
|
||||
"voucherAddress": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sign/transferAuth": {
|
||||
"post": {
|
||||
"description": "Sign and dispatch a transfer authorization (approve) request.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"network"
|
||||
],
|
||||
"summary": "Sign and dispatch a transfer authorization (approve) request.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Sign Transfer Authorization (approve) Request",
|
||||
"name": "signTransferAuthorzationRequest",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"authorizedAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"authorizer": {
|
||||
"type": "string"
|
||||
},
|
||||
"voucherAddress": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/track/{trackingId}": {
|
||||
"get": {
|
||||
"description": "Track an OTX (Origin transaction) status.",
|
||||
"consumes": [
|
||||
"*/*"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"track"
|
||||
],
|
||||
"summary": "Track an OTX (Origin transaction) status.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Tracking Id",
|
||||
"name": "trackingId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.OkResp"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.ErrResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.ErrResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.H": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"api.OkResp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"result": {
|
||||
"$ref": "#/definitions/api.H"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
188
docs/swagger.yaml
Normal file
188
docs/swagger.yaml
Normal file
@ -0,0 +1,188 @@
|
||||
basePath: /api
|
||||
definitions:
|
||||
api.ErrResp:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
ok:
|
||||
type: boolean
|
||||
type: object
|
||||
api.H:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
api.OkResp:
|
||||
properties:
|
||||
ok:
|
||||
type: boolean
|
||||
result:
|
||||
$ref: '#/definitions/api.H'
|
||||
type: object
|
||||
info:
|
||||
contact:
|
||||
email: devops@grassecon.org
|
||||
name: API Support
|
||||
url: https://grassecon.org/pages/contact-us
|
||||
description: Interact with CIC Custodial API
|
||||
license:
|
||||
name: AGPL-3.0
|
||||
url: https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
termsOfService: https://grassecon.org/pages/terms-and-conditions.html
|
||||
title: CIC Custodial API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/account/create:
|
||||
post:
|
||||
consumes:
|
||||
- '*/*'
|
||||
description: Create a new custodial account.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.OkResp'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
summary: Create a new custodial account.
|
||||
tags:
|
||||
- account
|
||||
/account/status/{address}:
|
||||
get:
|
||||
consumes:
|
||||
- '*/*'
|
||||
description: Return network balance and nonce.
|
||||
parameters:
|
||||
- description: Account Public Key
|
||||
in: path
|
||||
name: address
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.OkResp'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
summary: Get an address's network balance and nonce.
|
||||
tags:
|
||||
- network
|
||||
/sign/transfer:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Sign and dispatch a transfer request.
|
||||
parameters:
|
||||
- description: Sign Transfer Request
|
||||
in: body
|
||||
name: signTransferRequest
|
||||
required: true
|
||||
schema:
|
||||
properties:
|
||||
amount:
|
||||
type: integer
|
||||
from:
|
||||
type: string
|
||||
to:
|
||||
type: string
|
||||
voucherAddress:
|
||||
type: string
|
||||
type: object
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.OkResp'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
summary: Sign and dispatch transfer request.
|
||||
tags:
|
||||
- network
|
||||
/sign/transferAuth:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Sign and dispatch a transfer authorization (approve) request.
|
||||
parameters:
|
||||
- description: Sign Transfer Authorization (approve) Request
|
||||
in: body
|
||||
name: signTransferAuthorzationRequest
|
||||
required: true
|
||||
schema:
|
||||
properties:
|
||||
amount:
|
||||
type: integer
|
||||
authorizedAddress:
|
||||
type: string
|
||||
authorizer:
|
||||
type: string
|
||||
voucherAddress:
|
||||
type: string
|
||||
type: object
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.OkResp'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
summary: Sign and dispatch a transfer authorization (approve) request.
|
||||
tags:
|
||||
- network
|
||||
/track/{trackingId}:
|
||||
get:
|
||||
consumes:
|
||||
- '*/*'
|
||||
description: Track an OTX (Origin transaction) status.
|
||||
parameters:
|
||||
- description: Tracking Id
|
||||
in: path
|
||||
name: trackingId
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/api.OkResp'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/api.ErrResp'
|
||||
summary: Track an OTX (Origin transaction) status.
|
||||
tags:
|
||||
- track
|
||||
swagger: "2.0"
|
98
go.mod
98
go.mod
@ -1,24 +1,42 @@
|
||||
module github.com/grassrootseconomics/cic-custodial
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/arl/statsviz v0.5.1
|
||||
github.com/bsm/redislock v0.7.2
|
||||
github.com/celo-org/celo-blockchain v1.6.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/VictoriaMetrics/metrics v1.24.0
|
||||
github.com/bsm/redislock v0.9.3
|
||||
github.com/celo-org/celo-blockchain v1.7.2
|
||||
github.com/georgysavva/scany/v2 v2.0.0
|
||||
github.com/go-playground/validator/v10 v10.14.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/grassrootseconomics/cic-celo-sdk v0.3.0
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.1.0
|
||||
github.com/hibiken/asynq v0.23.0
|
||||
github.com/jackc/pgx/v5 v5.1.1
|
||||
github.com/knadh/koanf v1.4.4
|
||||
github.com/labstack/echo/v4 v4.9.1
|
||||
github.com/grassrootseconomics/celoutils v1.4.1-0.20250221123515-f25baeeb2f8c
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.2.0
|
||||
github.com/hibiken/asynq v0.24.0
|
||||
github.com/jackc/pgx/v5 v5.4.0
|
||||
github.com/jackc/tern/v2 v2.1.1
|
||||
github.com/knadh/goyesql/v2 v2.2.0
|
||||
github.com/knadh/koanf/parsers/toml v0.1.0
|
||||
github.com/knadh/koanf/providers/env v0.1.0
|
||||
github.com/knadh/koanf/providers/file v0.1.0
|
||||
github.com/knadh/koanf/v2 v2.0.1
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/nats-io/nats.go v1.27.1
|
||||
github.com/redis/go-redis/v9 v9.0.5
|
||||
github.com/swaggo/echo-swagger v1.4.0
|
||||
github.com/swaggo/swag v1.16.1
|
||||
github.com/zerodha/logf v0.5.5
|
||||
)
|
||||
|
||||
replace github.com/hibiken/asynq => github.com/grassrootseconomics/asynq v0.25.0
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.12.0 // indirect
|
||||
github.com/btcsuite/btcd v0.20.1-beta // indirect
|
||||
github.com/celo-org/celo-bls-go v0.6.4 // indirect
|
||||
@ -28,15 +46,20 @@ require (
|
||||
github.com/celo-org/celo-bls-go-macos v0.6.3 // indirect
|
||||
github.com/celo-org/celo-bls-go-other v0.6.3 // indirect
|
||||
github.com/celo-org/celo-bls-go-windows v0.6.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator v9.31.0+incompatible // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
@ -45,42 +68,57 @@ require (
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/holiman/uint256 v1.2.1 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/huin/goupnp v1.0.3 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/puddle/v2 v2.1.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.0 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/nats-io/nats-server/v2 v2.9.14 // indirect
|
||||
github.com/nats-io/nkeys v0.4.4 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/onsi/gomega v1.24.1 // indirect
|
||||
github.com/pelletier/go-toml v1.7.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/tsdb v0.10.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/valyala/fastrand v1.1.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/valyala/histogram v1.2.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
446
go.sum
446
go.sum
@ -34,60 +34,55 @@ github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxB
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.0 h1:vnVi/y9yKDcD9akmc4NqAoqgQhJrOwUF+j9LTgn4QDE=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8=
|
||||
github.com/VictoriaMetrics/metrics v1.24.0 h1:ILavebReOjYctAGY5QU2F9X0MYvkcrG3aEn2RKa1Zkw=
|
||||
github.com/VictoriaMetrics/metrics v1.24.0/go.mod h1:eFT25kvsTidQFHb6U0oa0rTrDRdz4xTYjpL8+UPohys=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
|
||||
github.com/arl/statsviz v0.5.1 h1:3HY0ZEB738JtguWsD1Tf1pFJZiCcWUmYRq/3OTYKaSI=
|
||||
github.com/arl/statsviz v0.5.1/go.mod h1:zDnjgRblGm1Dyd7J5YlbH7gM1/+HRC+SfkhZhQb5AnM=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
|
||||
github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
|
||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/bsm/redislock v0.7.2 h1:jggqOio8JyX9FJBKIfjF3fTxAu/v7zC5mAID9LveqG4=
|
||||
github.com/bsm/redislock v0.7.2/go.mod h1:kS2g0Yvlymc9Dz8V3iVYAtLAaSVruYbAFdYBDrmC5WU=
|
||||
github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
||||
github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
|
||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/bsm/redislock v0.9.3 h1:osmvugkXGiLDEhzUPdM0EUtKpTEgLLuli4Ky2Z4vx38=
|
||||
github.com/bsm/redislock v0.9.3/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
@ -100,41 +95,46 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72/go.mod h1:OEE5igu/CDjGegM1Jn6ZMo7R6LlV/JChAkjfQQIRLpg=
|
||||
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
github.com/celo-org/celo-blockchain v1.6.1 h1:Kv+OXLXwdORDz3aIrGY1oCuw0aYSlK4KH7o8cGxO8FU=
|
||||
github.com/celo-org/celo-blockchain v1.6.1/go.mod h1:NdcP5idffWajmOP79Q0PCRfaFNTmTdSKBMbngLDxjpQ=
|
||||
github.com/celo-org/celo-bls-go v0.2.4/go.mod h1:eXUCLXu5F1yfd3M+3VaUk5ZUXaA0sLK2rWdLC1Cfaqo=
|
||||
github.com/celo-org/celo-blockchain v1.7.2 h1:LjQ+t89inzSK2K9i/twwwW07kIE1ubEVDlrcet+UvLU=
|
||||
github.com/celo-org/celo-blockchain v1.7.2/go.mod h1:x5HsfXAfUjxKQfTWzroQSb2HljaQEVgf2/mQVDPIMIY=
|
||||
github.com/celo-org/celo-bls-go v0.3.3/go.mod h1:eoMAORYWgZ5HOo3Z0bIAv/nbPtj/eArIO0nh7XFx7rk=
|
||||
github.com/celo-org/celo-bls-go v0.6.4 h1:zBf6pEr9k64gaO0VSYnDfLCBmrxDigc4yUaDvWub5G8=
|
||||
github.com/celo-org/celo-bls-go v0.6.4/go.mod h1:apSlSDPoXIdGseCS4Z2AMrhO5B1xpIVTXYpfv/uRd04=
|
||||
github.com/celo-org/celo-bls-go-android v0.3.2/go.mod h1:cFgtFRH8+6x5b+EyG5SqniXY3aKd03NBSGDgITscX34=
|
||||
github.com/celo-org/celo-bls-go-android v0.6.3 h1:mCtzKM99OCfsSu/i9A4+BaUJhEfYSEWPR0wHUu4YlO0=
|
||||
github.com/celo-org/celo-bls-go-android v0.6.3/go.mod h1:N8rOFwyCS3Ff9p45/rLfdLWfoyFwOvjVQ6F+VKbmIbU=
|
||||
github.com/celo-org/celo-bls-go-ios v0.3.2/go.mod h1:eaSoMpx29YV5oF7jXVChzJpNfxeZHnAa8G4PjL5CyW0=
|
||||
github.com/celo-org/celo-bls-go-ios v0.6.3 h1:8t08eq924VlxjTCKwzRWOlULRWlhqNmF+ok7pn4Unjg=
|
||||
github.com/celo-org/celo-bls-go-ios v0.6.3/go.mod h1:huG1qfXZVMDkFmEZo4UYOAu46EEzYrR8brE4flPul5c=
|
||||
github.com/celo-org/celo-bls-go-linux v0.3.2/go.mod h1:DVpJadg22OrxBtMb0ub6iNVdqDBL/r6EDdWVAA0bHa0=
|
||||
github.com/celo-org/celo-bls-go-linux v0.6.3 h1:9Evpz1ix4v7tswiJ89+ED3m8sYQ6PG9T0ZogHrvz0xE=
|
||||
github.com/celo-org/celo-bls-go-linux v0.6.3/go.mod h1:E6fEQ+whLgMqEToE1e3FWf/9+ikG+wNE5ik7z5Dxgbc=
|
||||
github.com/celo-org/celo-bls-go-macos v0.3.2/go.mod h1:mYPuRqGMVxj6yZUeL6Q6ggtP52HPBS1jz+FvBPXQ7QA=
|
||||
github.com/celo-org/celo-bls-go-macos v0.6.3 h1:+sEiFYoqdlDVO50LmONBSzu/PxCP0aP8fiGjWuAvBzg=
|
||||
github.com/celo-org/celo-bls-go-macos v0.6.3/go.mod h1:RC05VQ2cvF8vMc0tpLc7pfp8oSKrrGcI+o5LLXGequk=
|
||||
github.com/celo-org/celo-bls-go-other v0.3.2/go.mod h1:tNxZNfekzyT7TdYQbyNPhkfpcYtA3KCU/IKX5FNxM/U=
|
||||
github.com/celo-org/celo-bls-go-other v0.6.3 h1:DTVLfg/ZNizB5Wnbxq0mYh3VXsDtD1lZFvd0Ye4xv7o=
|
||||
github.com/celo-org/celo-bls-go-other v0.6.3/go.mod h1:1xEWTbCXUrDx3Z6R9YlLi1zFNNn1oMMnEcpLbcW+q74=
|
||||
github.com/celo-org/celo-bls-go-windows v0.3.2/go.mod h1:82GC5iJA9Qw5gynhYqR8ht3J+l/MO8eSNzgSTMI8UdA=
|
||||
github.com/celo-org/celo-bls-go-windows v0.6.3 h1:yrHZ+1EjONf++u5QEUseebImiD23Rv2GEcoYfPxYhuw=
|
||||
github.com/celo-org/celo-bls-go-windows v0.6.3/go.mod h1:8bRJbLcHREIAWn+5iZM3u8rGPBsWepp9BdF3SexisuM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs=
|
||||
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
|
||||
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
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=
|
||||
@ -154,21 +154,21 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
|
||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/docker/docker v1.6.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU=
|
||||
github.com/georgysavva/scany/v2 v2.0.0/go.mod h1:sigOdh+0qb/+aOs3TVhehVT10p8qJL7K/Zhyz8vWo38=
|
||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@ -177,43 +177,45 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
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-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
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.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
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/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA=
|
||||
github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
|
||||
github.com/go-redis/redis/v8 v8.11.2/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
|
||||
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||
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-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
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.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.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@ -225,13 +227,11 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@ -251,21 +251,18 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
@ -278,67 +275,41 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||
github.com/grassrootseconomics/cic-celo-sdk v0.3.0 h1:uqYlad/sL4nWzExSE3ecfGQNdY0Gs6pqpkez1vX5XnI=
|
||||
github.com/grassrootseconomics/cic-celo-sdk v0.3.0/go.mod h1:EiR6d03GYu6jlVKNL1MbTAw/bqAW2WP3J/lkrZxPMdU=
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.1.0 h1:0fev2hYkGEyFX2D4oUG8yy4jXhtHv7qUtLLboXL5ycw=
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.1.0/go.mod h1:JtkXc+yDUiQQJdhYTqddZI/itdYGHY7H8PNZzBo4hCk=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/grassrootseconomics/asynq v0.25.0 h1:2zSz5YwNLu/oCTm/xfNixn86i9aw4zth9Dl0dc2kFEs=
|
||||
github.com/grassrootseconomics/asynq v0.25.0/go.mod h1:pe2XOdK1eIbTgTmRFHIYl75lvVuTPJxZq2T9Ocz/+2s=
|
||||
github.com/grassrootseconomics/celoutils v1.4.0 h1:AJNKiOpfnQqZ3kRxeUlhWH/zlDDjhtbs/OzAMb5zU4A=
|
||||
github.com/grassrootseconomics/celoutils v1.4.0/go.mod h1:Uo5YRy6AGLAHDZj9jaOI+AWoQ1H3L0v79728pPMkm9Q=
|
||||
github.com/grassrootseconomics/celoutils v1.4.1-0.20240824053735-9873e8ac0473 h1:caDCYYMKi7TEl00mlV7Vl2lTcsrD7hoFcQwXCec9Ztc=
|
||||
github.com/grassrootseconomics/celoutils v1.4.1-0.20240824053735-9873e8ac0473/go.mod h1:Uo5YRy6AGLAHDZj9jaOI+AWoQ1H3L0v79728pPMkm9Q=
|
||||
github.com/grassrootseconomics/celoutils v1.4.1-0.20250221123515-f25baeeb2f8c h1:HrCc3I59rA9wha2QkJe+vs9vF1r+uKDbxUrM/EFCJdk=
|
||||
github.com/grassrootseconomics/celoutils v1.4.1-0.20250221123515-f25baeeb2f8c/go.mod h1:Uo5YRy6AGLAHDZj9jaOI+AWoQ1H3L0v79728pPMkm9Q=
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.2.0 h1:YqibbPzX0tQKmxU1nUGzThPKk/fiYeYZY6Aif3eyu8U=
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.2.0/go.mod h1:WhBXNzNIvHmS6B2hAeShs56oa9Azb4jQSrOMKuMdBWw=
|
||||
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
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/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=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
|
||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff/go.mod h1:Feit0l8NcNO4g69XNjwvsR0LGcwMMfzI1TF253rOIlQ=
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU=
|
||||
github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hibiken/asynq v0.23.0 h1:kmKkNFgqiXBatC8oz94Mer6uvKoGn4STlIVDV5wnKyE=
|
||||
github.com/hibiken/asynq v0.23.0/go.mod h1:K70jPVx+CAmmQrXot7Dru0D52EO7ob4BIun3ri5z1Qw=
|
||||
github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs=
|
||||
github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||
github.com/holiman/uint256 v1.2.1 h1:XRtyuda/zw2l+Bq/38n5XUoEF72aSOu/77Thd9pPp2o=
|
||||
github.com/holiman/uint256 v1.2.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
|
||||
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
|
||||
github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
|
||||
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
|
||||
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
|
||||
@ -353,12 +324,14 @@ github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mq
|
||||
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
|
||||
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 h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgx/v5 v5.1.1 h1:pZD79K1SYv8wc2HmCQA6VdmRQi7/OtCfv9bM3WAXUYA=
|
||||
github.com/jackc/pgx/v5 v5.1.1/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||
github.com/jackc/puddle/v2 v2.1.2 h1:0f7vaaXINONKTsxYDn4otOAiJanX/BMeAtY//BXqzlg=
|
||||
github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels=
|
||||
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.4.0 h1:BSr+GCm4N6QcgIwv0DyTFHK9ugfEFF9DzSbbzxOiXU0=
|
||||
github.com/jackc/pgx/v5 v5.4.0/go.mod h1:q6iHT8uDNXWiFNOlRqJzBTaSH3+2xCXkokxHZC5qWFY=
|
||||
github.com/jackc/puddle/v2 v2.2.0 h1:RdcDk92EJBuBS55nQMMYFXTxwstHug4jkhT5pq8VxPk=
|
||||
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/tern/v2 v2.1.1 h1:qDo41wTtDHrTgkN7lhcoMQ6oiAWqiD8xKgslxyoKHNQ=
|
||||
github.com/jackc/tern/v2 v2.1.1/go.mod h1:xnRalAguscgir18eW/wscn/QTEoWwFqrpW+5S+CREWM=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
@ -366,95 +339,97 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1C
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
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/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
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/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
|
||||
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
|
||||
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knadh/koanf v1.4.4 h1:d2jY5nCCeoaiqvEKSBW9rEc93EfNy/XWgWsSB3j7JEA=
|
||||
github.com/knadh/koanf v1.4.4/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
|
||||
github.com/knadh/goyesql/v2 v2.2.0 h1:DNQIzgITmMTXA+z+jDzbXCpgr7fGD6Hp0AJ7ZLEAem4=
|
||||
github.com/knadh/goyesql/v2 v2.2.0/go.mod h1:is+wK/XQBukYK3DdKfpJRyDH9U/ZTMyX2u6DFijjRnI=
|
||||
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
|
||||
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||
github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
|
||||
github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
|
||||
github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg=
|
||||
github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ=
|
||||
github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c=
|
||||
github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA=
|
||||
github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g=
|
||||
github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
|
||||
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
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=
|
||||
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
@ -462,20 +437,25 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
|
||||
github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
|
||||
github.com/nats-io/nats-server/v2 v2.9.14 h1:n2GscWVgXpA14vQSRP/MM1SGi4wyazR9l19/gWxqgXQ=
|
||||
github.com/nats-io/nats-server/v2 v2.9.14/go.mod h1:40ZwFm4npKdFBhOdY7rkh3YyI1oI91FzLvlYyB7HfzM=
|
||||
github.com/nats-io/nats.go v1.27.1 h1:OuYnal9aKVSnOzLQIzf7554OXMCG7KbaTkCSBHRcSoo=
|
||||
github.com/nats-io/nats.go v1.27.1/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
|
||||
github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
|
||||
github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
@ -483,25 +463,20 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
|
||||
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
|
||||
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
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/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
@ -513,45 +488,33 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
|
||||
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
|
||||
github.com/redis/go-redis/v9 v9.0.1/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
||||
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
|
||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
||||
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
|
||||
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
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.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
|
||||
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=
|
||||
@ -559,22 +522,24 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@ -582,9 +547,21 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/swaggo/echo-swagger v1.4.0 h1:RCxLKySw1SceHLqnmc41pKyiIeE+OiD7NSI7FUOBlLo=
|
||||
github.com/swaggo/echo-swagger v1.4.0/go.mod h1:Wh3VlwjZGZf/LH0s81tz916JokuPG7y/ZqaqnckYqoQ=
|
||||
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
|
||||
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
|
||||
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
|
||||
github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
|
||||
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
@ -596,49 +573,44 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
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/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
|
||||
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
|
||||
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
|
||||
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zerodha/logf v0.5.5 h1:AhxHlixHNYwhFjvlgTv6uO4VBKYKxx2I6SbHoHtWLBk=
|
||||
github.com/zerodha/logf v0.5.5/go.mod h1:HWpfKsie+WFFpnUnUxelT6Z0FC6xu9+qt+oXNMPg6y8=
|
||||
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.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 v0.10.0 h1:G3eWbSNIskeRqtsN/1uI5B+eP73y3JUuBsv9AZjehb4=
|
||||
go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -660,15 +632,15 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.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/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=
|
||||
@ -683,24 +655,20 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -714,23 +682,19 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -739,23 +703,16 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -763,39 +720,46 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -813,7 +777,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -823,12 +786,11 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/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/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=
|
||||
@ -853,7 +815,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@ -866,45 +827,32 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
@ -912,13 +860,14 @@ 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.3/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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
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-20200615113413-eeeca48fe776/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=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
@ -931,4 +880,3 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
67
internal/api/account.go
Normal file
67
internal/api/account.go
Normal file
@ -0,0 +1,67 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker/task"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// HandleAccountCreate godoc
|
||||
// @Summary Create a new custodial account.
|
||||
// @Description Create a new custodial account.
|
||||
// @Tags account
|
||||
// @Accept */*
|
||||
// @Produce json
|
||||
// @Success 200 {object} OkResp
|
||||
// @Failure 500 {object} ErrResp
|
||||
// @Router /account/create [post]
|
||||
func HandleAccountCreate(cu *custodial.Custodial) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
generatedKeyPair, err := keypair.Generate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.Store.WriteKeyPair(c.Request().Context(), generatedKeyPair)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trackingId := uuid.NewString()
|
||||
taskPayload, err := json.Marshal(task.AccountPayload{
|
||||
PublicKey: generatedKeyPair.Public,
|
||||
TrackingId: trackingId,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
c.Request().Context(),
|
||||
tasker.AccountRegisterTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Id: trackingId,
|
||||
Payload: taskPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"publicKey": generatedKeyPair.Public,
|
||||
"custodialId": id,
|
||||
"trackingId": trackingId,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
11
internal/api/errors.go
Normal file
11
internal/api/errors.go
Normal file
@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func NewBadRequestError(message ...interface{}) *echo.HTTPError {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, message...)
|
||||
}
|
64
internal/api/network.go
Normal file
64
internal/api/network.go
Normal file
@ -0,0 +1,64 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// HandleNetworkAccountStatus godoc
|
||||
// @Summary Get an address's network balance and nonce.
|
||||
// @Description Return network balance and nonce.
|
||||
// @Tags network
|
||||
// @Accept */*
|
||||
// @Produce json
|
||||
// @Param address path string true "Account Public Key"
|
||||
// @Success 200 {object} OkResp
|
||||
// @Failure 400 {object} ErrResp
|
||||
// @Failure 500 {object} ErrResp
|
||||
// @Router /account/status/{address} [get]
|
||||
func HandleNetworkAccountStatus(cu *custodial.Custodial) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
var (
|
||||
accountStatusRequest struct {
|
||||
Address string `param:"address" validate:"required,eth_addr_checksum"`
|
||||
}
|
||||
networkBalance big.Int
|
||||
networkNonce uint64
|
||||
)
|
||||
|
||||
if err := c.Bind(&accountStatusRequest); err != nil {
|
||||
return NewBadRequestError(ErrInvalidJSON)
|
||||
}
|
||||
|
||||
if err := c.Validate(accountStatusRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
c.Request().Context(),
|
||||
eth.Nonce(celoutils.HexToAddress(accountStatusRequest.Address), nil).Returns(&networkNonce),
|
||||
eth.Balance(celoutils.HexToAddress(accountStatusRequest.Address), nil).Returns(&networkBalance),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if networkNonce > 0 {
|
||||
networkNonce--
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"balance": fmt.Sprintf("%s CELO", w3.FromWei(&networkBalance, 18)),
|
||||
"nonce": networkNonce,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/keystore"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker/task"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type registrationResponse struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
TaskRef string `json:"taskRef"`
|
||||
}
|
||||
|
||||
func RegistrationHandler(
|
||||
taskerClient *tasker.TaskerClient,
|
||||
keystore keystore.Keystore,
|
||||
) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
generatedKeyPair, err := keypair.Generate()
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, errResp{
|
||||
Ok: false,
|
||||
Error: KEYPAIR_ERROR,
|
||||
})
|
||||
}
|
||||
|
||||
if err := keystore.WriteKeyPair(c.Request().Context(), generatedKeyPair); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, errResp{
|
||||
Ok: false,
|
||||
Error: INTERNAL_ERROR,
|
||||
})
|
||||
}
|
||||
|
||||
taskPayload, err := json.Marshal(task.SystemPayload{
|
||||
PublicKey: generatedKeyPair.Public,
|
||||
})
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, errResp{
|
||||
Ok: false,
|
||||
Error: JSON_MARSHAL_ERROR,
|
||||
})
|
||||
}
|
||||
|
||||
task, err := taskerClient.CreateTask(
|
||||
tasker.PrepareAccountTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: taskPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, errResp{
|
||||
Ok: false,
|
||||
Error: TASK_CHAIN_ERROR,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{
|
||||
Ok: true,
|
||||
Data: registrationResponse{
|
||||
PublicKey: generatedKeyPair.Public,
|
||||
TaskRef: task.ID,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
98
internal/api/sign_transfer.go
Normal file
98
internal/api/sign_transfer.go
Normal file
@ -0,0 +1,98 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker/task"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// HandleSignTransfer godoc
|
||||
//
|
||||
// @Summary Sign and dispatch transfer request.
|
||||
// @Description Sign and dispatch a transfer request.
|
||||
// @Tags network
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param signTransferRequest body object{from=string,to=string,voucherAddress=string,amount=uint64} true "Sign Transfer Request"
|
||||
// @Success 200 {object} OkResp
|
||||
// @Failure 400 {object} ErrResp
|
||||
// @Failure 500 {object} ErrResp
|
||||
// @Router /sign/transfer [post]
|
||||
func HandleSignTransfer(cu *custodial.Custodial) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
var (
|
||||
req struct {
|
||||
From string `json:"from" validate:"required,eth_addr_checksum"`
|
||||
To string `json:"to" validate:"required,eth_addr_checksum"`
|
||||
VoucherAddress string `json:"voucherAddress" validate:"required,eth_addr_checksum"`
|
||||
Amount uint64 `json:"amount" validate:"gt=0"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return NewBadRequestError(ErrInvalidJSON)
|
||||
}
|
||||
|
||||
if err := c.Validate(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accountActive, gasLock, err := cu.Store.GetAccountStatus(c.Request().Context(), req.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !accountActive {
|
||||
return c.JSON(http.StatusForbidden, ErrResp{
|
||||
Ok: false,
|
||||
Message: "Account pending activation. Try again later.",
|
||||
})
|
||||
}
|
||||
|
||||
if gasLock {
|
||||
return c.JSON(http.StatusForbidden, ErrResp{
|
||||
Ok: false,
|
||||
Message: "Gas lock. Gas balance unavailable. Try again later.",
|
||||
})
|
||||
}
|
||||
|
||||
trackingId := uuid.NewString()
|
||||
|
||||
taskPayload, err := json.Marshal(task.TransferPayload{
|
||||
TrackingId: trackingId,
|
||||
From: req.From,
|
||||
To: req.To,
|
||||
VoucherAddress: req.VoucherAddress,
|
||||
Amount: req.Amount,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
c.Request().Context(),
|
||||
tasker.SignTransferTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Id: trackingId,
|
||||
Payload: taskPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"trackingId": trackingId,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
107
internal/api/sign_transfer_auth.go
Normal file
107
internal/api/sign_transfer_auth.go
Normal file
@ -0,0 +1,107 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker/task"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// Max 10k vouchers per approval session
|
||||
const approvalSafetyLimit = 10000 * 1000000
|
||||
|
||||
// HandleSignTransferAuthorization godoc
|
||||
//
|
||||
// @Summary Sign and dispatch a transfer authorization (approve) request.
|
||||
// @Description Sign and dispatch a transfer authorization (approve) request.
|
||||
// @Tags network
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param signTransferAuthorzationRequest body object{amount=uint64,authorizer=string,authorizedAddress=string,voucherAddress=string} true "Sign Transfer Authorization (approve) Request"
|
||||
// @Success 200 {object} OkResp
|
||||
// @Failure 400 {object} ErrResp
|
||||
// @Failure 500 {object} ErrResp
|
||||
// @Router /sign/transferAuth [post]
|
||||
func HandleSignTranserAuthorization(cu *custodial.Custodial) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
var (
|
||||
req struct {
|
||||
Amount uint64 `json:"amount" validate:"gte=0"`
|
||||
Authorizer string `json:"authorizer" validate:"required,eth_addr_checksum"`
|
||||
AuthorizedAddress string `json:"authorizedAddress" validate:"required,eth_addr_checksum"`
|
||||
VoucherAddress string `json:"voucherAddress" validate:"required,eth_addr_checksum"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return NewBadRequestError(ErrInvalidJSON)
|
||||
}
|
||||
|
||||
if err := c.Validate(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accountActive, gasLock, err := cu.Store.GetAccountStatus(c.Request().Context(), req.Authorizer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Amount > approvalSafetyLimit {
|
||||
return c.JSON(http.StatusForbidden, ErrResp{
|
||||
Ok: false,
|
||||
Message: "Approval amount per session exceeds 10k.",
|
||||
})
|
||||
}
|
||||
|
||||
if !accountActive {
|
||||
return c.JSON(http.StatusForbidden, ErrResp{
|
||||
Ok: false,
|
||||
Message: "Account pending activation. Try again later.",
|
||||
})
|
||||
}
|
||||
|
||||
if gasLock {
|
||||
return c.JSON(http.StatusForbidden, ErrResp{
|
||||
Ok: false,
|
||||
Message: "Gas lock. Gas balance unavailable. Try again later.",
|
||||
})
|
||||
}
|
||||
|
||||
trackingId := uuid.NewString()
|
||||
|
||||
taskPayload, err := json.Marshal(task.TransferAuthPayload{
|
||||
TrackingId: trackingId,
|
||||
Amount: req.Amount,
|
||||
Authorizer: req.Authorizer,
|
||||
AuthorizedAddress: req.AuthorizedAddress,
|
||||
VoucherAddress: req.VoucherAddress,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
c.Request().Context(),
|
||||
tasker.SignTransferTaskAuth,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Id: trackingId,
|
||||
Payload: taskPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"trackingId": trackingId,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
15
internal/api/swagger.go
Normal file
15
internal/api/swagger.go
Normal file
@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
// @title CIC Custodial API
|
||||
// @version 1.0
|
||||
// @description Interact with CIC Custodial API
|
||||
// @termsOfService https://grassecon.org/pages/terms-and-conditions.html
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.url https://grassecon.org/pages/contact-us
|
||||
// @contact.email devops@grassecon.org
|
||||
|
||||
// @license.name AGPL-3.0
|
||||
// @license.url https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
|
||||
// @BasePath /api
|
49
internal/api/track.go
Normal file
49
internal/api/track.go
Normal file
@ -0,0 +1,49 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// HandleTrackTx godoc
|
||||
// @Summary Track an OTX (Origin transaction) status.
|
||||
// @Description Track an OTX (Origin transaction) status.
|
||||
// @Tags track
|
||||
// @Accept */*
|
||||
// @Produce json
|
||||
// @Param trackingId path string true "Tracking Id"
|
||||
// @Success 200 {object} OkResp
|
||||
// @Failure 400 {object} ErrResp
|
||||
// @Failure 500 {object} ErrResp
|
||||
// @Router /track/{trackingId} [get]
|
||||
func HandleTrackTx(cu *custodial.Custodial) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
var (
|
||||
txStatusRequest struct {
|
||||
TrackingId string `param:"trackingId" validate:"required,uuid"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := c.Bind(&txStatusRequest); err != nil {
|
||||
return NewBadRequestError(err)
|
||||
}
|
||||
|
||||
if err := c.Validate(txStatusRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txs, err := cu.Store.GetTxStatus(c.Request().Context(), txStatusRequest.TrackingId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"transaction": txs,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
transferRequest struct {
|
||||
From string `json:"from" validate:"required,eth_addr"`
|
||||
To string `json:"to" validate:"required,eth_addr"`
|
||||
VoucherAddress string `json:"voucherAddress" validate:"required,eth_addr"`
|
||||
Amount string `json:"amount" validate:"required,numeric"`
|
||||
}
|
||||
|
||||
transferResponse struct {
|
||||
TaskRef string `json:"taskRef"`
|
||||
}
|
||||
)
|
||||
|
||||
func TransferHandler(
|
||||
taskerClient *tasker.TaskerClient,
|
||||
) func(echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
transferPayload := new(transferRequest)
|
||||
|
||||
if err := c.Bind(transferPayload); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, errResp{
|
||||
Ok: false,
|
||||
Error: BIND_ERROR,
|
||||
})
|
||||
}
|
||||
if err := c.Validate(transferPayload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
taskPayload, err := json.Marshal(transferPayload)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, errResp{
|
||||
Ok: false,
|
||||
Error: JSON_MARSHAL_ERROR,
|
||||
})
|
||||
}
|
||||
|
||||
task, err := taskerClient.CreateTask(
|
||||
tasker.TransferTokenTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: taskPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, errResp{
|
||||
Ok: false,
|
||||
Error: TASK_CHAIN_ERROR,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{
|
||||
Ok: true,
|
||||
Data: transferResponse{
|
||||
TaskRef: task.ID,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -1,20 +1,19 @@
|
||||
package api
|
||||
|
||||
const (
|
||||
INTERNAL_ERROR = "ERR_INTERNAL"
|
||||
KEYPAIR_ERROR = "ERR_GEN_KEYPAIR"
|
||||
JSON_MARSHAL_ERROR = "ERR_PAYLOAD_SERIALIZATION"
|
||||
TASK_CHAIN_ERROR = "ERR_START_TASK_CHAIN"
|
||||
VALIDATION_ERROR = "ERR_VALIDATE"
|
||||
BIND_ERROR = "ERR_BIND"
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidJSON = errors.New("Invalid JSON structure.")
|
||||
)
|
||||
|
||||
type okResp struct {
|
||||
type H map[string]any
|
||||
|
||||
type OkResp struct {
|
||||
Ok bool `json:"ok"`
|
||||
Data interface{} `json:"data"`
|
||||
Result H `json:"result"`
|
||||
}
|
||||
|
||||
type errResp struct {
|
||||
type ErrResp struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
@ -1,24 +1,18 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-playground/validator"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type CustomValidator struct {
|
||||
Validator *validator.Validate
|
||||
type Validator struct {
|
||||
ValidatorProvider *validator.Validate
|
||||
}
|
||||
|
||||
func (cv *CustomValidator) Validate(i interface{}) error {
|
||||
if err := cv.Validator.Struct(i); err != nil {
|
||||
fmt.Println(err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, errResp{
|
||||
Ok: false,
|
||||
Error: VALIDATION_ERROR,
|
||||
})
|
||||
func (v *Validator) Validate(i interface{}) error {
|
||||
if err := v.ValidatorProvider.Struct(i); err != nil {
|
||||
if _, ok := err.(validator.ValidationErrors); ok {
|
||||
return NewBadRequestError(err.(validator.ValidationErrors).Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
28
internal/custodial/abis.go
Normal file
28
internal/custodial/abis.go
Normal file
@ -0,0 +1,28 @@
|
||||
package custodial
|
||||
|
||||
import "github.com/grassrootseconomics/w3-celo-patch"
|
||||
|
||||
const (
|
||||
Approve = "approve"
|
||||
Check = "check"
|
||||
GiveTo = "giveTo"
|
||||
MintTo = "mintTo"
|
||||
NextTime = "nextTime"
|
||||
Register = "register"
|
||||
Transfer = "transfer"
|
||||
TransferFrom = "transferFrom"
|
||||
)
|
||||
|
||||
// Define common smart contrcat ABI's that can be injected into the system container.
|
||||
// Any relevant function signature that will be used by the custodial system can be defined here.
|
||||
func initAbis() map[string]*w3.Func {
|
||||
return map[string]*w3.Func{
|
||||
Approve: w3.MustNewFunc("approve(address, uint256)", "bool"),
|
||||
Check: w3.MustNewFunc("check(address)", "bool"),
|
||||
GiveTo: w3.MustNewFunc("giveTo(address)", "uint256"),
|
||||
MintTo: w3.MustNewFunc("mintTo(address, uint256)", "bool"),
|
||||
NextTime: w3.MustNewFunc("nextTime(address)", "uint256"),
|
||||
Register: w3.MustNewFunc("register(address)", ""),
|
||||
Transfer: w3.MustNewFunc("transfer(address,uint256)", "bool"),
|
||||
}
|
||||
}
|
89
internal/custodial/custodial.go
Normal file
89
internal/custodial/custodial.go
Normal file
@ -0,0 +1,89 @@
|
||||
package custodial
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common"
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/util"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type (
|
||||
Opts struct {
|
||||
ApprovalTimeout time.Duration
|
||||
CeloProvider *celoutils.Provider
|
||||
LockProvider *redislock.Client
|
||||
Logg logf.Logger
|
||||
Noncestore nonce.Noncestore
|
||||
Store store.Store
|
||||
RedisClient *redis.Client
|
||||
RegistryAddress string
|
||||
SystemPrivateKey string
|
||||
SystemPublicKey string
|
||||
TaskerClient *tasker.TaskerClient
|
||||
}
|
||||
|
||||
Custodial struct {
|
||||
ApprovalTimeout time.Duration
|
||||
Abis map[string]*w3.Func
|
||||
CeloProvider *celoutils.Provider
|
||||
LockProvider *redislock.Client
|
||||
Logg logf.Logger
|
||||
Noncestore nonce.Noncestore
|
||||
Store store.Store
|
||||
RedisClient *redis.Client
|
||||
RegistryMap map[string]common.Address
|
||||
SystemPrivateKey *ecdsa.PrivateKey
|
||||
SystemPublicKey string
|
||||
TaskerClient *tasker.TaskerClient
|
||||
}
|
||||
)
|
||||
|
||||
func NewCustodial(o Opts) (*Custodial, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), util.SLATimeout)
|
||||
defer cancel()
|
||||
|
||||
registryMap, err := o.CeloProvider.RegistryMap(ctx, celoutils.HexToAddress(o.RegistryAddress))
|
||||
if err != nil {
|
||||
o.Logg.Error("custodial: critical error loading contracts from registry: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
systemNonce, err := o.Noncestore.Peek(ctx, o.SystemPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o.Logg.Info("custodial: loaded_nonce", "system_nonce", systemNonce)
|
||||
|
||||
privateKey, err := eth_crypto.HexToECDSA(o.SystemPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Custodial{
|
||||
ApprovalTimeout: o.ApprovalTimeout,
|
||||
Abis: initAbis(),
|
||||
CeloProvider: o.CeloProvider,
|
||||
LockProvider: o.LockProvider,
|
||||
Logg: o.Logg,
|
||||
Noncestore: o.Noncestore,
|
||||
Store: o.Store,
|
||||
RedisClient: o.RedisClient,
|
||||
RegistryMap: registryMap,
|
||||
SystemPrivateKey: privateKey,
|
||||
SystemPublicKey: o.SystemPublicKey,
|
||||
TaskerClient: o.TaskerClient,
|
||||
}, nil
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
)
|
||||
|
||||
type Keystore interface {
|
||||
WriteKeyPair(context.Context, keypair.Key) error
|
||||
LoadPrivateKey(context.Context, string) (*ecdsa.PrivateKey, error)
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func applyMigration(dbPool *pgxpool.Pool) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := dbPool.Exec(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS keystore (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
public_key TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
PostgresPool *pgxpool.Pool
|
||||
Logg logf.Logger
|
||||
}
|
||||
|
||||
type PostgresKeystore struct {
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPostgresKeytore(o Opts) (Keystore, error) {
|
||||
if err := applyMigration(o.PostgresPool); err != nil {
|
||||
return nil, fmt.Errorf("keystore migration failed %v", err)
|
||||
}
|
||||
o.Logg.Info("Successfully ran keystore migrations")
|
||||
|
||||
return &PostgresKeystore{
|
||||
db: o.PostgresPool,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ks *PostgresKeystore) WriteKeyPair(ctx context.Context, keypair keypair.Key) error {
|
||||
_, err := ks.db.Exec(ctx, "INSERT INTO keystore(public_key, private_key) VALUES($1, $2)", keypair.Public, keypair.Private)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ks *PostgresKeystore) LoadPrivateKey(ctx context.Context, publicKey string) (*ecdsa.PrivateKey, error) {
|
||||
var (
|
||||
privateKeyString string
|
||||
)
|
||||
|
||||
if err := ks.db.QueryRow(ctx, "SELECT private_key FROM keystore WHERE public_key=$1", publicKey).Scan(&privateKeyString); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKey, err := eth_crypto.HexToECDSA(privateKeyString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
@ -2,10 +2,10 @@ package nonce
|
||||
|
||||
import "context"
|
||||
|
||||
// Noncestore defines how a nonce store should be implemented for any storage backend.
|
||||
type Noncestore interface {
|
||||
Peek(context.Context, string) (uint64, error)
|
||||
Acquire(context.Context, string) (uint64, error)
|
||||
Return(context.Context, string) error
|
||||
SyncNetworkNonce(context.Context, string) (uint64, error)
|
||||
SetNewAccountNonce(context.Context, string) error
|
||||
SetAccountNonce(context.Context, string, uint64) error
|
||||
}
|
||||
|
@ -2,37 +2,54 @@ package nonce
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
celo "github.com/grassrootseconomics/cic-celo-sdk"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
redispool "github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
type (
|
||||
Opts struct {
|
||||
ChainProvider *celoutils.Provider
|
||||
RedisPool *redispool.RedisPool
|
||||
ChainProvider *celo.Provider
|
||||
}
|
||||
Store store.Store
|
||||
}
|
||||
|
||||
// RedisNoncestore implements `Noncestore`
|
||||
type RedisNoncestore struct {
|
||||
chainProvider *celo.Provider
|
||||
// RedisNoncestore implements `Noncestore`
|
||||
RedisNoncestore struct {
|
||||
chainProvider *celoutils.Provider
|
||||
redis *redispool.RedisPool
|
||||
}
|
||||
store store.Store
|
||||
}
|
||||
)
|
||||
|
||||
func NewRedisNoncestore(o Opts) Noncestore {
|
||||
return &RedisNoncestore{
|
||||
redis: o.RedisPool,
|
||||
chainProvider: o.ChainProvider,
|
||||
redis: o.RedisPool,
|
||||
store: o.Store,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *RedisNoncestore) Peek(ctx context.Context, publicKey string) (uint64, error) {
|
||||
nonce, err := n.redis.Client.Get(ctx, publicKey).Uint64()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
nonce, err = n.bootstrap(ctx, publicKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nonce, nil
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return nonce, nil
|
||||
}
|
||||
|
||||
@ -43,7 +60,14 @@ func (n *RedisNoncestore) Acquire(ctx context.Context, publicKey string) (uint64
|
||||
|
||||
nonce, err := n.redis.Client.Get(ctx, publicKey).Uint64()
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
if err == redis.Nil {
|
||||
nonce, err = n.bootstrap(ctx, publicKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
err = n.redis.Client.Incr(ctx, publicKey).Err()
|
||||
@ -70,32 +94,37 @@ func (n *RedisNoncestore) Return(ctx context.Context, publicKey string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *RedisNoncestore) SyncNetworkNonce(ctx context.Context, publicKey string) (uint64, error) {
|
||||
var (
|
||||
networkNonce uint64
|
||||
)
|
||||
|
||||
err := n.chainProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.Nonce(w3.A(publicKey), nil).Returns(&networkNonce),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = n.redis.Client.Set(ctx, publicKey, networkNonce, 0).Err()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return networkNonce, nil
|
||||
}
|
||||
|
||||
func (n *RedisNoncestore) SetNewAccountNonce(ctx context.Context, publicKey string) error {
|
||||
err := n.redis.Client.Set(ctx, publicKey, 0, 0).Err()
|
||||
if err != nil {
|
||||
func (n *RedisNoncestore) SetAccountNonce(ctx context.Context, publicKey string, nonce uint64) error {
|
||||
if err := n.redis.Client.Set(ctx, publicKey, nonce, 0).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bootstrap can be used to restore a destroyed redis nonce cache automatically.
|
||||
// It first uses the otx_sign table as a source of nonce values.
|
||||
// If the otx_sign table is corrupted, it can fallback to the network nonce.
|
||||
// Ideally, the redis nonce cache should never be lost.
|
||||
func (n *RedisNoncestore) bootstrap(ctx context.Context, publicKey string) (uint64, error) {
|
||||
lastDbNonce, err := n.store.GetNextNonce(ctx, publicKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
err := n.chainProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.Nonce(celoutils.HexToAddress(publicKey), nil).Returns(&lastDbNonce),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := n.SetAccountNonce(ctx, publicKey, lastDbNonce); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return lastDbNonce, nil
|
||||
}
|
||||
|
73
internal/store/account.go
Normal file
73
internal/store/account.go
Normal file
@ -0,0 +1,73 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func (s *PgStore) ActivateAccount(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.ActivateAccount,
|
||||
publicAddress,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PgStore) GetAccountStatus(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) (bool, bool, error) {
|
||||
var (
|
||||
accountActive bool
|
||||
gasLock bool
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(
|
||||
ctx,
|
||||
s.queries.GetAccountStatus,
|
||||
publicAddress,
|
||||
).Scan(
|
||||
&accountActive,
|
||||
&gasLock,
|
||||
); err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
return accountActive, gasLock, nil
|
||||
}
|
||||
|
||||
func (s *PgStore) GasLock(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.GasLock,
|
||||
publicAddress,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PgStore) GasUnlock(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.GasUnlock,
|
||||
publicAddress,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
53
internal/store/keystore.go
Normal file
53
internal/store/keystore.go
Normal file
@ -0,0 +1,53 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
)
|
||||
|
||||
func (s *PgStore) WriteKeyPair(
|
||||
ctx context.Context,
|
||||
keypair keypair.Key,
|
||||
) (uint, error) {
|
||||
var (
|
||||
id uint
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(
|
||||
ctx,
|
||||
s.queries.WriteKeyPair,
|
||||
keypair.Public,
|
||||
keypair.Private,
|
||||
).Scan(&id); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *PgStore) LoadPrivateKey(
|
||||
ctx context.Context,
|
||||
publicKey string,
|
||||
) (*ecdsa.PrivateKey, error) {
|
||||
var (
|
||||
privateKeyString string
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(
|
||||
ctx,
|
||||
s.queries.LoadKeyPair,
|
||||
publicKey,
|
||||
).Scan(&privateKeyString); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKey, err := eth_crypto.HexToECDSA(privateKeyString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
154
internal/store/otx.go
Normal file
154
internal/store/otx.go
Normal file
@ -0,0 +1,154 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
)
|
||||
|
||||
type (
|
||||
Otx struct {
|
||||
TrackingId string
|
||||
Type enum.OtxType
|
||||
RawTx string
|
||||
TxHash string
|
||||
From string
|
||||
Data string
|
||||
GasLimit uint64
|
||||
TransferValue uint64
|
||||
GasPrice *big.Int
|
||||
Nonce uint64
|
||||
}
|
||||
TxStatus struct {
|
||||
CreatedAt time.Time `db:"created_at" json:"createdAt"`
|
||||
Status string `db:"status" json:"status"`
|
||||
TransferValue uint64 `db:"transfer_value" json:"transferValue"`
|
||||
TxHash string `db:"tx_hash" json:"txHash"`
|
||||
Type string `db:"type" json:"txType"`
|
||||
}
|
||||
)
|
||||
|
||||
func (s *PgStore) CreateOtx(
|
||||
ctx context.Context,
|
||||
otx Otx,
|
||||
) (uint, error) {
|
||||
var (
|
||||
id uint
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(
|
||||
ctx,
|
||||
s.queries.CreateOTX,
|
||||
otx.TrackingId,
|
||||
otx.Type,
|
||||
otx.RawTx,
|
||||
otx.TxHash,
|
||||
otx.From,
|
||||
otx.Data,
|
||||
otx.GasPrice,
|
||||
otx.GasLimit,
|
||||
otx.TransferValue,
|
||||
otx.Nonce,
|
||||
).Scan(
|
||||
&id,
|
||||
); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *PgStore) GetNextNonce(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) (uint64, error) {
|
||||
var (
|
||||
lastNonce uint64
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(
|
||||
ctx,
|
||||
s.queries.GetNextNonce,
|
||||
publicAddress,
|
||||
).Scan(
|
||||
&lastNonce,
|
||||
); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return lastNonce, nil
|
||||
}
|
||||
|
||||
func (s *PgStore) GetTxStatus(
|
||||
ctx context.Context,
|
||||
trackingId string,
|
||||
) (TxStatus, error) {
|
||||
var (
|
||||
tx TxStatus
|
||||
)
|
||||
|
||||
rows, err := s.db.Query(
|
||||
ctx,
|
||||
s.queries.GetTxStatusByTrackingId,
|
||||
trackingId,
|
||||
)
|
||||
if err != nil {
|
||||
return tx, err
|
||||
}
|
||||
|
||||
if err := pgxscan.ScanOne(
|
||||
&tx,
|
||||
rows,
|
||||
); err != nil {
|
||||
return tx, err
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func (s *PgStore) CreateDispatchStatus(
|
||||
ctx context.Context,
|
||||
otxId uint,
|
||||
otxStatus enum.OtxStatus,
|
||||
) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.CreateDispatchStatus,
|
||||
otxId,
|
||||
otxStatus,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PgStore) UpdateDispatchStatus(
|
||||
ctx context.Context,
|
||||
txSuccess bool,
|
||||
txHash string,
|
||||
txBlock uint64,
|
||||
) error {
|
||||
var (
|
||||
status = enum.SUCCESS
|
||||
)
|
||||
|
||||
if !txSuccess {
|
||||
status = enum.REVERTED
|
||||
}
|
||||
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.UpdateDispatchStatus,
|
||||
txHash,
|
||||
status,
|
||||
txBlock,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
130
internal/store/store.go
Normal file
130
internal/store/store.go
Normal file
@ -0,0 +1,130 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/util"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/jackc/tern/v2/migrate"
|
||||
"github.com/knadh/goyesql/v2"
|
||||
)
|
||||
|
||||
type (
|
||||
Store interface {
|
||||
// Keypair related actions.
|
||||
LoadPrivateKey(context.Context, string) (*ecdsa.PrivateKey, error)
|
||||
WriteKeyPair(context.Context, keypair.Key) (uint, error)
|
||||
// Otx related actions.
|
||||
CreateOtx(context.Context, Otx) (uint, error)
|
||||
GetNextNonce(context.Context, string) (uint64, error)
|
||||
GetTxStatus(context.Context, string) (TxStatus, error)
|
||||
CreateDispatchStatus(context.Context, uint, enum.OtxStatus) error
|
||||
UpdateDispatchStatus(context.Context, bool, string, uint64) error
|
||||
// Account related actions.
|
||||
ActivateAccount(context.Context, string) error
|
||||
GetAccountStatus(context.Context, string) (bool, bool, error)
|
||||
// Gas quota related actions.
|
||||
GasLock(context.Context, string) error
|
||||
GasUnlock(context.Context, string) error
|
||||
}
|
||||
|
||||
Opts struct {
|
||||
DSN string
|
||||
MigrationsFolderPath string
|
||||
QueriesFolderPath string
|
||||
}
|
||||
|
||||
PgStore struct {
|
||||
db *pgxpool.Pool
|
||||
queries *queries
|
||||
}
|
||||
|
||||
queries struct {
|
||||
// Keystore related queries.
|
||||
WriteKeyPair string `query:"write-key-pair"`
|
||||
LoadKeyPair string `query:"load-key-pair"`
|
||||
// Otx related queries.
|
||||
CreateOTX string `query:"create-otx"`
|
||||
GetNextNonce string `query:"get-next-nonce"`
|
||||
GetTxStatusByTrackingId string `query:"get-tx-status-by-tracking-id"`
|
||||
CreateDispatchStatus string `query:"create-dispatch-status"`
|
||||
UpdateDispatchStatus string `query:"update-dispatch-status"`
|
||||
// Account related queries.
|
||||
ActivateAccount string `query:"activate-account"`
|
||||
GetAccountStatus string `query:"get-account-status-by-address"`
|
||||
GasLock string `query:"acc-gas-lock"`
|
||||
GasUnlock string `query:"acc-gas-unlock"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewPgStore(o Opts) (Store, error) {
|
||||
parsedConfig, err := pgxpool.ParseConfig(o.DSN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbPool, err := pgxpool.NewWithConfig(context.Background(), parsedConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queries, err := loadQueries(o.QueriesFolderPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := runMigrations(context.Background(), dbPool, o.MigrationsFolderPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PgStore{
|
||||
db: dbPool,
|
||||
queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func loadQueries(queriesPath string) (*queries, error) {
|
||||
parsedQueries, err := goyesql.ParseFile(queriesPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loadedQueries := &queries{}
|
||||
|
||||
if err := goyesql.ScanToStruct(loadedQueries, parsedQueries, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan queries %v", err)
|
||||
}
|
||||
|
||||
return loadedQueries, nil
|
||||
}
|
||||
|
||||
func runMigrations(ctx context.Context, dbPool *pgxpool.Pool, migrationsPath string) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, util.SLATimeout)
|
||||
defer cancel()
|
||||
|
||||
conn, err := dbPool.Acquire(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Release()
|
||||
|
||||
migrator, err := migrate.NewMigrator(ctx, conn.Conn(), "schema_version")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrator.LoadMigrations(os.DirFS(migrationsPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrator.Migrate(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
59
internal/sub/handler.go
Normal file
59
internal/sub/handler.go
Normal file
@ -0,0 +1,59 @@
|
||||
package sub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type (
|
||||
ChainEvent struct {
|
||||
Block uint64 `json:"block"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
ContractAddress string `json:"contractAddress"`
|
||||
Success bool `json:"success"`
|
||||
TxHash string `json:"transactionHash"`
|
||||
TxIndex uint `json:"transactionIndex"`
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
)
|
||||
|
||||
func (s *Sub) processEventHandler(ctx context.Context, msg *nats.Msg) error {
|
||||
var (
|
||||
chainEvent ChainEvent
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(msg.Data, &chainEvent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.cu.Store.UpdateDispatchStatus(
|
||||
ctx,
|
||||
chainEvent.Success,
|
||||
chainEvent.TxHash,
|
||||
chainEvent.Block,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if chainEvent.Success {
|
||||
switch msg.Subject {
|
||||
case "CHAIN.register":
|
||||
if err := s.cu.Store.ActivateAccount(ctx, chainEvent.To); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.cu.Store.GasUnlock(ctx, chainEvent.To); err != nil {
|
||||
return err
|
||||
}
|
||||
case "CHAIN.gas":
|
||||
if err := s.cu.Store.GasUnlock(ctx, chainEvent.To); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
95
internal/sub/jetstream.go
Normal file
95
internal/sub/jetstream.go
Normal file
@ -0,0 +1,95 @@
|
||||
package sub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/util"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
const (
|
||||
durableId = "cic-custodial"
|
||||
pullStream = "CHAIN"
|
||||
pullSubject = "CHAIN.*"
|
||||
)
|
||||
|
||||
type (
|
||||
SubOpts struct {
|
||||
CustodialContainer *custodial.Custodial
|
||||
JsCtx nats.JetStreamContext
|
||||
Logg logf.Logger
|
||||
NatsConn *nats.Conn
|
||||
}
|
||||
|
||||
Sub struct {
|
||||
cu *custodial.Custodial
|
||||
jsCtx nats.JetStreamContext
|
||||
logg logf.Logger
|
||||
natsConn *nats.Conn
|
||||
}
|
||||
)
|
||||
|
||||
func NewSub(o SubOpts) (*Sub, error) {
|
||||
_, err := o.JsCtx.AddConsumer(pullStream, &nats.ConsumerConfig{
|
||||
Durable: durableId,
|
||||
AckPolicy: nats.AckExplicitPolicy,
|
||||
FilterSubject: pullSubject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Sub{
|
||||
cu: o.CustodialContainer,
|
||||
jsCtx: o.JsCtx,
|
||||
logg: o.Logg,
|
||||
natsConn: o.NatsConn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Sub) Process() error {
|
||||
subOpts := []nats.SubOpt{
|
||||
nats.ManualAck(),
|
||||
nats.Bind(pullStream, durableId),
|
||||
}
|
||||
|
||||
natsSub, err := s.jsCtx.PullSubscribe(pullSubject, durableId, subOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
events, err := natsSub.Fetch(1)
|
||||
if err != nil {
|
||||
if errors.Is(err, nats.ErrTimeout) {
|
||||
continue
|
||||
} else if errors.Is(err, nats.ErrConnectionClosed) {
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(events) > 0 {
|
||||
msg := events[0]
|
||||
ctx, cancel := context.WithTimeout(context.Background(), util.SLATimeout)
|
||||
|
||||
if err := s.processEventHandler(ctx, msg); err != nil {
|
||||
s.logg.Error("sub: handler error", "error", err)
|
||||
msg.Nak()
|
||||
} else {
|
||||
msg.Ack()
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sub) Close() {
|
||||
if s.natsConn != nil {
|
||||
s.natsConn.Close()
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package tasker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -8,14 +9,17 @@ import (
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
const (
|
||||
taskTimeout = 60 * time.Second
|
||||
taskRetention = 48 * time.Hour
|
||||
)
|
||||
|
||||
type TaskerClientOpts struct {
|
||||
RedisPool *redis.RedisPool
|
||||
TaskRetention time.Duration
|
||||
}
|
||||
|
||||
type TaskerClient struct {
|
||||
Client *asynq.Client
|
||||
taskRetention time.Duration
|
||||
}
|
||||
|
||||
func NewTaskerClient(o TaskerClientOpts) *TaskerClient {
|
||||
@ -24,20 +28,26 @@ func NewTaskerClient(o TaskerClientOpts) *TaskerClient {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TaskerClient) CreateTask(taskName TaskName, queueName QueueName, task *Task) (*asynq.TaskInfo, error) {
|
||||
func (c *TaskerClient) CreateTask(ctx context.Context, taskName TaskName, queueName QueueName, task *Task, extraOpts ...asynq.Option) (*asynq.TaskInfo, error) {
|
||||
if task.Id == "" {
|
||||
task.Id = uuid.NewString()
|
||||
}
|
||||
|
||||
defaultOpts := []asynq.Option{
|
||||
asynq.Queue(string(queueName)),
|
||||
asynq.TaskID(task.Id),
|
||||
asynq.Retention(taskRetention),
|
||||
asynq.Timeout(taskTimeout),
|
||||
}
|
||||
defaultOpts = append(defaultOpts, extraOpts...)
|
||||
|
||||
qTask := asynq.NewTask(
|
||||
string(taskName),
|
||||
task.Payload,
|
||||
asynq.Queue(string(queueName)),
|
||||
asynq.TaskID(task.Id),
|
||||
asynq.Retention(c.taskRetention),
|
||||
defaultOpts...,
|
||||
)
|
||||
|
||||
taskInfo, err := c.Client.Enqueue(qTask)
|
||||
taskInfo, err := c.Client.EnqueueContext(ctx, qTask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/logg"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/hibiken/asynq"
|
||||
@ -12,17 +11,17 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
fixedRetryCount = 25
|
||||
fixedRetryPeriod = time.Second * 1
|
||||
retryRequeueInterval = 1 * time.Second
|
||||
)
|
||||
|
||||
type TaskerServerOpts struct {
|
||||
Concurrency int
|
||||
ErrorHandler asynq.ErrorHandler
|
||||
IsFailureHandler func(error) bool
|
||||
Logg logf.Logger
|
||||
LogLevel asynq.LogLevel
|
||||
RedisPool *redis.RedisPool
|
||||
SystemContainer *SystemContainer
|
||||
TaskerClient *TaskerClient
|
||||
RetryHandler asynq.RetryDelayFunc
|
||||
}
|
||||
|
||||
type TaskerServer struct {
|
||||
@ -35,14 +34,16 @@ func NewTaskerServer(o TaskerServerOpts) *TaskerServer {
|
||||
o.RedisPool,
|
||||
asynq.Config{
|
||||
Concurrency: o.Concurrency,
|
||||
IsFailure: expectedFailures,
|
||||
Logger: logg.NewAsynqLogg(o.Logg),
|
||||
DelayedTaskCheckInterval: retryRequeueInterval,
|
||||
ErrorHandler: o.ErrorHandler,
|
||||
IsFailure: o.IsFailureHandler,
|
||||
LogLevel: o.LogLevel,
|
||||
Logger: logg.AsynqCompatibleLogger(o.Logg),
|
||||
Queues: map[string]int{
|
||||
string(HighPriority): 5,
|
||||
string(DefaultPriority): 2,
|
||||
string(HighPriority): 7,
|
||||
string(DefaultPriority): 3,
|
||||
},
|
||||
RetryDelayFunc: retryDelay,
|
||||
RetryDelayFunc: o.RetryHandler,
|
||||
},
|
||||
)
|
||||
|
||||
@ -54,6 +55,10 @@ func NewTaskerServer(o TaskerServerOpts) *TaskerServer {
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *TaskerServer) RegisterMiddlewareStack(middlewareStack []asynq.MiddlewareFunc) {
|
||||
ts.mux.Use(middlewareStack...)
|
||||
}
|
||||
|
||||
func (ts *TaskerServer) RegisterHandlers(taskName TaskName, taskHandler func(context.Context, *asynq.Task) error) {
|
||||
ts.mux.HandleFunc(string(taskName), taskHandler)
|
||||
}
|
||||
@ -70,20 +75,3 @@ func (ts *TaskerServer) Stop() {
|
||||
ts.server.Stop()
|
||||
ts.server.Shutdown()
|
||||
}
|
||||
|
||||
func expectedFailures(err error) bool {
|
||||
switch err {
|
||||
case redislock.ErrNotObtained:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func retryDelay(count int, err error, task *asynq.Task) time.Duration {
|
||||
if count < fixedRetryCount {
|
||||
return fixedRetryPeriod
|
||||
} else {
|
||||
return asynq.DefaultRetryDelayFunc(count, err, task)
|
||||
}
|
||||
}
|
||||
|
169
internal/tasker/task/account_refill_gas.go
Normal file
169
internal/tasker/task/account_refill_gas.go
Normal file
@ -0,0 +1,169 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common/hexutil"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
func AccountRefillGasProcessor(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
err error
|
||||
payload AccountPayload
|
||||
|
||||
nextTime big.Int
|
||||
checkStatus bool
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.CallFunc(
|
||||
cu.Abis[custodial.NextTime],
|
||||
cu.RegistryMap[celoutils.GasFaucet],
|
||||
celoutils.HexToAddress(payload.PublicKey),
|
||||
).Returns(&nextTime),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The user recently requested funds, there is a cooldown applied.
|
||||
// We can schedule an attempt after the cooldown period has passed + 10 seconds.
|
||||
if nextTime.Int64() > time.Now().Unix() {
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountRefillGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
asynq.ProcessAt(time.Unix(nextTime.Int64()+10, 0)),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.CallFunc(
|
||||
cu.Abis[custodial.Check],
|
||||
cu.RegistryMap[celoutils.GasFaucet],
|
||||
celoutils.HexToAddress(payload.PublicKey),
|
||||
).Returns(&checkStatus),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The gas faucet backend returns a false status, a poke will fail.
|
||||
if !checkStatus {
|
||||
return nil
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+cu.SystemPublicKey,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemPublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemPublicKey); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.Abis[custodial.GiveTo].EncodeArgs(
|
||||
celoutils.HexToAddress(payload.PublicKey),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
cu.SystemPrivateKey,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: cu.RegistryMap[celoutils.GasFaucet],
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: uint64(celoutils.SafeGasLimit),
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx, err := builtTx.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.Store.CreateOtx(ctx, store.Otx{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.REFILL_GAS,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemPublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
OtxId: id,
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.DispatchTxTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
126
internal/tasker/task/account_register.go
Normal file
126
internal/tasker/task/account_register.go
Normal file
@ -0,0 +1,126 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common/hexutil"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type AccountPayload struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
}
|
||||
|
||||
func AccountRegisterOnChainProcessor(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
err error
|
||||
payload AccountPayload
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+cu.SystemPublicKey,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemPublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemPublicKey); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.Abis[custodial.Register].EncodeArgs(
|
||||
celoutils.HexToAddress(payload.PublicKey),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
cu.SystemPrivateKey,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: cu.RegistryMap[celoutils.CustodialProxy],
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: uint64(celoutils.SafeGasLimit),
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx, err := builtTx.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.Store.CreateOtx(ctx, store.Otx{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.ACCOUNT_REGISTER,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemPublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
OtxId: id,
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.DispatchTxTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cu.Noncestore.SetAccountNonce(ctx, payload.PublicKey, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/celo-org/celo-blockchain/common"
|
||||
"github.com/celo-org/celo-blockchain/core/types"
|
||||
celo "github.com/grassrootseconomics/cic-celo-sdk"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type TxPayload struct {
|
||||
Tx *types.Transaction `json:"tx"`
|
||||
}
|
||||
|
||||
func TxDispatch(
|
||||
celoProvider *celo.Provider,
|
||||
) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
p TxPayload
|
||||
txHash common.Hash
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := celoProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.SendTx(p.Tx).Returns(&txHash),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
62
internal/tasker/task/dispatch_tx.go
Normal file
62
internal/tasker/task/dispatch_tx.go
Normal file
@ -0,0 +1,62 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/celo-org/celo-blockchain/common"
|
||||
"github.com/celo-org/celo-blockchain/core/types"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type TxPayload struct {
|
||||
OtxId uint `json:"otxId"`
|
||||
Tx *types.Transaction `json:"tx"`
|
||||
}
|
||||
|
||||
func DispatchTx(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
payload TxPayload
|
||||
dispathchTx common.Hash
|
||||
dispatchStatus enum.OtxStatus = enum.IN_NETWORK
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.SendTx(payload.Tx).Returns(&dispathchTx),
|
||||
); err != nil {
|
||||
switch err.Error() {
|
||||
case celoutils.ErrGasPriceLow:
|
||||
dispatchStatus = enum.FAIL_LOW_GAS_PRICE
|
||||
case celoutils.ErrInsufficientGas:
|
||||
dispatchStatus = enum.FAIL_NO_GAS
|
||||
case celoutils.ErrNonceLow:
|
||||
dispatchStatus = enum.FAIL_LOW_NONCE
|
||||
default:
|
||||
dispatchStatus = enum.FAIL_UNKNOWN_RPC_ERROR
|
||||
}
|
||||
|
||||
if err := cu.Store.CreateDispatchStatus(ctx, payload.OtxId, dispatchStatus); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := cu.Store.CreateDispatchStatus(ctx, payload.OtxId, dispatchStatus); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
168
internal/tasker/task/sign_transfer.go
Normal file
168
internal/tasker/task/sign_transfer.go
Normal file
@ -0,0 +1,168 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common/hexutil"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type TransferPayload struct {
|
||||
TrackingId string `json:"trackingId"`
|
||||
From string `json:"from" `
|
||||
To string `json:"to"`
|
||||
VoucherAddress string `json:"voucherAddress"`
|
||||
Amount uint64 `json:"amount"`
|
||||
}
|
||||
|
||||
func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
err error
|
||||
networkBalance big.Int
|
||||
payload TransferPayload
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+payload.From,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
key, err := cu.Store.LoadPrivateKey(ctx, payload.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, payload.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, payload.From); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.Abis[custodial.Transfer].EncodeArgs(
|
||||
celoutils.HexToAddress(payload.To),
|
||||
new(big.Int).SetUint64(payload.Amount),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
key,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: celoutils.HexToAddress(payload.VoucherAddress),
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: uint64(celoutils.SafeGasLimit),
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx, err := builtTx.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.Store.CreateOtx(ctx, store.Otx{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.TRANSFER_VOUCHER,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: payload.From,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: payload.Amount,
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.Balance(celoutils.HexToAddress(payload.From), nil).Returns(&networkBalance),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
OtxId: id,
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.DispatchTxTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gasRefillPayload, err := json.Marshal(AccountPayload{
|
||||
PublicKey: payload.From,
|
||||
TrackingId: payload.TrackingId,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !balanceCheck(networkBalance) {
|
||||
if err := cu.Store.GasLock(ctx, payload.From); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountRefillGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: gasRefillPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
196
internal/tasker/task/sign_transfer_auth.go
Normal file
196
internal/tasker/task/sign_transfer_auth.go
Normal file
@ -0,0 +1,196 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common/hexutil"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type TransferAuthPayload struct {
|
||||
Amount uint64 `json:"amount"`
|
||||
Authorizer string `json:"authorizer"`
|
||||
AuthorizedAddress string `json:"authorizedAddress"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
VoucherAddress string `json:"voucherAddress"`
|
||||
}
|
||||
|
||||
func SignTransferAuthorizationProcessor(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
err error
|
||||
networkBalance big.Int
|
||||
payload TransferAuthPayload
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+payload.Authorizer,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
key, err := cu.Store.LoadPrivateKey(ctx, payload.Authorizer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, payload.Authorizer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, payload.Authorizer); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.Abis[custodial.Approve].EncodeArgs(
|
||||
celoutils.HexToAddress(payload.AuthorizedAddress),
|
||||
new(big.Int).SetUint64(payload.Amount),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
key,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: celoutils.HexToAddress(payload.VoucherAddress),
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: uint64(celoutils.SafeGasLimit),
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx, err := builtTx.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.Store.CreateOtx(ctx, store.Otx{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.TRANSFER_AUTH,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemPublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: 0,
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.Balance(celoutils.HexToAddress(payload.Authorizer), nil).Returns(&networkBalance),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
OtxId: id,
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.DispatchTxTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Auto-revoke every session (15 min)
|
||||
// Check if already a revoke request
|
||||
if payload.Amount > 0 {
|
||||
taskPayload, err := json.Marshal(TransferAuthPayload{
|
||||
TrackingId: payload.TrackingId,
|
||||
Amount: 0,
|
||||
Authorizer: payload.Authorizer,
|
||||
AuthorizedAddress: payload.AuthorizedAddress,
|
||||
VoucherAddress: payload.VoucherAddress,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.SignTransferTaskAuth,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: taskPayload,
|
||||
},
|
||||
asynq.ProcessIn(cu.ApprovalTimeout),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
gasRefillPayload, err := json.Marshal(AccountPayload{
|
||||
PublicKey: payload.Authorizer,
|
||||
TrackingId: payload.TrackingId,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !balanceCheck(networkBalance) {
|
||||
if err := cu.Store.GasLock(ctx, payload.Authorizer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountRefillGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: gasRefillPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,273 +0,0 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
celo "github.com/grassrootseconomics/cic-celo-sdk"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type SystemPayload struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
}
|
||||
|
||||
func PrepareAccount(
|
||||
nonceProvider nonce.Noncestore,
|
||||
taskerClient *tasker.TaskerClient,
|
||||
) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var p SystemPayload
|
||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := nonceProvider.SetNewAccountNonce(ctx, p.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := taskerClient.CreateTask(
|
||||
tasker.GiftGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = taskerClient.CreateTask(
|
||||
tasker.GiftTokenTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GiftGasProcessor(
|
||||
celoProvider *celo.Provider,
|
||||
nonceProvider nonce.Noncestore,
|
||||
lockProvider *redislock.Client,
|
||||
system *tasker.SystemContainer,
|
||||
taskerClient *tasker.TaskerClient,
|
||||
) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var p SystemPayload
|
||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
lock, err := lockProvider.Obtain(ctx, system.LockPrefix+system.PublicKey, system.LockTimeout, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := nonceProvider.Acquire(ctx, system.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := celoProvider.SignGasTransferTx(
|
||||
system.PrivateKey,
|
||||
celo.GasTransferTxOpts{
|
||||
To: w3.A(p.PublicKey),
|
||||
Nonce: nonce,
|
||||
Value: system.GiftableGasValue,
|
||||
GasPrice: celo.FixedMinGas,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err := nonceProvider.Return(ctx, p.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("nonce.Return failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
_, err = taskerClient.CreateTask(
|
||||
tasker.TxDispatchTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GiftTokenProcessor(
|
||||
celoProvider *celo.Provider,
|
||||
nonceProvider nonce.Noncestore,
|
||||
lockProvider *redislock.Client,
|
||||
system *tasker.SystemContainer,
|
||||
taskerClient *tasker.TaskerClient,
|
||||
) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var p SystemPayload
|
||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
publicKey := w3.A(p.PublicKey)
|
||||
|
||||
lock, err := lockProvider.Obtain(ctx, system.LockPrefix+system.PublicKey, system.LockTimeout, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := nonceProvider.Acquire(ctx, system.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
abi, err := w3.NewFunc("mint(address,uint256)", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
input, err := abi.EncodeArgs(publicKey, system.GiftableTokenValue)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ABI encode failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
builtTx, err := celoProvider.SignContractExecutionTx(
|
||||
system.PrivateKey,
|
||||
celo.ContractExecutionTxOpts{
|
||||
ContractAddress: system.GiftableToken,
|
||||
InputData: input,
|
||||
GasPrice: celo.FixedMinGas,
|
||||
GasLimit: system.TokenTransferGasLimit,
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err := nonceProvider.Return(ctx, p.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("nonce.Return failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
_, err = taskerClient.CreateTask(
|
||||
tasker.TxDispatchTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func RefillGasProcessor(
|
||||
celoProvider *celo.Provider,
|
||||
nonceProvider nonce.Noncestore,
|
||||
lockProvider *redislock.Client,
|
||||
system *tasker.SystemContainer,
|
||||
taskerClient *tasker.TaskerClient,
|
||||
) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
p SystemPayload
|
||||
balance big.Int
|
||||
)
|
||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := celoProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.Balance(w3.A(p.PublicKey), nil).Returns(&balance),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if belowThreshold := balance.Cmp(system.GasRefillThreshold); belowThreshold > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lock, err := lockProvider.Obtain(ctx, system.LockPrefix+system.PublicKey, system.LockTimeout, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := nonceProvider.Acquire(ctx, system.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := celoProvider.SignGasTransferTx(
|
||||
system.PrivateKey,
|
||||
celo.GasTransferTxOpts{
|
||||
To: w3.A(p.PublicKey),
|
||||
Nonce: nonce,
|
||||
Value: system.GasRefillValue,
|
||||
GasPrice: celo.FixedMinGas,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err := nonceProvider.Return(ctx, p.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("nonce.Return failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
_, err = taskerClient.CreateTask(
|
||||
tasker.TxDispatchTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
celo "github.com/grassrootseconomics/cic-celo-sdk"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/keystore"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type TransferPayload struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
VoucherAddress string `json:"voucherAddress"`
|
||||
Amount string `json:"amount"`
|
||||
}
|
||||
|
||||
func TransferToken(
|
||||
celoProvider *celo.Provider,
|
||||
nonceProvider nonce.Noncestore,
|
||||
keystoreProvider keystore.Keystore,
|
||||
lockProvider *redislock.Client,
|
||||
system *tasker.SystemContainer,
|
||||
taskerClient *tasker.TaskerClient,
|
||||
) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var p TransferPayload
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &p); err != nil {
|
||||
return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
lock, err := lockProvider.Obtain(ctx, system.LockPrefix+p.From, system.LockTimeout, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := nonceProvider.Acquire(ctx, p.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := keystoreProvider.LoadPrivateKey(ctx, p.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
abi, err := w3.NewFunc("transfer(address,uint256)", "bool")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
input, err := abi.EncodeArgs(w3.A(p.To), parseTransferValue(p.Amount, system.TokenDecimals))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ABI encode failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
builtTx, err := celoProvider.SignContractExecutionTx(
|
||||
key,
|
||||
celo.ContractExecutionTxOpts{
|
||||
ContractAddress: system.GiftableToken,
|
||||
InputData: input,
|
||||
GasPrice: celo.FixedMinGas,
|
||||
GasLimit: system.TokenTransferGasLimit,
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err := nonceProvider.Return(ctx, p.From); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("nonce.Return failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
disptachJobPayload, err := json.Marshal(TxPayload{
|
||||
Tx: builtTx,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
_, err = taskerClient.CreateTask(
|
||||
tasker.TxDispatchTask,
|
||||
tasker.HighPriority,
|
||||
&tasker.Task{
|
||||
Payload: disptachJobPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gasRefillPayload, err := json.Marshal(SystemPayload{
|
||||
PublicKey: p.From,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal failed: %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
_, err = taskerClient.CreateTask(
|
||||
tasker.RefillGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: gasRefillPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func parseTransferValue(value string, tokenDecimals int) *big.Int {
|
||||
floatValue, _ := strconv.ParseFloat(value, 64)
|
||||
|
||||
return big.NewInt(int64(floatValue * math.Pow10(tokenDecimals)))
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_parseTransferValue(t *testing.T) {
|
||||
type args struct {
|
||||
value string
|
||||
tokenDecimals int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *big.Int
|
||||
}{
|
||||
{
|
||||
name: "zero value string",
|
||||
args: args{
|
||||
value: "0",
|
||||
tokenDecimals: 6,
|
||||
},
|
||||
want: big.NewInt(0),
|
||||
},
|
||||
{
|
||||
name: "fixed value string",
|
||||
args: args{
|
||||
value: "2",
|
||||
tokenDecimals: 6,
|
||||
},
|
||||
want: big.NewInt(2000000),
|
||||
},
|
||||
{
|
||||
name: "float (2 d.p) value string",
|
||||
args: args{
|
||||
value: "2.19",
|
||||
tokenDecimals: 6,
|
||||
},
|
||||
want: big.NewInt(2190000),
|
||||
},
|
||||
{
|
||||
name: "float (6 d.p) value string",
|
||||
args: args{
|
||||
value: "2.123456",
|
||||
tokenDecimals: 6,
|
||||
},
|
||||
want: big.NewInt(2123456),
|
||||
},
|
||||
{
|
||||
name: "float (10 d.p) value string",
|
||||
args: args{
|
||||
value: "2.1234567891",
|
||||
tokenDecimals: 6,
|
||||
},
|
||||
want: big.NewInt(2123456),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := parseTransferValue(tt.args.value, tt.args.tokenDecimals)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
39
internal/tasker/task/utils.go
Normal file
39
internal/tasker/task/utils.go
Normal file
@ -0,0 +1,39 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
)
|
||||
|
||||
const (
|
||||
lockPrefix = "lock:"
|
||||
lockRetryDelay = 25 * time.Millisecond
|
||||
lockTimeout = 1 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
// 20 gwei = max gas price we are willing to pay
|
||||
// 250k = max gas limit
|
||||
// minGasBalanceRequired is optimistic that the immidiate next transfer request will be successful
|
||||
// but the subsequent one could fail (though low probability), therefore we can trigger a gas lock.
|
||||
// Therefore our system wide threshold is 0.01 CELO or 10000000000000000 gas units
|
||||
// UPDATE: Feb 2025
|
||||
// 0.04455 CELO
|
||||
minGasBalanceRequired = big.NewInt(27000000000 * 550000 * 3)
|
||||
)
|
||||
|
||||
// lockRetry will at most try to obtain the lock 20 times within ~0.5s.
|
||||
// it is expected to prevent immidiate requeue of the task at the expense of more redis calls.
|
||||
func lockRetry() redislock.RetryStrategy {
|
||||
return redislock.LimitRetry(
|
||||
redislock.LinearBackoff(lockRetryDelay),
|
||||
20,
|
||||
)
|
||||
}
|
||||
|
||||
// balanceCheck compares the network balance with the system set min as threshold to execute a transfer.
|
||||
func balanceCheck(networkBalance big.Int) bool {
|
||||
return minGasBalanceRequired.Cmp(&networkBalance) < 0
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
package tasker
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/celo-org/celo-blockchain/common"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -14,34 +9,17 @@ type (
|
||||
TaskName string
|
||||
)
|
||||
|
||||
type SystemContainer struct {
|
||||
GasRefillThreshold *big.Int
|
||||
GasRefillValue *big.Int
|
||||
GiftableGasValue *big.Int
|
||||
GiftableToken common.Address
|
||||
GiftableTokenValue *big.Int
|
||||
LockPrefix string
|
||||
LockTimeout time.Duration
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
PublicKey string
|
||||
TokenDecimals int
|
||||
TokenTransferGasLimit uint64
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
Id string `json:"id"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
||||
|
||||
const (
|
||||
PrepareAccountTask TaskName = "sys:prepare_account"
|
||||
GiftGasTask TaskName = "sys:gift_gas"
|
||||
GiftTokenTask TaskName = "sys:gift_token"
|
||||
RefillGasTask TaskName = "admin:refill_gas"
|
||||
SweepGasTask TaskName = "admin:sweep_gas"
|
||||
AdminTokenApprovalTask TaskName = "admin:token_approval"
|
||||
TransferTokenTask TaskName = "usr:transfer_token"
|
||||
TxDispatchTask TaskName = "rpc:dispatch"
|
||||
AccountRegisterTask TaskName = "sys:register_account"
|
||||
AccountRefillGasTask TaskName = "sys:refill_gas"
|
||||
SignTransferTask TaskName = "usr:sign_transfer"
|
||||
SignTransferTaskAuth TaskName = "usr:sign_transfer_auth"
|
||||
DispatchTxTask TaskName = "rpc:dispatch"
|
||||
)
|
||||
|
||||
const (
|
||||
|
9
migrations/001_keystore.sql
Normal file
9
migrations/001_keystore.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- Keystore table
|
||||
CREATE TABLE IF NOT EXISTS keystore (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
public_key TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
active BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS public_key_idx ON keystore(public_key);
|
54
migrations/002_custodial_db.sql
Normal file
54
migrations/002_custodial_db.sql
Normal file
@ -0,0 +1,54 @@
|
||||
-- Otx tx type enum table
|
||||
CREATE TABLE IF NOT EXISTS otx_tx_type (
|
||||
value TEXT PRIMARY KEY
|
||||
);
|
||||
INSERT INTO otx_tx_type (value) VALUES
|
||||
('GIFT_GAS'),
|
||||
('ACCOUNT_REGISTER'),
|
||||
('GIFT_VOUCHER'),
|
||||
('REFILL_GAS'),
|
||||
('TRANSFER_VOUCHER');
|
||||
|
||||
-- Origin tx table
|
||||
CREATE TABLE IF NOT EXISTS otx_sign (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
tracking_id uuid NOT NULL,
|
||||
"type" TEXT REFERENCES otx_tx_type(value) NOT NULL,
|
||||
raw_tx TEXT NOT NULL,
|
||||
tx_hash TEXT NOT NULL,
|
||||
"from" TEXT NOT NULL,
|
||||
"data" TEXT NOT NULL,
|
||||
gas_price bigint NOT NULL,
|
||||
gas_limit bigint NOT NULL,
|
||||
transfer_value bigint NOT NULL,
|
||||
nonce int NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tracking_id_idx ON otx_sign (tracking_id);
|
||||
CREATE INDEX IF NOT EXISTS tx_hash_idx ON otx_sign (tx_hash);
|
||||
CREATE INDEX IF NOT EXISTS from_idx ON otx_sign ("from");
|
||||
|
||||
-- Otx dispatch status enum table
|
||||
-- Enforces referential integrity on the dispatch table
|
||||
CREATE TABLE IF NOT EXISTS otx_dispatch_status_type (
|
||||
value TEXT PRIMARY KEY
|
||||
);
|
||||
INSERT INTO otx_dispatch_status_type (value) VALUES
|
||||
('IN_NETWORK'),
|
||||
('OBSOLETE'),
|
||||
('SUCCESS'),
|
||||
('FAIL_NO_GAS'),
|
||||
('FAIL_LOW_NONCE'),
|
||||
('FAIL_LOW_GAS_PRICE'),
|
||||
('FAIL_UNKNOWN_RPC_ERROR'),
|
||||
('REVERTED');
|
||||
|
||||
-- Dispatch status table
|
||||
CREATE TABLE IF NOT EXISTS otx_dispatch (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
otx_id INT REFERENCES otx_sign(id),
|
||||
"status" TEXT REFERENCES otx_dispatch_status_type(value) NOT NULL,
|
||||
"block" bigint,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS status_idx ON otx_dispatch("status");
|
28
migrations/003_gas_quota.sql
Normal file
28
migrations/003_gas_quota.sql
Normal file
@ -0,0 +1,28 @@
|
||||
-- Gas quota meta table
|
||||
CREATE TABLE IF NOT EXISTS gas_quota_meta (
|
||||
default_quota INT NOT NULL
|
||||
);
|
||||
INSERT INTO gas_quota_meta (default_quota) VALUES (25);
|
||||
|
||||
-- Gas quota table
|
||||
CREATE TABLE IF NOT EXISTS gas_quota (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
key_id INT REFERENCES keystore(id) NOT NULL,
|
||||
quota INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- Gas quota trigger on keystore insert to default 0 quota
|
||||
-- We wait for the event handler to correctly set the chain quota
|
||||
create function insert_gas_quota()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into gas_quota (key_id) values (new.id);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create trigger insert_gas_quota
|
||||
after insert on keystore
|
||||
for each row
|
||||
execute procedure insert_gas_quota();
|
33
migrations/004_updated_at.sql
Normal file
33
migrations/004_updated_at.sql
Normal file
@ -0,0 +1,33 @@
|
||||
ALTER TABLE keystore
|
||||
ADD COLUMN updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
ALTER TABLE otx_dispatch
|
||||
ADD COLUMN updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
ALTER TABLE gas_quota
|
||||
ADD COLUMN updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
-- updated_at function
|
||||
create function update_timestamp()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
new.updated_at = current_timestamp;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create trigger update_keystore_timestamp
|
||||
before update on keystore
|
||||
for each row
|
||||
execute procedure update_timestamp();
|
||||
|
||||
create trigger update_otx_dispatch_timestamp
|
||||
before update on otx_dispatch
|
||||
for each row
|
||||
execute procedure update_timestamp();
|
||||
|
||||
create trigger update_gas_quota_timestamp
|
||||
before update on gas_quota
|
||||
for each row
|
||||
execute procedure update_timestamp();
|
34
migrations/005_remove_gas_quota.sql
Normal file
34
migrations/005_remove_gas_quota.sql
Normal file
@ -0,0 +1,34 @@
|
||||
-- Replace gas_quota with gas_lock which checks network balance threshold
|
||||
DROP TRIGGER IF EXISTS update_gas_quota_timestamp ON gas_quota;
|
||||
DROP TABLE IF EXISTS gas_quota_meta;
|
||||
DROP TABLE IF EXISTS gas_quota;
|
||||
DROP TRIGGER IF EXISTS insert_gas_quota ON keystore;
|
||||
DROP FUNCTION IF EXISTS insert_gas_quota;
|
||||
|
||||
-- Gas lock table
|
||||
-- A gas_locked account indicates gas balance is below threshold awaiting next available top up
|
||||
CREATE TABLE IF NOT EXISTS gas_lock (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
key_id INT REFERENCES keystore(id) NOT NULL,
|
||||
lock BOOLEAN DEFAULT true,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
create function insert_gas_lock()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
insert into gas_lock (key_id) values (new.id);
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create trigger insert_gas_lock
|
||||
after insert on keystore
|
||||
for each row
|
||||
execute procedure insert_gas_lock();
|
||||
|
||||
create trigger update_gas_lock_timestamp
|
||||
before update on gas_lock
|
||||
for each row
|
||||
execute procedure update_timestamp();
|
1
migrations/006_transfer_auth.sql
Normal file
1
migrations/006_transfer_auth.sql
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO otx_tx_type (value) VALUES ('TRANSFER_AUTHORIZATION');
|
26
pkg/enum/enum.go
Normal file
26
pkg/enum/enum.go
Normal file
@ -0,0 +1,26 @@
|
||||
package enum
|
||||
|
||||
type (
|
||||
// OtxStatus represents enum-like values received in the dispatcher from the RPC node or Network callback.
|
||||
// It includes a subset of well-known and likely failures the dispatcher may encounter.
|
||||
OtxStatus string
|
||||
// OtxType reprsents the specific type of signed transaction.
|
||||
OtxType string
|
||||
)
|
||||
|
||||
// NOTE: These values must also be inserted/updated into db to enforce referential integrity.
|
||||
const (
|
||||
IN_NETWORK OtxStatus = "IN_NETWORK"
|
||||
OBSOLETE OtxStatus = "OBSOLETE"
|
||||
SUCCESS OtxStatus = "SUCCESS"
|
||||
FAIL_NO_GAS OtxStatus = "FAIL_NO_GAS"
|
||||
FAIL_LOW_NONCE OtxStatus = "FAIL_LOW_NONCE"
|
||||
FAIL_LOW_GAS_PRICE OtxStatus = "FAIL_LOW_GAS_PRICE"
|
||||
FAIL_UNKNOWN_RPC_ERROR OtxStatus = "FAIL_UNKNOWN_RPC_ERROR"
|
||||
REVERTED OtxStatus = "REVERTED"
|
||||
|
||||
ACCOUNT_REGISTER OtxType = "ACCOUNT_REGISTER"
|
||||
REFILL_GAS OtxType = "REFILL_GAS"
|
||||
TRANSFER_AUTH OtxType = "TRANSFER_AUTHORIZATION"
|
||||
TRANSFER_VOUCHER OtxType = "TRANSFER_VOUCHER"
|
||||
)
|
@ -12,6 +12,7 @@ type Key struct {
|
||||
Private string
|
||||
}
|
||||
|
||||
// Generate creates a new keypair from internally randomized entropy.
|
||||
func Generate() (Key, error) {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
|
@ -2,32 +2,32 @@ package logg
|
||||
|
||||
import "github.com/zerodha/logf"
|
||||
|
||||
type AsynqLogg struct {
|
||||
logg *logf.Logger
|
||||
type AsynqLogger struct {
|
||||
Lo *logf.Logger
|
||||
}
|
||||
|
||||
func NewAsynqLogg(lo logf.Logger) AsynqLogg {
|
||||
return AsynqLogg{
|
||||
logg: &lo,
|
||||
func AsynqCompatibleLogger(lo logf.Logger) AsynqLogger {
|
||||
return AsynqLogger{
|
||||
Lo: &lo,
|
||||
}
|
||||
}
|
||||
|
||||
func (l AsynqLogg) Debug(args ...interface{}) {
|
||||
l.logg.Debug("asynq", "debug", args[0])
|
||||
func (l AsynqLogger) Debug(args ...interface{}) {
|
||||
l.Lo.Debug("asynq: server", "msg", args[0])
|
||||
}
|
||||
|
||||
func (l AsynqLogg) Info(args ...interface{}) {
|
||||
l.logg.Info("asynq", "info", args[0])
|
||||
func (l AsynqLogger) Info(args ...interface{}) {
|
||||
l.Lo.Info("asynq: server", "msg", args[0])
|
||||
}
|
||||
|
||||
func (l AsynqLogg) Warn(args ...interface{}) {
|
||||
l.logg.Warn("asynq", "warn", args[0])
|
||||
func (l AsynqLogger) Warn(args ...interface{}) {
|
||||
l.Lo.Warn("asynq: server", "msg", args[0])
|
||||
}
|
||||
|
||||
func (l AsynqLogg) Error(args ...interface{}) {
|
||||
l.logg.Error("asynq", "error", args[0])
|
||||
func (l AsynqLogger) Error(args ...interface{}) {
|
||||
l.Lo.Error("asynq: server", "msg", args[0])
|
||||
}
|
||||
|
||||
func (l AsynqLogg) Fatal(args ...interface{}) {
|
||||
l.logg.Fatal("asynq", "fatal", args[0])
|
||||
func (l AsynqLogger) Fatal(args ...interface{}) {
|
||||
l.Lo.Fatal("asynq: server", "msg", args[0])
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
package logg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type RedisLogg struct {
|
||||
logg *logf.Logger
|
||||
}
|
||||
|
||||
func NewRedisTraceLogg(lo logf.Logger) RedisLogg {
|
||||
return RedisLogg{
|
||||
logg: &lo,
|
||||
}
|
||||
}
|
||||
|
||||
func (l RedisLogg) Printf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.logg.Debug("redis", "debug", "format", format, "data", v)
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package logg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/tracelog"
|
||||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type PgxLogg struct {
|
||||
logg *logf.Logger
|
||||
}
|
||||
|
||||
func NewPgxTraceLogg(lo logf.Logger) PgxLogg {
|
||||
return PgxLogg{
|
||||
logg: &lo,
|
||||
}
|
||||
}
|
||||
|
||||
func (l PgxLogg) Log(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) {
|
||||
l.logg.Debug("pgx", "level", level, "msg", msg, "data", data)
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type PostgresPoolOpts struct {
|
||||
DSN string
|
||||
// Debug bool
|
||||
// Logg tracelog.Logger
|
||||
}
|
||||
|
||||
func NewPostgresPool(o PostgresPoolOpts) (*pgxpool.Pool, error) {
|
||||
parsedConfig, err := pgxpool.ParseConfig(o.DSN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if o.Debug {
|
||||
// parsedConfig.ConnConfig.Tracer = &tracelog.TraceLog{
|
||||
// Logger: o.Logg,
|
||||
// LogLevel: tracelog.LogLevelDebug,
|
||||
// }
|
||||
// }
|
||||
|
||||
dbPool, err := pgxpool.NewWithConfig(context.Background(), parsedConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := dbPool.Ping(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbPool, nil
|
||||
}
|
@ -2,23 +2,29 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/util"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type RedisPoolOpts struct {
|
||||
const (
|
||||
systemGlobalLockKey = "system:global_lock"
|
||||
)
|
||||
|
||||
type (
|
||||
RedisPoolOpts struct {
|
||||
DSN string
|
||||
// Debug bool
|
||||
// Logg logg.RedisLogg
|
||||
MinIdleConns int
|
||||
}
|
||||
}
|
||||
|
||||
type RedisPool struct {
|
||||
RedisPool struct {
|
||||
Client *redis.Client
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
func NewRedisPool(o RedisPoolOpts) (*RedisPool, error) {
|
||||
// NewRedisPool creates a reusable connection across the cic-custodial componenent.
|
||||
// Note: Each db namespace requires its own connection pool.
|
||||
func NewRedisPool(ctx context.Context, o RedisPoolOpts) (*RedisPool, error) {
|
||||
redisOpts, err := redis.ParseURL(o.DSN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -28,15 +34,10 @@ func NewRedisPool(o RedisPoolOpts) (*RedisPool, error) {
|
||||
|
||||
redisClient := redis.NewClient(redisOpts)
|
||||
|
||||
// if o.Debug {
|
||||
// redis.SetLogger(o.Logg)
|
||||
// }
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
ctx, cancel := context.WithTimeout(ctx, util.SLATimeout)
|
||||
defer cancel()
|
||||
|
||||
_, err = redisClient.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
if err := redisClient.SetNX(ctx, systemGlobalLockKey, false, 0).Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -45,6 +46,7 @@ func NewRedisPool(o RedisPoolOpts) (*RedisPool, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Interface adapter for asynq to resuse the same Redis connection pool.
|
||||
func (r *RedisPool) MakeRedisClient() interface{} {
|
||||
return r.Client
|
||||
}
|
||||
|
8
pkg/util/sla.go
Normal file
8
pkg/util/sla.go
Normal file
@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// SLATimeout is the max duration after which any network call/context should be aborted.
|
||||
SLATimeout = 5 * time.Second
|
||||
)
|
96
queries.sql
Normal file
96
queries.sql
Normal file
@ -0,0 +1,96 @@
|
||||
--name: write-key-pair
|
||||
-- Save hex encoded private key
|
||||
-- $1: public_key
|
||||
-- $2: private_key
|
||||
INSERT INTO keystore(public_key, private_key) VALUES($1, $2) RETURNING id
|
||||
|
||||
--name: load-key-pair
|
||||
-- Load saved key pair
|
||||
-- $1: public_key
|
||||
SELECT private_key FROM keystore WHERE public_key=$1
|
||||
|
||||
--name: create-otx
|
||||
-- Create a new locally originating tx
|
||||
-- $1: tracking_id
|
||||
-- $2: type
|
||||
-- $3: raw_tx
|
||||
-- $4: tx_hash
|
||||
-- $5: from
|
||||
-- $6: data
|
||||
-- $7: gas_price
|
||||
-- $8: gas_limit
|
||||
-- $9: transfer_value
|
||||
-- $10: nonce
|
||||
INSERT INTO otx_sign(
|
||||
tracking_id,
|
||||
"type",
|
||||
raw_tx,
|
||||
tx_hash,
|
||||
"from",
|
||||
"data",
|
||||
gas_price,
|
||||
gas_limit,
|
||||
transfer_value,
|
||||
nonce
|
||||
) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id
|
||||
|
||||
--name: get-next-nonce
|
||||
-- Gets last nonce from the otx table for a particular address for bootstrapping purposes
|
||||
-- $1: public_key
|
||||
SELECT nonce + 1 AS nonce FROM otx_sign WHERE otx_sign.from = $1 ORDER BY created_at DESC LIMIT 1;
|
||||
|
||||
--name: get-tx-status-by-tracking-id
|
||||
-- Gets tx status's from possible multiple txs with the same tracking_id
|
||||
-- $1: tracking_id
|
||||
SELECT otx_sign.type, otx_sign.tx_hash, otx_sign.transfer_value, otx_sign.created_at, otx_dispatch.status FROM otx_sign
|
||||
INNER JOIN otx_dispatch ON otx_sign.id = otx_dispatch.otx_id
|
||||
WHERE otx_sign.tracking_id=$1
|
||||
|
||||
--name: create-dispatch-status
|
||||
-- Create a new dispatch status
|
||||
-- $1: otx_id
|
||||
-- $2: status
|
||||
INSERT INTO otx_dispatch(
|
||||
otx_id,
|
||||
"status"
|
||||
) VALUES($1, $2) RETURNING id
|
||||
|
||||
--name: update-dispatch-status
|
||||
-- Updates the status of the dispatched tx with the chain mine status
|
||||
-- $1: tx_hash
|
||||
-- $2: status
|
||||
-- $3: block
|
||||
UPDATE otx_dispatch SET "status" = $2, "block" = $3 WHERE otx_dispatch.id = (
|
||||
SELECT otx_dispatch.id FROM otx_dispatch
|
||||
INNER JOIN otx_sign ON otx_dispatch.otx_id = otx_sign.id
|
||||
WHERE otx_sign.tx_hash=$1
|
||||
AND otx_dispatch.status = 'IN_NETWORK'
|
||||
)
|
||||
|
||||
--name: activate-account
|
||||
-- Activate an account following successful quorum
|
||||
-- $1: public_key
|
||||
UPDATE keystore SET active = true WHERE public_key=$1
|
||||
|
||||
--name: get-account-status-by-address
|
||||
-- Gets current gas lock and activation status for an individual account by address
|
||||
-- $1: public_key
|
||||
SELECT keystore.active, gas_lock.lock FROM keystore
|
||||
INNER JOIN gas_lock ON keystore.id = gas_lock.key_id
|
||||
WHERE keystore.public_key=$1
|
||||
|
||||
--name: acc-gas-lock
|
||||
-- Locks an account for gas reasons
|
||||
-- $1: public_key
|
||||
UPDATE gas_lock SET lock = true WHERE key_id = (
|
||||
SELECT id FROM keystore
|
||||
WHERE public_key=$1
|
||||
)
|
||||
|
||||
--name: acc-gas-unlock
|
||||
-- Unlocks an account for gas reasons
|
||||
-- $1: public_key
|
||||
UPDATE gas_lock SET lock = false WHERE key_id = (
|
||||
SELECT id FROM keystore
|
||||
WHERE public_key=$1
|
||||
)
|
Loading…
Reference in New Issue
Block a user