From 4791a6422dd5b5a29f6fb036b73f3717671519aa Mon Sep 17 00:00:00 2001 From: Wilfred Asomani Date: Mon, 15 May 2023 15:14:30 +0000 Subject: [PATCH] satellite/console: add STORJ bonus to transactions This change includes STORJ bonuses to the list of transactions returned by the /wallet/payments endpoint. Issue: https://github.com/storj/storj/issues/5755 Change-Id: Icc95c2cb9dd9fc5ee7a373e68c1cf8a991e1aa58 --- satellite/console/service.go | 25 +++++ satellite/console/service_test.go | 39 +++++++ satellite/payments/billing/transactions.go | 2 + satellite/satellitedb/billingdb.go | 19 ++++ satellite/satellitedb/dbx/billing.dbx | 7 ++ satellite/satellitedb/dbx/satellitedb.dbx.go | 108 +++++++++++++++++++ 6 files changed, 200 insertions(+) diff --git a/satellite/console/service.go b/satellite/console/service.go index fe70f4f4a..c1d4c9ae2 100644 --- a/satellite/console/service.go +++ b/satellite/console/service.go @@ -4,8 +4,10 @@ package console import ( + "bytes" "context" "database/sql" + "encoding/json" "errors" "fmt" "math" @@ -3082,6 +3084,10 @@ func (payment Payments) WalletPayments(ctx context.Context) (_ WalletPayments, e if err != nil { return WalletPayments{}, Error.Wrap(err) } + txns, err := payment.service.billing.ListSource(ctx, user.ID, billing.StorjScanBonusSource) + if err != nil { + return WalletPayments{}, Error.Wrap(err) + } var paymentInfos []PaymentInfo for _, walletPayment := range walletPayments { @@ -3107,6 +3113,25 @@ func (payment Payments) WalletPayments(ctx context.Context) (_ WalletPayments, e Timestamp: txInfo.CreatedAt.UTC(), }) } + for _, txn := range txns { + var meta struct { + ReferenceID string + LogIndex int + } + err = json.NewDecoder(bytes.NewReader(txn.Metadata)).Decode(&meta) + if err != nil { + return WalletPayments{}, Error.Wrap(err) + } + paymentInfos = append(paymentInfos, PaymentInfo{ + ID: fmt.Sprintf("%s#%d", meta.ReferenceID, meta.LogIndex), + Type: txn.Source, + Wallet: address.Hex(), + Amount: txn.Amount, + Status: string(txn.Status), + Link: EtherscanURL(meta.ReferenceID), + Timestamp: txn.Timestamp, + }) + } return WalletPayments{ Payments: paymentInfos, diff --git a/satellite/console/service_test.go b/satellite/console/service_test.go index 3a708b424..37a750075 100644 --- a/satellite/console/service_test.go +++ b/satellite/console/service_test.go @@ -4,6 +4,7 @@ package console_test import ( + "bytes" "context" "database/sql" "encoding/json" @@ -30,6 +31,7 @@ import ( "storj.io/storj/satellite/buckets" "storj.io/storj/satellite/console" "storj.io/storj/satellite/payments" + "storj.io/storj/satellite/payments/billing" "storj.io/storj/satellite/payments/coinpayments" "storj.io/storj/satellite/payments/storjscan" "storj.io/storj/satellite/payments/storjscan/blockchaintest" @@ -1485,6 +1487,12 @@ func TestDeleteAllSessionsByUserIDExcept(t *testing.T) { func TestPaymentsWalletPayments(t *testing.T) { testplanet.Run(t, testplanet.Config{ SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0, + Reconfigure: testplanet.Reconfigure{ + Satellite: func(log *zap.Logger, index int, config *satellite.Config) { + config.Payments.BillingConfig.DisableLoop = false + config.Payments.BonusRate = 10 + }, + }, }, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) { sat := planet.Satellites[0] now := time.Now().Truncate(time.Second).UTC() @@ -1574,6 +1582,37 @@ func TestPaymentsWalletPayments(t *testing.T) { }) } + // get billing chore to insert bonuses for transactions. + sat.Core.Payments.BillingChore.TransactionCycle.TriggerWait() + + txns, err := sat.DB.Billing().ListSource(ctx, user.ID, billing.StorjScanBonusSource) + require.NoError(t, err) + require.NotEmpty(t, txns) + + for _, txn := range txns { + if txn.Source != billing.StorjScanBonusSource { + continue + } + var meta struct { + ReferenceID string + Wallet string + LogIndex int + } + err = json.NewDecoder(bytes.NewReader(txn.Metadata)).Decode(&meta) + if err != nil { + continue + } + expected = append(expected, console.PaymentInfo{ + ID: fmt.Sprintf("%s#%d", meta.ReferenceID, meta.LogIndex), + Type: txn.Source, + Wallet: meta.Wallet, + Amount: txn.Amount, + Status: string(txn.Status), + Link: console.EtherscanURL(meta.ReferenceID), + Timestamp: txn.Timestamp, + }) + } + walletPayments, err := sat.API.Console.Service.Payments().WalletPayments(reqCtx) require.NoError(t, err) require.Equal(t, expected, walletPayments.Payments) diff --git a/satellite/payments/billing/transactions.go b/satellite/payments/billing/transactions.go index 218a7db76..7f8f114f7 100644 --- a/satellite/payments/billing/transactions.go +++ b/satellite/payments/billing/transactions.go @@ -65,6 +65,8 @@ type TransactionsDB interface { LastTransaction(ctx context.Context, txSource string, txType TransactionType) (time.Time, []byte, error) // List returns all transactions for the specified user. List(ctx context.Context, userID uuid.UUID) ([]Transaction, error) + // ListSource returns all transactions for the specified user and source. + ListSource(ctx context.Context, userID uuid.UUID, txSource string) ([]Transaction, error) // GetBalance returns the current usable balance for the specified user. GetBalance(ctx context.Context, userID uuid.UUID) (currency.Amount, error) } diff --git a/satellite/satellitedb/billingdb.go b/satellite/satellitedb/billingdb.go index 34dfcd366..59115f714 100644 --- a/satellite/satellitedb/billingdb.go +++ b/satellite/satellitedb/billingdb.go @@ -235,6 +235,25 @@ func (db billingDB) List(ctx context.Context, userID uuid.UUID) (txs []billing.T return txs, nil } +func (db billingDB) ListSource(ctx context.Context, userID uuid.UUID, txSource string) (txs []billing.Transaction, err error) { + defer mon.Task()(&ctx)(&err) + dbxTXs, err := db.db.All_BillingTransaction_By_UserId_And_Source_OrderBy_Desc_Timestamp(ctx, + dbx.BillingTransaction_UserId(userID[:]), dbx.BillingTransaction_Source(txSource)) + if err != nil { + return nil, Error.Wrap(err) + } + + for _, dbxTX := range dbxTXs { + tx, err := fromDBXBillingTransaction(dbxTX) + if err != nil { + return nil, Error.Wrap(err) + } + txs = append(txs, *tx) + } + + return txs, nil +} + func (db billingDB) GetBalance(ctx context.Context, userID uuid.UUID) (_ currency.Amount, err error) { defer mon.Task()(&ctx)(&err) dbxBilling, err := db.db.Get_BillingBalance_Balance_By_UserId(ctx, diff --git a/satellite/satellitedb/dbx/billing.dbx b/satellite/satellitedb/dbx/billing.dbx index c3e0219ff..4b7d33992 100644 --- a/satellite/satellitedb/dbx/billing.dbx +++ b/satellite/satellitedb/dbx/billing.dbx @@ -110,6 +110,13 @@ read all ( orderby desc billing_transaction.timestamp ) +read all ( + select billing_transaction + where billing_transaction.user_id = ? + where billing_transaction.source = ? + orderby desc billing_transaction.timestamp +) + read first ( select billing_transaction where billing_transaction.source = ? diff --git a/satellite/satellitedb/dbx/satellitedb.dbx.go b/satellite/satellitedb/dbx/satellitedb.dbx.go index c03b0f6d7..085052f87 100644 --- a/satellite/satellitedb/dbx/satellitedb.dbx.go +++ b/satellite/satellitedb/dbx/satellitedb.dbx.go @@ -14005,6 +14005,52 @@ func (obj *pgxImpl) All_BillingTransaction_By_UserId_OrderBy_Desc_Timestamp(ctx } +func (obj *pgxImpl) All_BillingTransaction_By_UserId_And_Source_OrderBy_Desc_Timestamp(ctx context.Context, + billing_transaction_user_id BillingTransaction_UserId_Field, + billing_transaction_source BillingTransaction_Source_Field) ( + rows []*BillingTransaction, err error) { + defer mon.Task()(&ctx)(&err) + + var __embed_stmt = __sqlbundle_Literal("SELECT billing_transactions.id, billing_transactions.user_id, billing_transactions.amount, billing_transactions.currency, billing_transactions.description, billing_transactions.source, billing_transactions.status, billing_transactions.type, billing_transactions.metadata, billing_transactions.timestamp, billing_transactions.created_at FROM billing_transactions WHERE billing_transactions.user_id = ? AND billing_transactions.source = ? ORDER BY billing_transactions.timestamp DESC") + + var __values []interface{} + __values = append(__values, billing_transaction_user_id.value(), billing_transaction_source.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + for { + rows, err = func() (rows []*BillingTransaction, err error) { + __rows, err := obj.driver.QueryContext(ctx, __stmt, __values...) + if err != nil { + return nil, err + } + defer __rows.Close() + + for __rows.Next() { + billing_transaction := &BillingTransaction{} + err = __rows.Scan(&billing_transaction.Id, &billing_transaction.UserId, &billing_transaction.Amount, &billing_transaction.Currency, &billing_transaction.Description, &billing_transaction.Source, &billing_transaction.Status, &billing_transaction.Type, &billing_transaction.Metadata, &billing_transaction.Timestamp, &billing_transaction.CreatedAt) + if err != nil { + return nil, err + } + rows = append(rows, billing_transaction) + } + if err := __rows.Err(); err != nil { + return nil, err + } + return rows, nil + }() + if err != nil { + if obj.shouldRetry(err) { + continue + } + return nil, obj.makeErr(err) + } + return rows, nil + } + +} + func (obj *pgxImpl) First_BillingTransaction_By_Source_And_Type_OrderBy_Desc_CreatedAt(ctx context.Context, billing_transaction_source BillingTransaction_Source_Field, billing_transaction_type BillingTransaction_Type_Field) ( @@ -21818,6 +21864,52 @@ func (obj *pgxcockroachImpl) All_BillingTransaction_By_UserId_OrderBy_Desc_Times } +func (obj *pgxcockroachImpl) All_BillingTransaction_By_UserId_And_Source_OrderBy_Desc_Timestamp(ctx context.Context, + billing_transaction_user_id BillingTransaction_UserId_Field, + billing_transaction_source BillingTransaction_Source_Field) ( + rows []*BillingTransaction, err error) { + defer mon.Task()(&ctx)(&err) + + var __embed_stmt = __sqlbundle_Literal("SELECT billing_transactions.id, billing_transactions.user_id, billing_transactions.amount, billing_transactions.currency, billing_transactions.description, billing_transactions.source, billing_transactions.status, billing_transactions.type, billing_transactions.metadata, billing_transactions.timestamp, billing_transactions.created_at FROM billing_transactions WHERE billing_transactions.user_id = ? AND billing_transactions.source = ? ORDER BY billing_transactions.timestamp DESC") + + var __values []interface{} + __values = append(__values, billing_transaction_user_id.value(), billing_transaction_source.value()) + + var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt) + obj.logStmt(__stmt, __values...) + + for { + rows, err = func() (rows []*BillingTransaction, err error) { + __rows, err := obj.driver.QueryContext(ctx, __stmt, __values...) + if err != nil { + return nil, err + } + defer __rows.Close() + + for __rows.Next() { + billing_transaction := &BillingTransaction{} + err = __rows.Scan(&billing_transaction.Id, &billing_transaction.UserId, &billing_transaction.Amount, &billing_transaction.Currency, &billing_transaction.Description, &billing_transaction.Source, &billing_transaction.Status, &billing_transaction.Type, &billing_transaction.Metadata, &billing_transaction.Timestamp, &billing_transaction.CreatedAt) + if err != nil { + return nil, err + } + rows = append(rows, billing_transaction) + } + if err := __rows.Err(); err != nil { + return nil, err + } + return rows, nil + }() + if err != nil { + if obj.shouldRetry(err) { + continue + } + return nil, obj.makeErr(err) + } + return rows, nil + } + +} + func (obj *pgxcockroachImpl) First_BillingTransaction_By_Source_And_Type_OrderBy_Desc_CreatedAt(ctx context.Context, billing_transaction_source BillingTransaction_Source_Field, billing_transaction_type BillingTransaction_Type_Field) ( @@ -27473,6 +27565,17 @@ func (rx *Rx) All_AccountFreezeEvent_By_UserId(ctx context.Context, return tx.All_AccountFreezeEvent_By_UserId(ctx, account_freeze_event_user_id) } +func (rx *Rx) All_BillingTransaction_By_UserId_And_Source_OrderBy_Desc_Timestamp(ctx context.Context, + billing_transaction_user_id BillingTransaction_UserId_Field, + billing_transaction_source BillingTransaction_Source_Field) ( + rows []*BillingTransaction, err error) { + var tx *Tx + if tx, err = rx.getTx(ctx); err != nil { + return + } + return tx.All_BillingTransaction_By_UserId_And_Source_OrderBy_Desc_Timestamp(ctx, billing_transaction_user_id, billing_transaction_source) +} + func (rx *Rx) All_BillingTransaction_By_UserId_OrderBy_Desc_Timestamp(ctx context.Context, billing_transaction_user_id BillingTransaction_UserId_Field) ( rows []*BillingTransaction, err error) { @@ -29467,6 +29570,11 @@ type Methods interface { account_freeze_event_user_id AccountFreezeEvent_UserId_Field) ( rows []*AccountFreezeEvent, err error) + All_BillingTransaction_By_UserId_And_Source_OrderBy_Desc_Timestamp(ctx context.Context, + billing_transaction_user_id BillingTransaction_UserId_Field, + billing_transaction_source BillingTransaction_Source_Field) ( + rows []*BillingTransaction, err error) + All_BillingTransaction_By_UserId_OrderBy_Desc_Timestamp(ctx context.Context, billing_transaction_user_id BillingTransaction_UserId_Field) ( rows []*BillingTransaction, err error)