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  }