code.vegaprotocol.io/vega@v0.79.0/datanode/sqlsubscribers/ledger_movement.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package sqlsubscribers 17 18 import ( 19 "context" 20 "fmt" 21 "strings" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/datanode/entities" 26 "code.vegaprotocol.io/vega/protos/vega" 27 28 "github.com/pkg/errors" 29 "github.com/shopspring/decimal" 30 ) 31 32 type Ledger interface { 33 AddLedgerEntry(entities.LedgerEntry) error 34 AddTransferResponse(*vega.LedgerMovement) 35 Flush(ctx context.Context) error 36 } 37 38 type LedgerMovementEvents interface { 39 events.Event 40 LedgerMovements() []*vega.LedgerMovement 41 } 42 43 type TransferResponse struct { 44 subscriber 45 ledger Ledger 46 accounts AccountService 47 } 48 49 func NewTransferResponse(ledger Ledger, accounts AccountService) *TransferResponse { 50 return &TransferResponse{ 51 ledger: ledger, 52 accounts: accounts, 53 } 54 } 55 56 func (t *TransferResponse) Types() []events.Type { 57 return []events.Type{events.LedgerMovementsEvent} 58 } 59 60 func (t *TransferResponse) Flush(ctx context.Context) error { 61 err := t.ledger.Flush(ctx) 62 return errors.Wrap(err, "flushing ledger") 63 } 64 65 func (t *TransferResponse) Push(ctx context.Context, evt events.Event) error { 66 return t.consume(ctx, evt.(LedgerMovementEvents)) 67 } 68 69 func (t *TransferResponse) consume(ctx context.Context, e LedgerMovementEvents) error { 70 var errs strings.Builder 71 for _, tr := range e.LedgerMovements() { 72 t.ledger.AddTransferResponse(tr) 73 for _, vle := range tr.Entries { 74 if err := t.addLedgerEntry(ctx, vle, e.TxHash(), t.vegaTime); err != nil { 75 errs.WriteString(fmt.Sprintf("couldn't add ledger entry: %v, error:%s\n", vle, err)) 76 } 77 } 78 } 79 80 if errs.Len() != 0 { 81 return errors.Errorf("processing transfer response:%s", errs.String()) 82 } 83 84 return nil 85 } 86 87 func (t *TransferResponse) addLedgerEntry(ctx context.Context, vle *vega.LedgerEntry, txHash string, vegaTime time.Time) error { 88 fromAcc, err := t.obtainAccountWithAccountDetails(ctx, vle.FromAccount, txHash, vegaTime) 89 if err != nil { 90 return errors.Wrap(err, "obtaining 'from' account") 91 } 92 93 toAcc, err := t.obtainAccountWithAccountDetails(ctx, vle.ToAccount, txHash, vegaTime) 94 if err != nil { 95 return errors.Wrap(err, "obtaining 'to' account") 96 } 97 98 quantity, err := decimal.NewFromString(vle.Amount) 99 if err != nil { 100 return errors.Wrap(err, "parsing amount string") 101 } 102 103 fromAccountBalance, err := decimal.NewFromString(vle.FromAccountBalance) 104 if err != nil { 105 return errors.Wrap(err, "parsing FromAccountBalance string") 106 } 107 108 toAccountBalance, err := decimal.NewFromString(vle.ToAccountBalance) 109 if err != nil { 110 return errors.Wrap(err, "parsing ToAccountBalance string") 111 } 112 113 var transferID entities.TransferID 114 if vle.TransferId != nil { 115 transferID = entities.TransferID(*vle.TransferId) 116 } 117 118 le := entities.LedgerEntry{ 119 FromAccountID: fromAcc.ID, 120 ToAccountID: toAcc.ID, 121 Quantity: quantity, 122 TxHash: entities.TxHash(txHash), 123 VegaTime: vegaTime, 124 TransferTime: time.Unix(0, vle.Timestamp), 125 Type: entities.LedgerMovementType(vle.Type), 126 FromAccountBalance: fromAccountBalance, 127 ToAccountBalance: toAccountBalance, 128 TransferID: transferID, 129 } 130 131 err = t.ledger.AddLedgerEntry(le) 132 if err != nil { 133 return errors.Wrap(err, "adding to store") 134 } 135 return nil 136 } 137 138 // Parse the vega account ID; if that account already exists in the db, fetch it; else create it. 139 func (t *TransferResponse) obtainAccountWithAccountDetails(ctx context.Context, ad *vega.AccountDetails, txHash string, vegaTime time.Time) (entities.Account, error) { 140 a, err := entities.AccountProtoFromDetails(ad, entities.TxHash(txHash)) 141 if err != nil { 142 return entities.Account{}, errors.Wrapf(err, "parsing account id: %s", ad.String()) 143 } 144 a.VegaTime = vegaTime 145 err = t.accounts.Obtain(ctx, &a) 146 if err != nil { 147 return entities.Account{}, errors.Wrapf(err, "obtaining account for id: %s", ad.String()) 148 } 149 return a, nil 150 } 151 152 func (t *TransferResponse) Name() string { 153 return "TransferResponse" 154 }