Compare commits
39 Commits
v0.3.4-alp
...
master
Author | SHA1 | Date |
---|---|---|
dependabot[bot] | 8b116f8a0f | |
dependabot[bot] | 888fb4ce11 | |
dependabot[bot] | 27b300db93 | |
dependabot[bot] | 788cf66fc7 | |
dependabot[bot] | d2cb0796c8 | |
Mohamed Sohail | 9e1d62a014 | |
Mohamed Sohail | 98ff897049 | |
Mohamed Sohail | 8dc0dcf12d | |
dependabot[bot] | f84b90f411 | |
dependabot[bot] | 83113c8411 | |
dependabot[bot] | 6ccc27685c | |
dependabot[bot] | e821da13d4 | |
dependabot[bot] | 2b42da206b | |
Mohamed Sohail | b9d3c219c8 | |
Mohamed Sohail | 8ef2311d8e | |
Mohamed Sohail | e91f82c08a | |
dependabot[bot] | 223656b2bc | |
dependabot[bot] | 0218831bd9 | |
dependabot[bot] | b2df62f0df | |
dependabot[bot] | d9acaa9583 | |
Mohamed Sohail | d51e74883d | |
Mohamed Sohail | 0e5db7f06f | |
Mohamed Sohail | b137088d38 | |
Mohamed Sohail | 21a17d2735 | |
Mohamed Sohail | eba329eefa | |
Mohamed Sohail | 82294b96f8 | |
Mohamed Sohail | e67a42ede3 | |
Mohamed Sohail | a98fe958a3 | |
Mohamed Sohail | e203c49049 | |
Mohamed Sohail | 448b142f7c | |
Mohamed Sohail | 750fdd9c1f | |
Mohamed Sohail | 5f5678e25b | |
Mohamed Sohail | ef5bd1860f | |
Mohamed Sohail | ac5567bc56 | |
Mohamed Sohail | 2d5e41eb81 | |
dependabot[bot] | fc49f2387d | |
dependabot[bot] | 81a3bb7e3c | |
Mohamed Sohail | 0235f25f81 | |
Mohamed Sohail | 04e5d3c20f |
|
@ -38,8 +38,8 @@ jobs:
|
|||
|
||||
- name: Set outputs
|
||||
run: |
|
||||
echo "RELEASE_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
echo "RELEASE_SHORT_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cic-custodial
|
||||
dist
|
||||
.env
|
||||
**/*.env
|
||||
covprofile
|
6
Makefile
6
Makefile
|
@ -2,7 +2,7 @@ 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
|
||||
.PHONY: build run run-debug docs
|
||||
|
||||
clean:
|
||||
rm ${BIN}
|
||||
|
@ -10,6 +10,10 @@ clean:
|
|||
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/*
|
||||
|
||||
|
|
|
@ -2,24 +2,25 @@ package main
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"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 (
|
||||
contextTimeout = 5 * time.Second
|
||||
systemGlobalLockKey = "system:global_lock"
|
||||
)
|
||||
|
||||
// Bootstrap API server.
|
||||
func initApiServer(custodialContainer *custodial.Custodial) *echo.Echo {
|
||||
customValidator := validator.New()
|
||||
customValidator.RegisterValidation("eth_checksum", api.EthChecksumValidator)
|
||||
|
||||
server := echo.New()
|
||||
server.HideBanner = true
|
||||
|
@ -29,15 +30,9 @@ func initApiServer(custodialContainer *custodial.Custodial) *echo.Echo {
|
|||
}
|
||||
server.HTTPErrorHandler = customHTTPErrorHandler
|
||||
|
||||
server.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
c.Set("cu", custodialContainer)
|
||||
return next(c)
|
||||
}
|
||||
})
|
||||
server.Use(middleware.Recover())
|
||||
server.Use(middleware.BodyLimit("1M"))
|
||||
server.Use(middleware.ContextTimeout(contextTimeout))
|
||||
server.Use(middleware.ContextTimeout(util.SLATimeout))
|
||||
|
||||
if ko.Bool("service.metrics") {
|
||||
server.GET("/metrics", func(c echo.Context) error {
|
||||
|
@ -46,10 +41,17 @@ func initApiServer(custodialContainer *custodial.Custodial) *echo.Echo {
|
|||
})
|
||||
}
|
||||
|
||||
apiRoute := server.Group("/api")
|
||||
apiRoute.POST("/account/create", api.HandleAccountCreate)
|
||||
apiRoute.POST("/sign/transfer", api.HandleSignTransfer)
|
||||
apiRoute.GET("/track/:trackingId", api.HandleTrackTx)
|
||||
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
|
||||
}
|
||||
|
@ -81,3 +83,23 @@ func customHTTPErrorHandler(err error, c echo.Context) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/w3-celo-patch"
|
||||
)
|
||||
|
||||
// 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{
|
||||
// Keccak hash -> 0x449a52f8
|
||||
"mintTo": w3.MustNewFunc("mintTo(address, uint256)", "bool"),
|
||||
// Keccak hash -> 0xa9059cbb
|
||||
"transfer": w3.MustNewFunc("transfer(address,uint256)", "bool"),
|
||||
// Keccak hash -> 0x23b872dd
|
||||
"transferFrom": w3.MustNewFunc("transferFrom(address, address, uint256)", "bool"),
|
||||
// Add to account index
|
||||
"add": w3.MustNewFunc("add(address)", "bool"),
|
||||
// giveTo gas refill
|
||||
"giveTo": w3.MustNewFunc("giveTo(address)", "uint256"),
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrap the internal custodial system configs and system signer key.
|
||||
// This container is passed down to individual tasker and API handlers.
|
||||
func initSystemContainer(ctx context.Context, noncestore nonce.Noncestore) *custodial.SystemContainer {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Some custodial system defaults loaded from the config file.
|
||||
systemContainer := &custodial.SystemContainer{
|
||||
Abis: initAbis(),
|
||||
AccountIndexContract: w3.A(ko.MustString("system.account_index_address")),
|
||||
GasFaucetContract: w3.A(ko.MustString("system.gas_faucet_address")),
|
||||
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")),
|
||||
LockTimeout: 1 * time.Second,
|
||||
PublicKey: ko.MustString("system.public_key"),
|
||||
TokenDecimals: ko.MustInt("system.token_decimals"),
|
||||
TokenTransferGasLimit: uint64(ko.MustInt64("system.token_transfer_gas_limit")),
|
||||
}
|
||||
|
||||
// Check if system signer account nonce is present.
|
||||
// If not (first boot), we bootstrap it from the network.
|
||||
currentSystemNonce, err := noncestore.Peek(ctx, ko.MustString("system.public_key"))
|
||||
lo.Info("custodial: loaded system nonce from noncestore", "nonce", currentSystemNonce)
|
||||
if err == redis.Nil {
|
||||
nonce, err := noncestore.SyncNetworkNonce(ctx, ko.MustString("system.public_key"))
|
||||
lo.Info("custodial: syncing system nonce from network", "nonce", nonce)
|
||||
if err != nil {
|
||||
lo.Fatal("custodial: critical error bootstrapping system container", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
loadedPrivateKey, err := eth_crypto.HexToECDSA(ko.MustString("system.private_key"))
|
||||
if err != nil {
|
||||
lo.Fatal("custodial: critical error bootstrapping system container", "error", err)
|
||||
}
|
||||
systemContainer.PrivateKey = loadedPrivateKey
|
||||
|
||||
return systemContainer
|
||||
}
|
|
@ -3,23 +3,16 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/keystore"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/pub"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/queries"
|
||||
"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/postgres"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/redis"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/knadh/goyesql/v2"
|
||||
"github.com/knadh/koanf/parsers/toml"
|
||||
"github.com/knadh/koanf/providers/env"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
|
@ -86,21 +79,6 @@ func initCeloProvider() *celoutils.Provider {
|
|||
return provider
|
||||
}
|
||||
|
||||
// Load postgres pool.
|
||||
func initPostgresPool() *pgxpool.Pool {
|
||||
poolOpts := postgres.PostgresPoolOpts{
|
||||
DSN: ko.MustString("postgres.dsn"),
|
||||
MigrationsFolderPath: migrationsFolderFlag,
|
||||
}
|
||||
|
||||
pool, err := postgres.NewPostgresPool(context.Background(), poolOpts)
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error connecting to postgres", "error", err)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
// Load separate redis connection for the tasker on a reserved db namespace.
|
||||
func initAsynqRedisPool() *redis.RedisPool {
|
||||
poolOpts := redis.RedisPoolOpts{
|
||||
|
@ -131,36 +109,12 @@ func initCommonRedisPool() *redis.RedisPool {
|
|||
return pool
|
||||
}
|
||||
|
||||
// Load SQL statements into struct.
|
||||
func initQueries() *queries.Queries {
|
||||
parsedQueries, err := goyesql.ParseFile(queriesFlag)
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error loading SQL queries", "error", err)
|
||||
}
|
||||
|
||||
loadedQueries, err := queries.LoadQueries(parsedQueries)
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error loading SQL queries", "error", err)
|
||||
}
|
||||
|
||||
return loadedQueries
|
||||
}
|
||||
|
||||
// Load postgres based keystore.
|
||||
func initPostgresKeystore(postgresPool *pgxpool.Pool, queries *queries.Queries) keystore.Keystore {
|
||||
keystore := keystore.NewPostgresKeytore(keystore.Opts{
|
||||
PostgresPool: postgresPool,
|
||||
Queries: queries,
|
||||
})
|
||||
|
||||
return keystore
|
||||
}
|
||||
|
||||
// Load redis backed noncestore.
|
||||
func initRedisNoncestore(redisPool *redis.RedisPool, celoProvider *celoutils.Provider) nonce.Noncestore {
|
||||
func initRedisNoncestore(redisPool *redis.RedisPool, chainProvider *celoutils.Provider, store store.Store) nonce.Noncestore {
|
||||
return nonce.NewRedisNoncestore(nonce.Opts{
|
||||
RedisPool: redisPool,
|
||||
CeloProvider: celoProvider,
|
||||
ChainProvider: chainProvider,
|
||||
RedisPool: redisPool,
|
||||
Store: store,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -177,11 +131,17 @@ func initTaskerClient(redisPool *redis.RedisPool) *tasker.TaskerClient {
|
|||
}
|
||||
|
||||
// Load Postgres store.
|
||||
func initPostgresStore(postgresPool *pgxpool.Pool, queries *queries.Queries) store.Store {
|
||||
return store.NewPostgresStore(store.Opts{
|
||||
PostgresPool: postgresPool,
|
||||
Queries: queries,
|
||||
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.
|
||||
|
@ -200,19 +160,6 @@ func initJetStream() (*nats.Conn, nats.JetStreamContext) {
|
|||
return natsConn, js
|
||||
}
|
||||
|
||||
func initPub(jsCtx nats.JetStreamContext) *pub.Pub {
|
||||
pub, err := pub.NewPub(pub.PubOpts{
|
||||
DedupDuration: time.Duration(ko.MustInt("jetstream.dedup_duration_hrs")) * time.Hour,
|
||||
JsCtx: jsCtx,
|
||||
PersistDuration: time.Duration(ko.MustInt("jetstream.persist_duration_hrs")) * time.Hour,
|
||||
})
|
||||
if err != nil {
|
||||
lo.Fatal("init: critical error bootstrapping pub", "error", err)
|
||||
}
|
||||
|
||||
return pub
|
||||
}
|
||||
|
||||
func initSub(natsConn *nats.Conn, jsCtx nats.JetStreamContext, cu *custodial.Custodial) *sub.Sub {
|
||||
sub, err := sub.NewSub(sub.SubOpts{
|
||||
CustodialContainer: cu,
|
||||
|
|
|
@ -14,13 +14,11 @@ import (
|
|||
"github.com/zerodha/logf"
|
||||
)
|
||||
|
||||
type (
|
||||
internalServiceContainer struct {
|
||||
apiService *echo.Echo
|
||||
jetstreamSub *sub.Sub
|
||||
taskerService *tasker.TaskerServer
|
||||
}
|
||||
)
|
||||
type internalServicesContainer struct {
|
||||
apiService *echo.Echo
|
||||
jetstreamSub *sub.Sub
|
||||
taskerService *tasker.TaskerServer
|
||||
}
|
||||
|
||||
var (
|
||||
build string
|
||||
|
@ -48,35 +46,35 @@ func init() {
|
|||
func main() {
|
||||
lo.Info("main: starting cic-custodial", "build", build)
|
||||
|
||||
parsedQueries := initQueries()
|
||||
celoProvider := initCeloProvider()
|
||||
postgresPool := initPostgresPool()
|
||||
asynqRedisPool := initAsynqRedisPool()
|
||||
redisPool := initCommonRedisPool()
|
||||
|
||||
postgresKeystore := initPostgresKeystore(postgresPool, parsedQueries)
|
||||
pgStore := initPostgresStore(postgresPool, parsedQueries)
|
||||
redisNoncestore := initRedisNoncestore(redisPool, celoProvider)
|
||||
store := initPgStore()
|
||||
redisNoncestore := initRedisNoncestore(redisPool, celoProvider, store)
|
||||
lockProvider := initLockProvider(redisPool.Client)
|
||||
taskerClient := initTaskerClient(asynqRedisPool)
|
||||
systemContainer := initSystemContainer(context.Background(), redisNoncestore)
|
||||
|
||||
natsConn, jsCtx := initJetStream()
|
||||
jsPub := initPub(jsCtx)
|
||||
|
||||
custodial := &custodial.Custodial{
|
||||
CeloProvider: celoProvider,
|
||||
Keystore: postgresKeystore,
|
||||
LockProvider: lockProvider,
|
||||
Noncestore: redisNoncestore,
|
||||
PgStore: pgStore,
|
||||
Pub: jsPub,
|
||||
RedisClient: redisPool.Client,
|
||||
SystemContainer: systemContainer,
|
||||
TaskerClient: taskerClient,
|
||||
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 := &internalServiceContainer{}
|
||||
internalServices := &internalServicesContainer{}
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
signalCh, closeCh := createSigChannel()
|
||||
|
|
|
@ -34,13 +34,10 @@ func initTasker(custodialContainer *custodial.Custodial, redisPool *redis.RedisP
|
|||
observibilityMiddleware(),
|
||||
})
|
||||
|
||||
taskerServer.RegisterHandlers(tasker.AccountPrepareTask, task.AccountPrepare(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.AccountRegisterTask, task.AccountRegisterOnChainProcessor(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.AccountGiftGasTask, task.AccountGiftGasProcessor(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.AccountGiftVoucherTask, task.GiftVoucherProcessor(custodialContainer))
|
||||
taskerServer.RegisterHandlers(tasker.AccountActivateTask, task.AccountActivateProcessor(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
|
||||
|
@ -72,6 +69,8 @@ func observibilityMiddleware() asynq.MiddlewareFunc {
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func createSigChannel() (chan os.Signal, func()) {
|
|||
}
|
||||
}
|
||||
|
||||
func startGracefulShutdown(ctx context.Context, internalServices *internalServiceContainer) {
|
||||
func startGracefulShutdown(ctx context.Context, internalServices *internalServicesContainer) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
|
|
37
config.toml
37
config.toml
|
@ -3,32 +3,17 @@ address = ":5000"
|
|||
# Exposes Go process Prometheus metrics
|
||||
# /metrics endpoint
|
||||
metrics = true
|
||||
docs = false
|
||||
|
||||
[chain]
|
||||
rpc_endpoint = ""
|
||||
testnet = true
|
||||
devnet = false
|
||||
rpc_endpoint = ""
|
||||
testnet = true
|
||||
registry_address = ""
|
||||
|
||||
[system]
|
||||
# System default values
|
||||
|
||||
# Gas values are in wei with 18 d.p. precision unless otherwise stated
|
||||
# Token values are in wei with 6 d.p. precision unless otherwise stated
|
||||
|
||||
# All addresses MUST be checksumed
|
||||
account_index_address = ""
|
||||
gas_faucet_address = ""
|
||||
gas_refill_threshold = 2500000000000000
|
||||
gas_refill_value = 15000000000000000
|
||||
giftable_gas_value = 15000000000000000
|
||||
giftable_token_address = ""
|
||||
giftable_token_value = 5000000
|
||||
# System private key
|
||||
# Should always be toped up
|
||||
private_key = ""
|
||||
public_key = ""
|
||||
token_decimals = 6
|
||||
token_transfer_gas_limit = 200000
|
||||
public_key = ""
|
||||
approve_timeout = "30m"
|
||||
|
||||
[postgres]
|
||||
dsn = ""
|
||||
|
@ -36,17 +21,15 @@ dsn = ""
|
|||
[redis]
|
||||
# Used for locks and the Noncestore
|
||||
# Ideally use DB 1
|
||||
dsn = ""
|
||||
dsn = ""
|
||||
min_idle_conn = 5
|
||||
|
||||
[asynq]
|
||||
# Exclusively used by the asynq tasker
|
||||
# Ideally use DB 0
|
||||
dsn = ""
|
||||
dsn = ""
|
||||
task_retention_hrs = 24
|
||||
worker_count = 15
|
||||
worker_count = 15
|
||||
|
||||
[jetstream]
|
||||
endpoint = ""
|
||||
persist_duration_hrs = 48
|
||||
dedup_duration_hrs = 6
|
||||
endpoint = ""
|
|
@ -1,7 +1,7 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
redis:
|
||||
image: redis:6-alpine
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --save 60 1 --loglevel warning
|
||||
volumes:
|
||||
|
@ -20,9 +20,9 @@ services:
|
|||
environment:
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=cic_custodial
|
||||
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:
|
||||
|
@ -37,8 +37,8 @@ services:
|
|||
volumes:
|
||||
- cic-custodial-nats:/nats/data
|
||||
ports:
|
||||
- "4222:4222"
|
||||
- "8222:8222"
|
||||
- "127.0.0.1:4222:4222"
|
||||
- "127.0.0.1:8222:8222"
|
||||
asynqmon:
|
||||
image: hibiken/asynqmon
|
||||
restart: unless-stopped
|
||||
|
@ -49,6 +49,16 @@ services:
|
|||
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
|
|
@ -0,0 +1,2 @@
|
|||
CREATE DATABASE cic_custodial;
|
||||
CREATE DATABASE cic_chain_events;
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
51
go.mod
51
go.mod
|
@ -3,34 +3,40 @@ module github.com/grassrootseconomics/cic-custodial
|
|||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/VictoriaMetrics/metrics v1.23.1
|
||||
github.com/bsm/redislock v0.7.2
|
||||
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.11.2
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-playground/validator/v10 v10.14.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/grassrootseconomics/celoutils v1.0.0
|
||||
github.com/grassrootseconomics/celoutils v1.4.0
|
||||
github.com/grassrootseconomics/w3-celo-patch v0.2.0
|
||||
github.com/hibiken/asynq v0.24.0
|
||||
github.com/jackc/pgx/v5 v5.3.1
|
||||
github.com/jackc/tern/v2 v2.0.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.0
|
||||
github.com/knadh/koanf/v2 v2.0.1
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/nats-io/nats.go v1.24.0
|
||||
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
|
||||
golang.org/x/crypto v0.6.0
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -40,11 +46,16 @@ 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.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-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
|
||||
|
@ -64,9 +75,12 @@ require (
|
|||
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/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
|
||||
|
@ -74,9 +88,10 @@ require (
|
|||
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.3.0 // 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.9.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
@ -86,6 +101,7 @@ require (
|
|||
github.com/shirou/gopsutil v3.21.11+incompatible // 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
|
||||
|
@ -94,12 +110,15 @@ require (
|
|||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/valyala/histogram v1.2.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/net v0.7.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.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // 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
|
||||
)
|
||||
|
|
150
go.sum
150
go.sum
|
@ -37,6 +37,8 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt
|
|||
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=
|
||||
|
@ -44,12 +46,16 @@ github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYr
|
|||
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.23.1 h1:/j8DzeJBxSpL2qSIdqnRFLvQQhbJyJbbEi22yMm7oL0=
|
||||
github.com/VictoriaMetrics/metrics v1.23.1/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc=
|
||||
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=
|
||||
|
@ -71,8 +77,12 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
|||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
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=
|
||||
|
@ -112,8 +122,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
|||
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=
|
||||
|
@ -123,6 +134,7 @@ github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB0
|
|||
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/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=
|
||||
|
@ -153,6 +165,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||
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=
|
||||
|
@ -172,19 +186,23 @@ 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-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.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||
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-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=
|
||||
|
@ -214,7 +232,6 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
|||
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=
|
||||
|
@ -234,7 +251,6 @@ 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=
|
||||
|
@ -259,8 +275,10 @@ 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/celoutils v1.0.0 h1:+pfiNZA+PJczmpN7RsZ/sr9PM0jKQQHxB6y/Ix3AFwA=
|
||||
github.com/grassrootseconomics/celoutils v1.0.0/go.mod h1:Uo5YRy6AGLAHDZj9jaOI+AWoQ1H3L0v79728pPMkm9Q=
|
||||
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/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=
|
||||
|
@ -271,8 +289,6 @@ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:i
|
|||
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.24.0 h1:r1CiSVYCy1vGq9REKGI/wdB2D5n/QmtzihYHHXOuBUs=
|
||||
github.com/hibiken/asynq v0.24.0/go.mod h1:FVnRfUTm6gcoDkM/EjF4OIh5/06ergCPUO6pS2B2y+w=
|
||||
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=
|
||||
|
@ -306,12 +322,12 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
|||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
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.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
|
||||
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
|
||||
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.0.0 h1:ZE3nUQdjGFljIB2ExOgh9/2snUBfzvbAlbP8jt92xI0=
|
||||
github.com/jackc/tern/v2 v2.0.0/go.mod h1:4cpqN/grjWYeRWcKXah5YGoviJKJuoqNLoORKLumoG0=
|
||||
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=
|
||||
|
@ -320,6 +336,8 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M
|
|||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
|
@ -334,7 +352,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
|
|||
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.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
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=
|
||||
|
@ -348,8 +367,8 @@ github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clw
|
|||
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.0 h1:XPQ5ilNnwnNaHrfQ1YpTVhUAjcGHnEKA+lRpipQv02Y=
|
||||
github.com/knadh/koanf/v2 v2.0.0/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
|
||||
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/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=
|
||||
|
@ -358,6 +377,7 @@ 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/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.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
|
@ -366,13 +386,16 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
|
|||
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=
|
||||
|
@ -419,12 +442,13 @@ github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo
|
|||
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.24.0 h1:CRiD8L5GOQu/DcfkmgBcTTIQORMwizF+rPk6T0RaHVQ=
|
||||
github.com/nats-io/nats.go v1.24.0/go.mod h1:dVQF+BK3SzUZpwyzHedXsvH3EO38aVKuOPkkHlv5hXA=
|
||||
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
|
||||
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
|
||||
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=
|
||||
|
@ -435,15 +459,12 @@ 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=
|
||||
|
@ -476,6 +497,9 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
|||
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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||
|
@ -509,7 +533,9 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
|
|||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
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=
|
||||
|
@ -517,9 +543,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.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
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=
|
||||
|
@ -542,7 +580,6 @@ github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tz
|
|||
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.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=
|
||||
|
@ -552,8 +589,6 @@ 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/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
@ -567,12 +602,11 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh
|
|||
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-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
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.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
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=
|
||||
|
@ -602,6 +636,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
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=
|
||||
|
@ -621,17 +656,15 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
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-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.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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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=
|
||||
|
@ -685,23 +718,23 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/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-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-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.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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.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=
|
||||
|
@ -715,8 +748,8 @@ 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.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.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.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=
|
||||
|
@ -751,8 +784,9 @@ golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/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=
|
||||
|
@ -789,22 +823,17 @@ 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-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
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/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=
|
||||
|
@ -813,6 +842,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
|||
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=
|
||||
|
@ -828,8 +858,10 @@ 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.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=
|
||||
|
|
|
@ -12,52 +12,56 @@ import (
|
|||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// CreateAccountHandler route.
|
||||
// POST: /api/account/create
|
||||
// Returns the public key.
|
||||
func HandleAccountCreate(c echo.Context) error {
|
||||
var (
|
||||
cu = c.Get("cu").(*custodial.Custodial)
|
||||
)
|
||||
// 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
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
id, err := cu.Keystore.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.AccountPrepareTask,
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,98 +0,0 @@
|
|||
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 route.
|
||||
// POST: /api/sign/transfer
|
||||
// JSON Body:
|
||||
// from -> ETH address
|
||||
// to -> ETH address
|
||||
// voucherAddress -> ETH address
|
||||
// amount -> int (6 d.p. precision)
|
||||
// e.g. 1000000 = 1 VOUCHER
|
||||
// Returns the task id.
|
||||
func HandleSignTransfer(c echo.Context) error {
|
||||
var (
|
||||
cu = c.Get("cu").(*custodial.Custodial)
|
||||
req struct {
|
||||
From string `json:"from" validate:"required,eth_checksum"`
|
||||
To string `json:"to" validate:"required,eth_checksum"`
|
||||
VoucherAddress string `json:"voucherAddress" validate:"required,eth_checksum"`
|
||||
Amount uint64 `json:"amount" validate:"required"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return NewBadRequestError(ErrInvalidJSON)
|
||||
}
|
||||
|
||||
if err := c.Validate(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accountActive, gasQuota, err := cu.PgStore.GetAccountStatusByAddress(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 gasQuota < 1 {
|
||||
return c.JSON(http.StatusForbidden, ErrResp{
|
||||
Ok: false,
|
||||
Message: "Out of gas, refill pending. 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
|
||||
}
|
||||
|
||||
err = cu.PgStore.DecrGasQuota(c.Request().Context(), req.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"trackingId": trackingId,
|
||||
},
|
||||
})
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -7,36 +7,43 @@ import (
|
|||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// HandleTxStatus route.
|
||||
// GET: /api/track/:trackingId
|
||||
// Route param:
|
||||
// trackingId -> tracking UUID
|
||||
// Returns array of tx status.
|
||||
func HandleTrackTx(c echo.Context) error {
|
||||
var (
|
||||
cu = c.Get("cu").(*custodial.Custodial)
|
||||
txStatusRequest struct {
|
||||
TrackingId string `param:"trackingId" validate:"required,uuid"`
|
||||
// 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.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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if err := c.Validate(txStatusRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txs, err := cu.PgStore.GetTxStatusByTrackingId(c.Request().Context(), txStatusRequest.TrackingId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"transactions": txs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/celo-org/celo-blockchain/common"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
|
@ -17,12 +16,3 @@ func (v *Validator) Validate(i interface{}) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func EthChecksumValidator(fl validator.FieldLevel) bool {
|
||||
addr, err := common.NewMixedcaseAddressFromString(fl.Field().String())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return addr.ValidChecksum()
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
|
@ -1,47 +1,89 @@
|
|||
package custodial
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common"
|
||||
"github.com/go-redis/redis/v8"
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/keystore"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/nonce"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/pub"
|
||||
"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 (
|
||||
SystemContainer struct {
|
||||
Abis map[string]*w3.Func
|
||||
AccountIndexContract common.Address
|
||||
GasFaucetContract common.Address
|
||||
GasRefillThreshold *big.Int
|
||||
GasRefillValue *big.Int
|
||||
GiftableGasValue *big.Int
|
||||
GiftableToken common.Address
|
||||
GiftableTokenValue *big.Int
|
||||
LockTimeout time.Duration
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
PublicKey string
|
||||
TokenDecimals int
|
||||
TokenTransferGasLimit uint64
|
||||
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 {
|
||||
CeloProvider *celoutils.Provider
|
||||
Keystore keystore.Keystore
|
||||
LockProvider *redislock.Client
|
||||
Noncestore nonce.Noncestore
|
||||
PgStore store.Store
|
||||
Pub *pub.Pub
|
||||
RedisClient *redis.Client
|
||||
SystemContainer *SystemContainer
|
||||
TaskerClient *tasker.TaskerClient
|
||||
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,14 +0,0 @@
|
|||
package keystore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
)
|
||||
|
||||
// Keystore defines how keypairs should be stored and accessed from a storage backend.
|
||||
type Keystore interface {
|
||||
WriteKeyPair(context.Context, keypair.Key) (uint, error)
|
||||
LoadPrivateKey(context.Context, string) (*ecdsa.PrivateKey, error)
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package keystore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
eth_crypto "github.com/celo-org/celo-blockchain/crypto"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/queries"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/keypair"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type (
|
||||
Opts struct {
|
||||
PostgresPool *pgxpool.Pool
|
||||
Queries *queries.Queries
|
||||
}
|
||||
|
||||
PostgresKeystore struct {
|
||||
db *pgxpool.Pool
|
||||
queries *queries.Queries
|
||||
}
|
||||
)
|
||||
|
||||
func NewPostgresKeytore(o Opts) Keystore {
|
||||
return &PostgresKeystore{
|
||||
db: o.PostgresPool,
|
||||
queries: o.Queries,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteKeyPair inserts a keypair into the db and returns the linked id.
|
||||
func (ks *PostgresKeystore) WriteKeyPair(ctx context.Context, keypair keypair.Key) (uint, error) {
|
||||
var (
|
||||
id uint
|
||||
)
|
||||
|
||||
if err := ks.db.QueryRow(ctx, ks.queries.WriteKeyPair, keypair.Public, keypair.Private).Scan(&id); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// LoadPrivateKey loads a private key as a crypto primitive for direct use. An id is used to search for the private key.
|
||||
func (ks *PostgresKeystore) LoadPrivateKey(ctx context.Context, publicKey string) (*ecdsa.PrivateKey, error) {
|
||||
var (
|
||||
privateKeyString string
|
||||
)
|
||||
|
||||
if err := ks.db.QueryRow(ctx, ks.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
|
||||
}
|
|
@ -7,6 +7,5 @@ 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,35 +2,52 @@ package nonce
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"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 {
|
||||
RedisPool *redispool.RedisPool
|
||||
CeloProvider *celoutils.Provider
|
||||
}
|
||||
type (
|
||||
Opts struct {
|
||||
ChainProvider *celoutils.Provider
|
||||
RedisPool *redispool.RedisPool
|
||||
Store store.Store
|
||||
}
|
||||
|
||||
// RedisNoncestore implements `Noncestore`
|
||||
type RedisNoncestore struct {
|
||||
chainProvider *celoutils.Provider
|
||||
redis *redispool.RedisPool
|
||||
}
|
||||
// RedisNoncestore implements `Noncestore`
|
||||
RedisNoncestore struct {
|
||||
chainProvider *celoutils.Provider
|
||||
redis *redispool.RedisPool
|
||||
store store.Store
|
||||
}
|
||||
)
|
||||
|
||||
func NewRedisNoncestore(o Opts) Noncestore {
|
||||
return &RedisNoncestore{
|
||||
chainProvider: o.ChainProvider,
|
||||
redis: o.RedisPool,
|
||||
chainProvider: o.CeloProvider,
|
||||
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 {
|
||||
return 0, err
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package pub
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
const (
|
||||
streamName string = "CUSTODIAL"
|
||||
streamSubjects string = "CUSTODIAL.*"
|
||||
AccountNewNonce string = "CUSTODIAL.accountNewNonce"
|
||||
AccountRegister string = "CUSTODIAL.accountRegister"
|
||||
AccountGiftGas string = "CUSTODIAL.systemNewAccountGas"
|
||||
AccountGiftVoucher string = "CUSTODIAL.systemNewAccountVoucher"
|
||||
AccountRefillGas string = "CUSTODIAL.systemRefillAccountGas"
|
||||
DispatchFail string = "CUSTODIAL.dispatchFail"
|
||||
DispatchSuccess string = "CUSTODIAL.dispatchSuccess"
|
||||
SignTransfer string = "CUSTODIAL.signTransfer"
|
||||
)
|
||||
|
||||
type (
|
||||
PubOpts struct {
|
||||
DedupDuration time.Duration
|
||||
JsCtx nats.JetStreamContext
|
||||
PersistDuration time.Duration
|
||||
}
|
||||
|
||||
Pub struct {
|
||||
jsCtx nats.JetStreamContext
|
||||
}
|
||||
|
||||
EventPayload struct {
|
||||
OtxId uint `json:"otxId"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
TxHash string `json:"txHash"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewPub(o PubOpts) (*Pub, error) {
|
||||
stream, _ := o.JsCtx.StreamInfo(streamName)
|
||||
if stream == nil {
|
||||
_, err := o.JsCtx.AddStream(&nats.StreamConfig{
|
||||
Name: streamName,
|
||||
MaxAge: o.PersistDuration,
|
||||
Storage: nats.FileStorage,
|
||||
Subjects: []string{streamSubjects},
|
||||
Duplicates: o.DedupDuration,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Pub{
|
||||
jsCtx: o.JsCtx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Pub) Publish(subject string, dedupId string, eventPayload interface{}) error {
|
||||
jsonData, err := json.Marshal(eventPayload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = p.jsCtx.Publish(subject, jsonData, nats.MsgId(dedupId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package queries
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/knadh/goyesql/v2"
|
||||
)
|
||||
|
||||
type Queries struct {
|
||||
// Keystore
|
||||
WriteKeyPair string `query:"write-key-pair"`
|
||||
LoadKeyPair string `query:"load-key-pair"`
|
||||
// Store
|
||||
CreateOTX string `query:"create-otx"`
|
||||
CreateDispatchStatus string `query:"create-dispatch-status"`
|
||||
ActivateAccount string `query:"activate-account"`
|
||||
UpdateChainStatus string `query:"update-chain-status"`
|
||||
GetTxStatusByTrackingId string `query:"get-tx-status-by-tracking-id"`
|
||||
GetAccountActivationQuorum string `query:"get-account-activation-quorum"`
|
||||
GetAccountStatus string `query:"get-account-status-by-address"`
|
||||
DecrGasQuota string `query:"decr-gas-quota"`
|
||||
ResetGasQuota string `query:"reset-gas-quota"`
|
||||
}
|
||||
|
||||
func LoadQueries(q goyesql.Queries) (*Queries, error) {
|
||||
loadedQueries := &Queries{}
|
||||
|
||||
if err := goyesql.ScanToStruct(loadedQueries, q, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan queries %v", err)
|
||||
}
|
||||
|
||||
return loadedQueries, nil
|
||||
}
|
|
@ -4,56 +4,10 @@ import (
|
|||
"context"
|
||||
)
|
||||
|
||||
func (s *PostgresStore) GetAccountStatusByAddress(ctx context.Context, publicAddress string) (bool, int, error) {
|
||||
var (
|
||||
accountActive bool
|
||||
gasQuota int
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(ctx, s.queries.GetAccountStatus, publicAddress).Scan(&accountActive, &gasQuota); err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
return accountActive, gasQuota, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) GetAccountActivationQuorum(ctx context.Context, trackingId string) (int, error) {
|
||||
var (
|
||||
quorum int
|
||||
)
|
||||
|
||||
if err := s.db.QueryRow(ctx, s.queries.GetAccountActivationQuorum, trackingId).Scan(&quorum); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return quorum, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) DecrGasQuota(ctx context.Context, publicAddress string) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.DecrGasQuota,
|
||||
publicAddress,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) ResetGasQuota(ctx context.Context, publicAddress string) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.ResetGasQuota,
|
||||
publicAddress,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) ActivateAccount(ctx context.Context, publicAddress string) error {
|
||||
func (s *PgStore) ActivateAccount(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.ActivateAccount,
|
||||
|
@ -64,3 +18,56 @@ func (s *PostgresStore) ActivateAccount(ctx context.Context, publicAddress strin
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -2,21 +2,39 @@ package store
|
|||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
)
|
||||
|
||||
type TxStatus struct {
|
||||
Type string `db:"type" json:"txType"`
|
||||
TxHash string `db:"tx_hash" json:"txHash"`
|
||||
TransferValue uint64 `db:"transfer_value" json:"transferValue"`
|
||||
CreatedAt time.Time `db:"created_at" json:"createdAt"`
|
||||
Status string `db:"status" json:"status"`
|
||||
}
|
||||
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 *PostgresStore) CreateOtx(ctx context.Context, otx OTX) (uint, error) {
|
||||
func (s *PgStore) CreateOtx(
|
||||
ctx context.Context,
|
||||
otx Otx,
|
||||
) (uint, error) {
|
||||
var (
|
||||
id uint
|
||||
)
|
||||
|
@ -34,37 +52,73 @@ func (s *PostgresStore) CreateOtx(ctx context.Context, otx OTX) (uint, error) {
|
|||
otx.GasLimit,
|
||||
otx.TransferValue,
|
||||
otx.Nonce,
|
||||
).Scan(&id); err != nil {
|
||||
).Scan(
|
||||
&id,
|
||||
); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) GetTxStatusByTrackingId(ctx context.Context, trackingId string) ([]*TxStatus, error) {
|
||||
func (s *PgStore) GetNextNonce(
|
||||
ctx context.Context,
|
||||
publicAddress string,
|
||||
) (uint64, error) {
|
||||
var (
|
||||
txs []*TxStatus
|
||||
lastNonce uint64
|
||||
)
|
||||
|
||||
if err := pgxscan.Select(
|
||||
if err := s.db.QueryRow(
|
||||
ctx,
|
||||
s.db,
|
||||
&txs,
|
||||
s.queries.GetTxStatusByTrackingId,
|
||||
trackingId,
|
||||
s.queries.GetNextNonce,
|
||||
publicAddress,
|
||||
).Scan(
|
||||
&lastNonce,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return txs, nil
|
||||
return lastNonce, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) CreateDispatchStatus(ctx context.Context, dispatch DispatchStatus) error {
|
||||
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,
|
||||
dispatch.OtxId,
|
||||
dispatch.Status,
|
||||
otxId,
|
||||
otxStatus,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -72,21 +126,26 @@ func (s *PostgresStore) CreateDispatchStatus(ctx context.Context, dispatch Dispa
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) UpdateOtxStatusFromChainEvent(ctx context.Context, chainEvent MinimalTxInfo) error {
|
||||
func (s *PgStore) UpdateDispatchStatus(
|
||||
ctx context.Context,
|
||||
txSuccess bool,
|
||||
txHash string,
|
||||
txBlock uint64,
|
||||
) error {
|
||||
var (
|
||||
status = enum.SUCCESS
|
||||
)
|
||||
|
||||
if !chainEvent.Success {
|
||||
if !txSuccess {
|
||||
status = enum.REVERTED
|
||||
}
|
||||
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.UpdateChainStatus,
|
||||
chainEvent.TxHash,
|
||||
s.queries.UpdateDispatchStatus,
|
||||
txHash,
|
||||
status,
|
||||
chainEvent.Block,
|
||||
txBlock,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/queries"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type (
|
||||
Opts struct {
|
||||
PostgresPool *pgxpool.Pool
|
||||
Queries *queries.Queries
|
||||
}
|
||||
|
||||
PostgresStore struct {
|
||||
db *pgxpool.Pool
|
||||
queries *queries.Queries
|
||||
}
|
||||
)
|
||||
|
||||
func NewPostgresStore(o Opts) Store {
|
||||
return &PostgresStore{
|
||||
db: o.PostgresPool,
|
||||
queries: o.Queries,
|
||||
}
|
||||
}
|
|
@ -2,48 +2,129 @@ 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 (
|
||||
MinimalTxInfo 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"`
|
||||
}
|
||||
OTX struct {
|
||||
TrackingId string
|
||||
Type enum.OtxType
|
||||
RawTx string
|
||||
TxHash string
|
||||
From string
|
||||
Data string
|
||||
GasLimit uint64
|
||||
TransferValue uint64
|
||||
GasPrice uint64
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
DispatchStatus struct {
|
||||
OtxId uint
|
||||
Status enum.OtxStatus
|
||||
}
|
||||
|
||||
Store interface {
|
||||
CreateOtx(ctx context.Context, otx OTX) (id uint, err error)
|
||||
CreateDispatchStatus(ctx context.Context, dispatch DispatchStatus) error
|
||||
GetTxStatusByTrackingId(ctx context.Context, trackingId string) ([]*TxStatus, error)
|
||||
UpdateOtxStatusFromChainEvent(ctx context.Context, chainEvent MinimalTxInfo) error
|
||||
GetAccountStatusByAddress(ctx context.Context, publicAddress string) (bool, int, error)
|
||||
GetAccountActivationQuorum(ctx context.Context, trackingId string) (int, error)
|
||||
DecrGasQuota(ctx context.Context, publicAddress string) error
|
||||
ResetGasQuota(ctx context.Context, publicAddress string) error
|
||||
ActivateAccount(ctx context.Context, publicAddress string) error
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -4,27 +4,54 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
func (s *Sub) handler(ctx context.Context, msg *nats.Msg) error {
|
||||
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 store.MinimalTxInfo
|
||||
chainEvent ChainEvent
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(msg.Data, &chainEvent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.cu.PgStore.UpdateOtxStatusFromChainEvent(ctx, chainEvent); err != nil {
|
||||
if err := s.cu.Store.UpdateDispatchStatus(
|
||||
ctx,
|
||||
chainEvent.Success,
|
||||
chainEvent.TxHash,
|
||||
chainEvent.Block,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.Subject {
|
||||
case "CHAIN.gas":
|
||||
if err := s.cu.PgStore.ResetGasQuota(ctx, checksum(chainEvent.To)); 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,19 +3,17 @@ package sub
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"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.*"
|
||||
actionTimeout = 5 * time.Second
|
||||
waitDelay = 1 * time.Second
|
||||
durableId = "cic-custodial"
|
||||
pullStream = "CHAIN"
|
||||
pullSubject = "CHAIN.*"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -67,7 +65,6 @@ func (s *Sub) Process() error {
|
|||
events, err := natsSub.Fetch(1)
|
||||
if err != nil {
|
||||
if errors.Is(err, nats.ErrTimeout) {
|
||||
s.logg.Debug("sub: no msg to pull")
|
||||
continue
|
||||
} else if errors.Is(err, nats.ErrConnectionClosed) {
|
||||
return nil
|
||||
|
@ -78,13 +75,12 @@ func (s *Sub) Process() error {
|
|||
|
||||
if len(events) > 0 {
|
||||
msg := events[0]
|
||||
ctx, cancel := context.WithTimeout(context.Background(), actionTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), util.SLATimeout)
|
||||
|
||||
if err := s.handler(ctx, msg); err != nil {
|
||||
if err := s.processEventHandler(ctx, msg); err != nil {
|
||||
s.logg.Error("sub: handler error", "error", err)
|
||||
msg.Nak()
|
||||
} else {
|
||||
s.logg.Debug("sub: processed msg", "subject", msg.Subject)
|
||||
msg.Ack()
|
||||
}
|
||||
cancel()
|
|
@ -1,31 +0,0 @@
|
|||
package sub
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// TODO: This should probably be used project wide
|
||||
func checksum(address string) string {
|
||||
address = strings.ToLower(address)
|
||||
address = strings.Replace(address, "0x", "", 1)
|
||||
|
||||
sha := sha3.NewLegacyKeccak256()
|
||||
sha.Write([]byte(address))
|
||||
hash := sha.Sum(nil)
|
||||
hashstr := hex.EncodeToString(hash)
|
||||
result := []string{"0x"}
|
||||
for i, v := range address {
|
||||
res, _ := strconv.ParseInt(string(hashstr[i]), 16, 64)
|
||||
if res > 7 {
|
||||
result = append(result, strings.ToUpper(string(v)))
|
||||
continue
|
||||
}
|
||||
result = append(result, string(v))
|
||||
}
|
||||
|
||||
return strings.Join(result, "")
|
||||
}
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
retryRequeueInterval = 2 * time.Second
|
||||
retryRequeueInterval = 1 * time.Second
|
||||
)
|
||||
|
||||
type TaskerServerOpts struct {
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
const (
|
||||
requiredQuorum = 3
|
||||
)
|
||||
|
||||
var (
|
||||
ErrQuorumNotReached = errors.New("Account activation quorum not reached.")
|
||||
)
|
||||
|
||||
func AccountActivateProcessor(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
payload AccountPayload
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
quorum, err := cu.PgStore.GetAccountActivationQuorum(ctx, payload.TrackingId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if quorum < requiredQuorum {
|
||||
return ErrQuorumNotReached
|
||||
}
|
||||
|
||||
if err := cu.PgStore.ActivateAccount(ctx, payload.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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/pub"
|
||||
"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"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
const (
|
||||
accountActivationCheckDelay = 5 * time.Second
|
||||
)
|
||||
|
||||
func AccountGiftGasProcessor(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 fmt.Errorf("account: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+cu.SystemContainer.PublicKey,
|
||||
cu.SystemContainer.LockTimeout,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemContainer.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemContainer.PublicKey); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
builtTx, err := cu.CeloProvider.SignGasTransferTx(
|
||||
cu.SystemContainer.PrivateKey,
|
||||
celoutils.GasTransferTxOpts{
|
||||
To: w3.A(payload.PublicKey),
|
||||
Nonce: nonce,
|
||||
Value: cu.SystemContainer.GiftableGasValue,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx, err := builtTx.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.GIFT_GAS,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: cu.SystemContainer.GiftableGasValue.Uint64(),
|
||||
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
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountActivateTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
asynq.ProcessIn(accountActivationCheckDelay),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eventPayload := &pub.EventPayload{
|
||||
OtxId: id,
|
||||
TrackingId: payload.TrackingId,
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(
|
||||
pub.AccountGiftGas,
|
||||
builtTx.Hash().Hex(),
|
||||
eventPayload,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"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/pub"
|
||||
"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"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
func GiftVoucherProcessor(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.SystemContainer.PublicKey,
|
||||
cu.SystemContainer.LockTimeout,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemContainer.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemContainer.PublicKey); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.SystemContainer.Abis["mintTo"].EncodeArgs(
|
||||
w3.A(payload.PublicKey),
|
||||
cu.SystemContainer.GiftableTokenValue,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
cu.SystemContainer.PrivateKey,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: cu.SystemContainer.GiftableToken,
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: cu.SystemContainer.TokenTransferGasLimit,
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx, err := builtTx.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.GIFT_VOUCHER,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: cu.SystemContainer.GiftableTokenValue.Uint64(),
|
||||
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
|
||||
}
|
||||
|
||||
eventPayload := &pub.EventPayload{
|
||||
OtxId: id,
|
||||
TrackingId: payload.TrackingId,
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(
|
||||
pub.AccountGiftVoucher,
|
||||
builtTx.Hash().Hex(),
|
||||
eventPayload,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/pub"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/tasker"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type AccountPayload struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
}
|
||||
|
||||
func AccountPrepare(cu *custodial.Custodial) func(context.Context, *asynq.Task) error {
|
||||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var payload AccountPayload
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return fmt.Errorf("account: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := cu.Noncestore.SetNewAccountNonce(ctx, payload.PublicKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountRegisterTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountGiftGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountGiftVoucherTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: t.Payload(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eventPayload := pub.EventPayload{
|
||||
TrackingId: payload.TrackingId,
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(
|
||||
pub.AccountNewNonce,
|
||||
payload.PublicKey,
|
||||
eventPayload,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -3,86 +3,121 @@ package task
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/bsm/redislock"
|
||||
"github.com/celo-org/celo-blockchain/common/hexutil"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/pub"
|
||||
"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"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
const (
|
||||
gasLockPrefix = "gas_lock:"
|
||||
gasLockExpiry = 1 * time.Hour
|
||||
)
|
||||
|
||||
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 fmt.Errorf("account: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
// TODO: Check eth-faucet whether we can request for a topup before signing the tx.
|
||||
_, gasQuota, err := cu.PgStore.GetAccountStatusByAddress(ctx, payload.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gasLock, err := cu.RedisClient.Get(ctx, gasLockPrefix+payload.PublicKey).Bool()
|
||||
if !errors.Is(err, redis.Nil) {
|
||||
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
|
||||
}
|
||||
|
||||
if gasQuota > 0 || gasLock {
|
||||
// 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
|
||||
}
|
||||
|
||||
// TODO: Use eth-faucet.
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+cu.SystemContainer.PublicKey,
|
||||
cu.SystemContainer.LockTimeout,
|
||||
nil,
|
||||
lockPrefix+cu.SystemPublicKey,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemContainer.PublicKey)
|
||||
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.SystemContainer.PublicKey); nErr != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemPublicKey); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO: Review gas params
|
||||
builtTx, err := cu.CeloProvider.SignGasTransferTx(
|
||||
cu.SystemContainer.PrivateKey,
|
||||
celoutils.GasTransferTxOpts{
|
||||
To: w3.A(payload.PublicKey),
|
||||
Nonce: nonce,
|
||||
Value: cu.SystemContainer.GiftableGasValue,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
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 {
|
||||
|
@ -94,17 +129,16 @@ func AccountRefillGasProcessor(cu *custodial.Custodial) func(context.Context, *a
|
|||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.REFILL_GAS,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: cu.SystemContainer.GiftableGasValue.Uint64(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
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
|
||||
|
@ -130,24 +164,6 @@ func AccountRefillGasProcessor(cu *custodial.Custodial) func(context.Context, *a
|
|||
return err
|
||||
}
|
||||
|
||||
eventPayload := &pub.EventPayload{
|
||||
OtxId: id,
|
||||
TrackingId: payload.TrackingId,
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(
|
||||
pub.AccountRefillGas,
|
||||
builtTx.Hash().Hex(),
|
||||
eventPayload,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := cu.RedisClient.SetEX(ctx, gasLockPrefix+payload.PublicKey, true, gasLockExpiry).Result(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,19 +3,22 @@ package task
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"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/pub"
|
||||
"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"
|
||||
"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 (
|
||||
|
@ -24,48 +27,49 @@ func AccountRegisterOnChainProcessor(cu *custodial.Custodial) func(context.Conte
|
|||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return fmt.Errorf("account: failed %v: %w", err, asynq.SkipRetry)
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+cu.SystemContainer.PublicKey,
|
||||
cu.SystemContainer.LockTimeout,
|
||||
nil,
|
||||
lockPrefix+cu.SystemPublicKey,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemContainer.PublicKey)
|
||||
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.SystemContainer.PublicKey); nErr != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemPublicKey); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.SystemContainer.Abis["add"].EncodeArgs(
|
||||
w3.A(payload.PublicKey),
|
||||
input, err := cu.Abis[custodial.Register].EncodeArgs(
|
||||
celoutils.HexToAddress(payload.PublicKey),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Review gas params.
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
cu.SystemContainer.PrivateKey,
|
||||
cu.SystemPrivateKey,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: cu.SystemContainer.AccountIndexContract,
|
||||
ContractAddress: cu.RegistryMap[celoutils.CustodialProxy],
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: cu.SystemContainer.TokenTransferGasLimit,
|
||||
GasLimit: uint64(celoutils.SafeGasLimit),
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
|
@ -78,14 +82,14 @@ func AccountRegisterOnChainProcessor(cu *custodial.Custodial) func(context.Conte
|
|||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
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.SystemContainer.PublicKey,
|
||||
From: cu.SystemPublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
GasPrice: builtTx.GasPrice(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
|
@ -113,17 +117,7 @@ func AccountRegisterOnChainProcessor(cu *custodial.Custodial) func(context.Conte
|
|||
return err
|
||||
}
|
||||
|
||||
eventPayload := &pub.EventPayload{
|
||||
OtxId: id,
|
||||
TrackingId: payload.TrackingId,
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(
|
||||
pub.AccountRegister,
|
||||
builtTx.Hash().Hex(),
|
||||
eventPayload,
|
||||
); err != nil {
|
||||
if err := cu.Noncestore.SetAccountNonce(ctx, payload.PublicKey, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -9,8 +9,6 @@ import (
|
|||
"github.com/celo-org/celo-blockchain/core/types"
|
||||
"github.com/grassrootseconomics/celoutils"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/pub"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
|
@ -25,53 +23,37 @@ func DispatchTx(cu *custodial.Custodial) func(context.Context, *asynq.Task) erro
|
|||
return func(ctx context.Context, t *asynq.Task) error {
|
||||
var (
|
||||
payload TxPayload
|
||||
dispatchStatus store.DispatchStatus
|
||||
eventPayload pub.EventPayload
|
||||
dispathchTx common.Hash
|
||||
dispatchStatus enum.OtxStatus = enum.IN_NETWORK
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
return err
|
||||
}
|
||||
|
||||
txHash := payload.Tx.Hash().Hex()
|
||||
|
||||
dispatchStatus.OtxId, eventPayload.OtxId = payload.OtxId, payload.OtxId
|
||||
eventPayload.TxHash = txHash
|
||||
|
||||
if err := cu.CeloProvider.Client.CallCtx(
|
||||
ctx,
|
||||
eth.SendTx(payload.Tx).Returns(&dispathchTx),
|
||||
); err != nil {
|
||||
switch err.Error() {
|
||||
case celoutils.ErrGasPriceLow:
|
||||
dispatchStatus.Status = enum.FAIL_LOW_GAS_PRICE
|
||||
dispatchStatus = enum.FAIL_LOW_GAS_PRICE
|
||||
case celoutils.ErrInsufficientGas:
|
||||
dispatchStatus.Status = enum.FAIL_NO_GAS
|
||||
dispatchStatus = enum.FAIL_NO_GAS
|
||||
case celoutils.ErrNonceLow:
|
||||
dispatchStatus.Status = enum.FAIL_LOW_NONCE
|
||||
dispatchStatus = enum.FAIL_LOW_NONCE
|
||||
default:
|
||||
dispatchStatus.Status = enum.FAIL_UNKNOWN_RPC_ERROR
|
||||
dispatchStatus = enum.FAIL_UNKNOWN_RPC_ERROR
|
||||
}
|
||||
|
||||
if err := cu.PgStore.CreateDispatchStatus(ctx, dispatchStatus); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(pub.DispatchFail, txHash, eventPayload); err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
dispatchStatus.Status = enum.IN_NETWORK
|
||||
|
||||
if err := cu.PgStore.CreateDispatchStatus(ctx, dispatchStatus); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(pub.DispatchSuccess, txHash, eventPayload); err != nil {
|
||||
if err := cu.Store.CreateDispatchStatus(ctx, payload.OtxId, dispatchStatus); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,60 +3,53 @@ package task
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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/pub"
|
||||
"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"
|
||||
"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"`
|
||||
}
|
||||
|
||||
transferEventPayload struct {
|
||||
DispatchTaskId string `json:"dispatchTaskId"`
|
||||
OTXId uint `json:"otxId"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
TxHash string `json:"txHash"`
|
||||
}
|
||||
)
|
||||
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
|
||||
payload TransferPayload
|
||||
err error
|
||||
networkBalance big.Int
|
||||
payload TransferPayload
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
||||
return fmt.Errorf("account: failed %v: %w", err, asynq.SkipRetry)
|
||||
return err
|
||||
}
|
||||
|
||||
lock, err := cu.LockProvider.Obtain(
|
||||
ctx,
|
||||
lockPrefix+payload.From,
|
||||
cu.SystemContainer.LockTimeout,
|
||||
nil,
|
||||
lockTimeout,
|
||||
&redislock.Options{
|
||||
RetryStrategy: lockRetry(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lock.Release(ctx)
|
||||
|
||||
key, err := cu.Keystore.LoadPrivateKey(ctx, payload.From)
|
||||
key, err := cu.Store.LoadPrivateKey(ctx, payload.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -67,26 +60,28 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
|
|||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, cu.SystemContainer.PublicKey); nErr != nil {
|
||||
if nErr := cu.Noncestore.Return(ctx, payload.From); nErr != nil {
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.SystemContainer.Abis["transfer"].EncodeArgs(w3.A(payload.To), new(big.Int).SetUint64(payload.Amount))
|
||||
input, err := cu.Abis[custodial.Transfer].EncodeArgs(
|
||||
celoutils.HexToAddress(payload.To),
|
||||
new(big.Int).SetUint64(payload.Amount),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Review gas params.
|
||||
builtTx, err := cu.CeloProvider.SignContractExecutionTx(
|
||||
key,
|
||||
celoutils.ContractExecutionTxOpts{
|
||||
ContractAddress: w3.A(payload.VoucherAddress),
|
||||
ContractAddress: celoutils.HexToAddress(payload.VoucherAddress),
|
||||
InputData: input,
|
||||
GasFeeCap: celoutils.SafeGasFeeCap,
|
||||
GasTipCap: celoutils.SafeGasTipCap,
|
||||
GasLimit: cu.SystemContainer.TokenTransferGasLimit,
|
||||
GasLimit: uint64(celoutils.SafeGasLimit),
|
||||
Nonce: nonce,
|
||||
},
|
||||
)
|
||||
|
@ -99,14 +94,14 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
|
|||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
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().Uint64(),
|
||||
GasPrice: builtTx.GasPrice(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: payload.Amount,
|
||||
Nonce: builtTx.Nonce(),
|
||||
|
@ -115,6 +110,13 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
|
|||
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,
|
||||
|
@ -143,30 +145,22 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountRefillGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: gasRefillPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !balanceCheck(networkBalance) {
|
||||
if err := cu.Store.GasLock(ctx, payload.From); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eventPayload := &transferEventPayload{
|
||||
OTXId: id,
|
||||
TrackingId: payload.TrackingId,
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
}
|
||||
|
||||
if err := cu.Pub.Publish(
|
||||
pub.SignTransfer,
|
||||
builtTx.Hash().Hex(),
|
||||
eventPayload,
|
||||
); err != nil {
|
||||
return err
|
||||
_, err = cu.TaskerClient.CreateTask(
|
||||
ctx,
|
||||
tasker.AccountRefillGasTask,
|
||||
tasker.DefaultPriority,
|
||||
&tasker.Task{
|
||||
Payload: gasRefillPayload,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -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,5 +1,37 @@
|
|||
package task
|
||||
|
||||
const (
|
||||
lockPrefix = "lock:"
|
||||
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
|
||||
minGasBalanceRequired = big.NewInt(20000000000 * 250000 * 2)
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -15,14 +15,11 @@ type Task struct {
|
|||
}
|
||||
|
||||
const (
|
||||
AccountPrepareTask TaskName = "sys:prepare_account"
|
||||
AccountRegisterTask TaskName = "sys:register_account"
|
||||
AccountGiftGasTask TaskName = "sys:gift_gas"
|
||||
AccountGiftVoucherTask TaskName = "sys:gift_token"
|
||||
AccountRefillGasTask TaskName = "sys:refill_gas"
|
||||
AccountActivateTask TaskName = "sys:quorum_check"
|
||||
SignTransferTask TaskName = "usr:sign_transfer"
|
||||
DispatchTxTask 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 (
|
||||
|
|
|
@ -25,4 +25,4 @@ $$ language plpgsql;
|
|||
create trigger insert_gas_quota
|
||||
after insert on keystore
|
||||
for each row
|
||||
execute procedure insert_gas_quota()
|
||||
execute procedure insert_gas_quota();
|
|
@ -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();
|
|
@ -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();
|
|
@ -0,0 +1 @@
|
|||
INSERT INTO otx_tx_type (value) VALUES ('TRANSFER_AUTHORIZATION');
|
|
@ -19,9 +19,8 @@ const (
|
|||
FAIL_UNKNOWN_RPC_ERROR OtxStatus = "FAIL_UNKNOWN_RPC_ERROR"
|
||||
REVERTED OtxStatus = "REVERTED"
|
||||
|
||||
GIFT_GAS OtxType = "GIFT_GAS"
|
||||
ACCOUNT_REGISTER OtxType = "ACCOUNT_REGISTER"
|
||||
GIFT_VOUCHER OtxType = "GIFT_VOUCHER"
|
||||
REFILL_GAS OtxType = "REFILL_GAS"
|
||||
TRANSFER_AUTH OtxType = "TRANSFER_AUTHORIZATION"
|
||||
TRANSFER_VOUCHER OtxType = "TRANSFER_VOUCHER"
|
||||
)
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/jackc/tern/v2/migrate"
|
||||
)
|
||||
|
||||
const (
|
||||
schemaTable = "schema_version"
|
||||
)
|
||||
|
||||
type PostgresPoolOpts struct {
|
||||
DSN string
|
||||
MigrationsFolderPath string
|
||||
}
|
||||
|
||||
// NewPostgresPool creates a reusbale connection pool across the cic-custodial component.
|
||||
func NewPostgresPool(ctx context.Context, o PostgresPoolOpts) (*pgxpool.Pool, error) {
|
||||
parsedConfig, err := pgxpool.ParseConfig(o.DSN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbPool, err := pgxpool.NewWithConfig(ctx, parsedConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := dbPool.Acquire(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Release()
|
||||
|
||||
migrator, err := migrate.NewMigrator(ctx, conn.Conn(), schemaTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := migrator.LoadMigrations(os.DirFS(o.MigrationsFolderPath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := migrator.Migrate(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbPool, nil
|
||||
}
|
|
@ -2,19 +2,25 @@ 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 {
|
||||
DSN string
|
||||
MinIdleConns int
|
||||
}
|
||||
const (
|
||||
systemGlobalLockKey = "system:global_lock"
|
||||
)
|
||||
|
||||
type RedisPool struct {
|
||||
Client *redis.Client
|
||||
}
|
||||
type (
|
||||
RedisPoolOpts struct {
|
||||
DSN string
|
||||
MinIdleConns int
|
||||
}
|
||||
|
||||
RedisPool struct {
|
||||
Client *redis.Client
|
||||
}
|
||||
)
|
||||
|
||||
// NewRedisPool creates a reusable connection across the cic-custodial componenent.
|
||||
// Note: Each db namespace requires its own connection pool.
|
||||
|
@ -28,11 +34,10 @@ func NewRedisPool(ctx context.Context, o RedisPoolOpts) (*RedisPool, error) {
|
|||
|
||||
redisClient := redis.NewClient(redisOpts)
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
60
queries.sql
60
queries.sql
|
@ -9,11 +9,6 @@ INSERT INTO keystore(public_key, private_key) VALUES($1, $2) RETURNING id
|
|||
-- $1: public_key
|
||||
SELECT private_key FROM keystore WHERE public_key=$1
|
||||
|
||||
--name: activate-account
|
||||
-- Activate an account following successful quorum
|
||||
-- $1: public_key
|
||||
UPDATE keystore SET active = true WHERE public_key=$1
|
||||
|
||||
--name: create-otx
|
||||
-- Create a new locally originating tx
|
||||
-- $1: tracking_id
|
||||
|
@ -39,6 +34,18 @@ INSERT INTO otx_sign(
|
|||
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
|
||||
|
@ -48,7 +55,7 @@ INSERT INTO otx_dispatch(
|
|||
"status"
|
||||
) VALUES($1, $2) RETURNING id
|
||||
|
||||
--name: update-chain-status
|
||||
--name: update-dispatch-status
|
||||
-- Updates the status of the dispatched tx with the chain mine status
|
||||
-- $1: tx_hash
|
||||
-- $2: status
|
||||
|
@ -60,45 +67,30 @@ UPDATE otx_dispatch SET "status" = $2, "block" = $3 WHERE otx_dispatch.id = (
|
|||
AND otx_dispatch.status = 'IN_NETWORK'
|
||||
)
|
||||
|
||||
--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
|
||||
|
||||
-- TODO: Scroll by status type with cursor pagination
|
||||
|
||||
--name: get-account-activation-quorum
|
||||
-- Gets quorum of required and confirmed system transactions for the account
|
||||
-- $1: tracking_id
|
||||
SELECT count(*) FROM otx_dispatch INNER JOIN
|
||||
otx_sign ON otx_dispatch.otx_id = otx_sign.id
|
||||
WHERE otx_sign.tracking_id=$1
|
||||
AND otx_dispatch.status = 'SUCCESS'
|
||||
--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 quota for an individual account by address
|
||||
-- Gets current gas lock and activation status for an individual account by address
|
||||
-- $1: public_key
|
||||
SELECT keystore.active, gas_quota.quota FROM keystore
|
||||
INNER JOIN gas_quota ON keystore.id = gas_quota.key_id
|
||||
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: decr-gas-quota
|
||||
-- Consumes a gas quota
|
||||
--name: acc-gas-lock
|
||||
-- Locks an account for gas reasons
|
||||
-- $1: public_key
|
||||
UPDATE gas_quota SET quota = quota - 1 WHERE key_id = (
|
||||
UPDATE gas_lock SET lock = true WHERE key_id = (
|
||||
SELECT id FROM keystore
|
||||
WHERE public_key=$1
|
||||
)
|
||||
|
||||
--name: reset-gas-quota
|
||||
-- Resets the gas quota
|
||||
-- 25 is the agreed upon quota
|
||||
--name: acc-gas-unlock
|
||||
-- Unlocks an account for gas reasons
|
||||
-- $1: public_key
|
||||
UPDATE gas_quota SET quota = gas_quota_meta.default_quota
|
||||
FROM gas_quota_meta
|
||||
WHERE key_id = (
|
||||
UPDATE gas_lock SET lock = false WHERE key_id = (
|
||||
SELECT id FROM keystore
|
||||
WHERE public_key=$1
|
||||
)
|
Loading…
Reference in New Issue