mirror of
https://github.com/grassrootseconomics/cic-custodial.git
synced 2024-11-21 13:56:47 +01:00
feat: add otx tracking API, add enums, minor fixes
* otx can nw be tracked at /api/track/:trackingId * moved queries to queries folder * fixed validation error check in ErrorHandler * added enum package with enum types * updated migrations: added enum tables
This commit is contained in:
parent
140c887341
commit
40cb86f522
@ -30,7 +30,7 @@ func initApiServer(custodialContainer *custodial.Custodial) *echo.Echo {
|
||||
return
|
||||
}
|
||||
|
||||
if err.(validator.ValidationErrors) != nil {
|
||||
if _, ok := err.(validator.ValidationErrors); ok {
|
||||
c.JSON(http.StatusForbidden, api.ErrResp{
|
||||
Ok: false,
|
||||
Code: api.VALIDATION_ERROR,
|
||||
@ -66,6 +66,7 @@ func initApiServer(custodialContainer *custodial.Custodial) *echo.Echo {
|
||||
apiRoute := server.Group("/api")
|
||||
apiRoute.POST("/account/create", api.CreateAccountHandler(custodialContainer))
|
||||
apiRoute.POST("/sign/transfer", api.SignTransferHandler(custodialContainer))
|
||||
apiRoute.GET("/track/:trackingId", api.TxStatus(custodialContainer.PgStore))
|
||||
|
||||
return server
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ var (
|
||||
func init() {
|
||||
flag.StringVar(&confFlag, "config", "config.toml", "Config file location")
|
||||
flag.BoolVar(&debugFlag, "log", false, "Enable debug logging")
|
||||
flag.StringVar(&queriesFlag, "queries", "queries.sql", "Queries file location")
|
||||
flag.StringVar(&queriesFlag, "queries", "queries/queries.sql", "Queries file location")
|
||||
flag.Parse()
|
||||
|
||||
lo = initLogger(debugFlag)
|
||||
|
2
go.mod
2
go.mod
@ -6,6 +6,7 @@ require (
|
||||
github.com/VictoriaMetrics/metrics v1.23.1
|
||||
github.com/bsm/redislock v0.7.2
|
||||
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/google/uuid v1.3.0
|
||||
@ -70,7 +71,6 @@ require (
|
||||
github.com/prometheus/tsdb v0.10.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
|
7
go.sum
7
go.sum
@ -136,6 +136,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs=
|
||||
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
|
||||
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@ -175,6 +176,8 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU=
|
||||
github.com/georgysavva/scany/v2 v2.0.0/go.mod h1:sigOdh+0qb/+aOs3TVhehVT10p8qJL7K/Zhyz8vWo38=
|
||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@ -218,6 +221,7 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
@ -424,6 +428,7 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2
|
||||
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/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=
|
||||
@ -572,7 +577,6 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
@ -602,6 +606,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
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=
|
||||
|
@ -29,7 +29,7 @@ func SignTransferHandler(cu *custodial.Custodial) func(echo.Context) error {
|
||||
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 int64 `json:"amount" validate:"required,numeric"`
|
||||
Amount uint64 `json:"amount" validate:"required,numeric"`
|
||||
}
|
||||
|
||||
if err := c.Bind(&transferRequest); err != nil {
|
||||
|
37
internal/api/track.go
Normal file
37
internal/api/track.go
Normal file
@ -0,0 +1,37 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func TxStatus(store store.Store) 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 err
|
||||
}
|
||||
|
||||
if err := c.Validate(txStatusRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: handle potential timeouts
|
||||
txs, err := store.GetTxStatusByTrackingId(c.Request().Context(), txStatusRequest.TrackingId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, OkResp{
|
||||
Ok: true,
|
||||
Result: H{
|
||||
"transactions": txs,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -5,10 +5,8 @@ type EventEmitter interface {
|
||||
Publish(subject string, dedupId string, eventPayload interface{}) error
|
||||
}
|
||||
|
||||
type (
|
||||
EventPayload struct {
|
||||
OtxId uint `json:"otxId"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
TxHash string `json:"txHash"`
|
||||
}
|
||||
)
|
||||
type EventPayload struct {
|
||||
OtxId uint `json:"otxId"`
|
||||
TrackingId string `json:"trackingId"`
|
||||
TxHash string `json:"txHash"`
|
||||
}
|
||||
|
@ -10,10 +10,11 @@ type Queries struct {
|
||||
// Keystore
|
||||
WriteKeyPair string `query:"write-key-pair"`
|
||||
LoadKeyPair string `query:"load-key-pair"`
|
||||
// OTX
|
||||
CreateOTX string `query:"create-otx"`
|
||||
// Dispatch
|
||||
CreateDispatchStatus string `query:"create-dispatch-status"`
|
||||
// Store
|
||||
CreateOTX string `query:"create-otx"`
|
||||
CreateDispatchStatus string `query:"create-dispatch-status"`
|
||||
UpdateChainStatus string `query:"update-chain-status"`
|
||||
GetTxStatusByTrackingId string `query:"get-tx-status-by-tracking-id"`
|
||||
}
|
||||
|
||||
func LoadQueries(q goyesql.Queries) (*Queries, error) {
|
||||
|
@ -2,9 +2,9 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Status string
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
)
|
||||
|
||||
func (s *PostgresStore) CreateDispatchStatus(ctx context.Context, dispatch DispatchStatus) error {
|
||||
if _, err := s.db.Exec(
|
||||
@ -18,3 +18,17 @@ func (s *PostgresStore) CreateDispatchStatus(ctx context.Context, dispatch Dispa
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) UpdateChainStatus(ctx context.Context, txHash string, status enum.OtxStatus, block uint64) error {
|
||||
if _, err := s.db.Exec(
|
||||
ctx,
|
||||
s.queries.UpdateChainStatus,
|
||||
txHash,
|
||||
status,
|
||||
block,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,8 +1,21 @@
|
||||
package store
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
func (s *PostgresStore) CreateOTX(ctx context.Context, otx OTX) (uint, error) {
|
||||
"github.com/georgysavva/scany/v2/pgxscan"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
func (s *PostgresStore) CreateOtx(ctx context.Context, otx OTX) (uint, error) {
|
||||
var (
|
||||
id uint
|
||||
)
|
||||
@ -17,6 +30,8 @@ func (s *PostgresStore) CreateOTX(ctx context.Context, otx OTX) (uint, error) {
|
||||
otx.From,
|
||||
otx.Data,
|
||||
otx.GasPrice,
|
||||
otx.GasLimit,
|
||||
otx.TransferValue,
|
||||
otx.Nonce,
|
||||
).Scan(&id); err != nil {
|
||||
return id, err
|
||||
@ -24,3 +39,21 @@ func (s *PostgresStore) CreateOTX(ctx context.Context, otx OTX) (uint, error) {
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) GetTxStatusByTrackingId(ctx context.Context, trackingId string) ([]*TxStatus, error) {
|
||||
var (
|
||||
txs []*TxStatus
|
||||
)
|
||||
|
||||
if err := pgxscan.Select(
|
||||
ctx,
|
||||
s.db,
|
||||
&txs,
|
||||
s.queries.GetTxStatusByTrackingId,
|
||||
trackingId,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txs, nil
|
||||
}
|
||||
|
@ -3,30 +3,32 @@ package store
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/status"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
)
|
||||
|
||||
type (
|
||||
OTX struct {
|
||||
TrackingId string
|
||||
Type string
|
||||
RawTx string
|
||||
TxHash string
|
||||
From string
|
||||
Data string
|
||||
GasPrice uint64
|
||||
Nonce uint64
|
||||
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 status.Status
|
||||
Status enum.OtxStatus
|
||||
}
|
||||
|
||||
Store interface {
|
||||
// OTX (Custodial originating transactions).
|
||||
CreateOTX(ctx context.Context, otx OTX) (id uint, err error)
|
||||
// Dispatch status.
|
||||
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)
|
||||
UpdateChainStatus(ctx context.Context, txHash string, status enum.OtxStatus, block uint64) error
|
||||
}
|
||||
)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/events"
|
||||
"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"
|
||||
)
|
||||
@ -67,15 +68,17 @@ func AccountGiftGasProcessor(cu *custodial.Custodial) func(context.Context, *asy
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: "GIFT_GAS",
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
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
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/events"
|
||||
"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"
|
||||
)
|
||||
@ -75,15 +76,17 @@ func GiftVoucherProcessor(cu *custodial.Custodial) func(context.Context, *asynq.
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: "GIFT_VOUCHER",
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: enum.ACCOUNT_REGISTER,
|
||||
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 {
|
||||
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/events"
|
||||
"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"
|
||||
@ -82,15 +83,17 @@ func AccountRefillGasProcessor(cu *custodial.Custodial) func(context.Context, *a
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: "REFILL_GAS",
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
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(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/events"
|
||||
"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"
|
||||
)
|
||||
@ -76,14 +77,15 @@ func AccountRegisterOnChainProcessor(cu *custodial.Custodial) func(context.Conte
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
|
||||
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: "ACCOUNT_REGISTER",
|
||||
Type: enum.ACCOUNT_REGISTER,
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: cu.SystemContainer.PublicKey,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/custodial"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/events"
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/store"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/status"
|
||||
"github.com/grassrootseconomics/cic-custodial/pkg/enum"
|
||||
"github.com/grassrootseconomics/w3-celo-patch/module/eth"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
@ -43,15 +43,15 @@ func DispatchTx(cu *custodial.Custodial) func(context.Context, *asynq.Task) erro
|
||||
ctx,
|
||||
eth.SendTx(payload.Tx).Returns(&dispathchTx),
|
||||
); err != nil {
|
||||
dispatchStatus.Status = status.Unknown
|
||||
|
||||
switch err.Error() {
|
||||
case celoutils.ErrGasPriceLow:
|
||||
dispatchStatus.Status = status.FailGasPrice
|
||||
dispatchStatus.Status = enum.FAIL_LOW_GAS_PRICE
|
||||
case celoutils.ErrInsufficientGas:
|
||||
dispatchStatus.Status = status.FailInsufficientGas
|
||||
dispatchStatus.Status = enum.FAIL_NO_GAS
|
||||
case celoutils.ErrNonceLow:
|
||||
dispatchStatus.Status = status.FailNonce
|
||||
dispatchStatus.Status = enum.FAIL_LOW_NONCE
|
||||
default:
|
||||
dispatchStatus.Status = enum.FAIL_UNKNOWN_RPC_ERROR
|
||||
}
|
||||
|
||||
if err := cu.PgStore.CreateDispatchStatus(ctx, dispatchStatus); err != nil {
|
||||
@ -65,7 +65,7 @@ func DispatchTx(cu *custodial.Custodial) func(context.Context, *asynq.Task) erro
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
}
|
||||
|
||||
dispatchStatus.Status = status.Successful
|
||||
dispatchStatus.Status = enum.IN_NETWORK
|
||||
|
||||
if err := cu.PgStore.CreateDispatchStatus(ctx, dispatchStatus); err != nil {
|
||||
return fmt.Errorf("dispatch: failed %v: %w", err, asynq.SkipRetry)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/grassrootseconomics/cic-custodial/internal/events"
|
||||
"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"
|
||||
)
|
||||
@ -22,7 +23,7 @@ type (
|
||||
From string `json:"from" `
|
||||
To string `json:"to"`
|
||||
VoucherAddress string `json:"voucherAddress"`
|
||||
Amount int64 `json:"amount"`
|
||||
Amount uint64 `json:"amount"`
|
||||
}
|
||||
|
||||
transferEventPayload struct {
|
||||
@ -71,7 +72,7 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
|
||||
}
|
||||
}()
|
||||
|
||||
input, err := cu.SystemContainer.Abis["transfer"].EncodeArgs(w3.A(payload.To), big.NewInt(payload.Amount))
|
||||
input, err := cu.SystemContainer.Abis["transfer"].EncodeArgs(w3.A(payload.To), new(big.Int).SetUint64(payload.Amount))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -97,15 +98,17 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
|
||||
TrackingId: payload.TrackingId,
|
||||
Type: "TRANSFER",
|
||||
RawTx: hexutil.Encode(rawTx),
|
||||
TxHash: builtTx.Hash().Hex(),
|
||||
From: payload.From,
|
||||
Data: hexutil.Encode(builtTx.Data()),
|
||||
GasPrice: builtTx.GasPrice().Uint64(),
|
||||
Nonce: builtTx.Nonce(),
|
||||
id, err := cu.PgStore.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(),
|
||||
GasLimit: builtTx.Gas(),
|
||||
TransferValue: payload.Amount,
|
||||
Nonce: builtTx.Nonce(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1,23 +1,53 @@
|
||||
-- Otx tx type enum table
|
||||
CREATE TABLE IF NOT EXISTS otx_tx_type (
|
||||
value TEXT PRIMARY KEY
|
||||
);
|
||||
INSERT INTO otx_tx_type (value) VALUES
|
||||
('GIFT_GAS'),
|
||||
('ACCOUNT_REGISTER'),
|
||||
('GIFT_VOUCHER'),
|
||||
('REFILL_GAS'),
|
||||
('TRANSFER_VOUCHER');
|
||||
|
||||
-- Origin tx table
|
||||
CREATE TABLE IF NOT EXISTS otx (
|
||||
CREATE TABLE IF NOT EXISTS otx_sign (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
tracking_id TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"type" TEXT REFERENCES otx_tx_type(value) NOT NULL,
|
||||
raw_tx TEXT NOT NULL,
|
||||
tx_hash TEXT NOT NULL,
|
||||
"from" TEXT NOT NULL,
|
||||
"data" TEXT NOT NULL,
|
||||
gas_price bigint NOT NULL,
|
||||
gas_limit bigint NOT NULL,
|
||||
transfer_value bigint NOT NULL,
|
||||
nonce int NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tx_hash_idx ON otx USING hash(tx_hash);
|
||||
CREATE INDEX IF NOT EXISTS from_idx ON otx USING hash("from");
|
||||
CREATE INDEX IF NOT EXISTS tx_hash_idx ON otx_sign USING hash(tx_hash);
|
||||
CREATE INDEX IF NOT EXISTS from_idx ON otx_sign USING hash("from");
|
||||
|
||||
-- Otx dispatch status enum table
|
||||
-- Enforces referential integrity on the dispatch table
|
||||
CREATE TABLE IF NOT EXISTS otx_dispatch_status_type (
|
||||
value TEXT PRIMARY KEY
|
||||
);
|
||||
INSERT INTO otx_dispatch_status_type (value) VALUES
|
||||
('IN_NETWORK'),
|
||||
('OBSOLETE'),
|
||||
('SUCCESS'),
|
||||
('FAIL_NO_GAS'),
|
||||
('FAIL_LOW_NONCE'),
|
||||
('FAIL_LOW_GAS_PRICE'),
|
||||
('FAIL_UNKNOWN_RPC_ERROR'),
|
||||
('REVERTED');
|
||||
|
||||
-- Dispatch status table
|
||||
CREATE TABLE IF NOT EXISTS dispatch (
|
||||
CREATE TABLE IF NOT EXISTS otx_dispatch (
|
||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
otx_id INT REFERENCES otx(id),
|
||||
"status" TEXT NOT NULL,
|
||||
otx_id INT REFERENCES otx_sign(id),
|
||||
"status" TEXT REFERENCES otx_dispatch_status_type(value) NOT NULL,
|
||||
"block" bigint,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS status_idx ON otx_dispatch("status");
|
||||
|
27
pkg/enum/enum.go
Normal file
27
pkg/enum/enum.go
Normal file
@ -0,0 +1,27 @@
|
||||
package enum
|
||||
|
||||
type (
|
||||
// OtxStatus represents enum-like values received in the dispatcher from the RPC node or Network callback.
|
||||
// It includes a subset of well-known and likely failures the dispatcher may encounter.
|
||||
OtxStatus string
|
||||
// OtxType reprsents the specific type of signed transaction.
|
||||
OtxType string
|
||||
)
|
||||
|
||||
// NOTE: These values must also be inserted/updated into db to enforce referential integrity.
|
||||
const (
|
||||
IN_NETWORK OtxStatus = "IN_NETWORK"
|
||||
OBSOLETE OtxStatus = "OBSOLETE"
|
||||
SUCCESS OtxStatus = "SUCCESS"
|
||||
FAIL_NO_GAS OtxStatus = "FAIL_NO_GAS"
|
||||
FAIL_LOW_NONCE OtxStatus = "FAIL_LOW_NONCE"
|
||||
FAIL_LOW_GAS_PRICE OtxStatus = "FAIL_LOW_GAS_PRICE"
|
||||
FAIL_UNKNOWN_RPC_ERROR OtxStatus = "FAIL_UNKNOWN_RPC_ERROR"
|
||||
REVERTED OtxStatus = "REVERTED"
|
||||
|
||||
GIFT_GAS OtxType = "GIFT_GAS"
|
||||
ACCOUNT_REGISTER OtxType = "ACCOUNT_REGISTER"
|
||||
GIFT_VOUCHER OtxType = "GIFT_VOUCHER"
|
||||
REFILL_GAS OtxType = "REFILL_GAS"
|
||||
TRANSFER_VOUCHER OtxType = "TRANSFER_VOUCHER"
|
||||
)
|
@ -1,13 +0,0 @@
|
||||
package status
|
||||
|
||||
// Status represents enum-like values received in the dispatcher from the RPC node.
|
||||
// It includes a subset of well-known and likely failures the dispatcher may encounter.
|
||||
type Status string
|
||||
|
||||
const (
|
||||
FailGasPrice = "FAIL_LOW_GAS_PRICE"
|
||||
FailInsufficientGas = "FAIL_NO_GAS"
|
||||
FailNonce = "FAIL_LOW_NONCE"
|
||||
Successful = "SUCCESSFUL"
|
||||
Unknown = "UNKNOWN"
|
||||
)
|
47
queries.sql
47
queries.sql
@ -1,47 +0,0 @@
|
||||
-- Keystore queries
|
||||
|
||||
--name: write-key-pair
|
||||
-- Save hex encoded private key
|
||||
-- $1: public_key
|
||||
-- $2: private_key
|
||||
INSERT INTO keystore(public_key, private_key) VALUES($1, $2) RETURNING id
|
||||
|
||||
--name: load-key-pair
|
||||
-- Load saved key pair
|
||||
-- $1: public_key
|
||||
SELECT private_key FROM keystore WHERE public_key=$1
|
||||
|
||||
-- OTX queries
|
||||
|
||||
--name: create-otx
|
||||
-- Create a new locally originating tx
|
||||
-- $1: tracking_id
|
||||
-- $2: type
|
||||
-- $3: raw_tx
|
||||
-- $4: tx_hash
|
||||
-- $5: from
|
||||
-- $6: data
|
||||
-- $7: gas_price
|
||||
-- $8: nonce
|
||||
INSERT INTO otx(
|
||||
tracking_id,
|
||||
"type",
|
||||
raw_tx,
|
||||
tx_hash,
|
||||
"from",
|
||||
"data",
|
||||
gas_price,
|
||||
nonce
|
||||
) VALUES($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id
|
||||
|
||||
|
||||
-- Dispatch status queries
|
||||
|
||||
--name: create-dispatch-status
|
||||
-- Create a new dispatch status
|
||||
-- $1: otx_id
|
||||
-- $2: status
|
||||
INSERT INTO dispatch(
|
||||
otx_id,
|
||||
"status"
|
||||
) VALUES($1, $2) RETURNING id
|
65
queries/queries.sql
Normal file
65
queries/queries.sql
Normal file
@ -0,0 +1,65 @@
|
||||
--name: write-key-pair
|
||||
-- Save hex encoded private key
|
||||
-- $1: public_key
|
||||
-- $2: private_key
|
||||
INSERT INTO keystore(public_key, private_key) VALUES($1, $2) RETURNING id
|
||||
|
||||
--name: load-key-pair
|
||||
-- Load saved key pair
|
||||
-- $1: public_key
|
||||
SELECT private_key FROM keystore WHERE public_key=$1
|
||||
|
||||
--name: create-otx
|
||||
-- Create a new locally originating tx
|
||||
-- $1: tracking_id
|
||||
-- $2: type
|
||||
-- $3: raw_tx
|
||||
-- $4: tx_hash
|
||||
-- $5: from
|
||||
-- $6: data
|
||||
-- $7: gas_price
|
||||
-- $8: gas_limit
|
||||
-- $9: transfer_value
|
||||
-- $10: nonce
|
||||
INSERT INTO otx_sign(
|
||||
tracking_id,
|
||||
"type",
|
||||
raw_tx,
|
||||
tx_hash,
|
||||
"from",
|
||||
"data",
|
||||
gas_price,
|
||||
gas_limit,
|
||||
transfer_value,
|
||||
nonce
|
||||
) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id
|
||||
|
||||
--name: create-dispatch-status
|
||||
-- Create a new dispatch status
|
||||
-- $1: otx_id
|
||||
-- $2: status
|
||||
INSERT INTO otx_dispatch(
|
||||
otx_id,
|
||||
"status"
|
||||
) VALUES($1, $2) RETURNING id
|
||||
|
||||
--name: update-chain-status
|
||||
-- Updates the status of the dispatched tx with the chain mine status
|
||||
-- $1: tx_hash
|
||||
-- $2: status
|
||||
-- $3: block
|
||||
UPDATE otx_dispatch SET "status" = $2, "block" = $3 WHERE otx_dispatch.id = (
|
||||
SELECT otx_dispatch.id FROM otx_dispatch
|
||||
INNER JOIN otx_sign ON otx_dispatch.otx_id = otx_sign.id
|
||||
WHERE otx_sign.tx_hash = $1
|
||||
AND otx_dispatch.status = 'IN_NETWORK'
|
||||
)
|
||||
|
||||
--name: 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
|
Loading…
Reference in New Issue
Block a user