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:
Mohamed Sohail 2023-02-21 17:34:22 +00:00
parent 140c887341
commit 40cb86f522
Signed by: kamikazechaser
GPG Key ID: 7DD45520C01CD85D
22 changed files with 314 additions and 147 deletions

View File

@ -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
}

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View 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,
},
})
}
}

View File

@ -5,10 +5,8 @@ type EventEmitter interface {
Publish(subject string, dedupId string, eventPayload interface{}) error
}
type (
EventPayload struct {
type EventPayload struct {
OtxId uint `json:"otxId"`
TrackingId string `json:"trackingId"`
TxHash string `json:"txHash"`
}
)
}

View File

@ -10,10 +10,11 @@ type Queries struct {
// Keystore
WriteKeyPair string `query:"write-key-pair"`
LoadKeyPair string `query:"load-key-pair"`
// OTX
// Store
CreateOTX string `query:"create-otx"`
// Dispatch
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) {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
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
}
)

View File

@ -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,14 +68,16 @@ func AccountGiftGasProcessor(cu *custodial.Custodial) func(context.Context, *asy
return err
}
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
TrackingId: payload.TrackingId,
Type: "GIFT_GAS",
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 {

View File

@ -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,14 +76,16 @@ func GiftVoucherProcessor(cu *custodial.Custodial) func(context.Context, *asynq.
return err
}
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
TrackingId: payload.TrackingId,
Type: "GIFT_VOUCHER",
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 {

View File

@ -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,14 +83,16 @@ func AccountRefillGasProcessor(cu *custodial.Custodial) func(context.Context, *a
return err
}
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
TrackingId: payload.TrackingId,
Type: "REFILL_GAS",
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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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,14 +98,16 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er
return err
}
id, err := cu.PgStore.CreateOTX(ctx, store.OTX{
id, err := cu.PgStore.CreateOtx(ctx, store.OTX{
TrackingId: payload.TrackingId,
Type: "TRANSFER",
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 {

View File

@ -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
View 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"
)

View File

@ -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"
)

View File

@ -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
View 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