rpcserver: add Marshaler interface

The Marshaler interface is used to transform internal types to
decorated RPC ones.

It decouples that functionality form the rpcServer so it is easier to
test exhaustively.
This commit is contained in:
positiveblue 2021-12-09 22:52:03 -08:00
parent 65157959b0
commit 1589efe1ea
No known key found for this signature in database
GPG key ID: 4FFF2510928804DC
5 changed files with 191 additions and 78 deletions

5
gen.go
View file

@ -5,8 +5,13 @@ package pool
// make mock
//
//go:generate mockgen -source=interfaces.go -package=pool -destination=mock_interfaces.go
//go:generate mockgen -source=sidecar/interfaces.go -package=sidecar -destination=sidecar/mock_interfaces.go
//go:generate mockgen -source=internal/test/interfaces.go -package=test -destination=internal/test/mock_interfaces.go
//go:generate mockgen -source=account/interfaces.go -package=account -destination=account/mock_interfaces.go
//go:generate mockgen -source=account/watcher/interfaces.go -package=watcher -destination=account/watcher/mock_interface_test.go
//go:generate mockgen -source=order/interfaces.go -package=order -destination=order/mock_interfaces.go

16
interfaces.go Normal file
View file

@ -0,0 +1,16 @@
package pool
import (
"context"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/poolrpc"
)
// Marshaler interface to transform internal types to decorated RPC ones.
type Marshaler interface {
// MarshallAccountsWithAvailableBalance returns the RPC representation
// of an account with the account.AvailableBalance value populated.
MarshallAccountsWithAvailableBalance(ctx context.Context,
accounts []*account.Account) ([]*poolrpc.Account, error)
}

106
marshaler.go Normal file
View file

@ -0,0 +1,106 @@
package pool
import (
"context"
"fmt"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/order"
"github.com/lightninglabs/pool/poolrpc"
"github.com/lightninglabs/pool/terms"
)
// marshalerConfig contains all of the marshaler's dependencies in order to
// carry out its duties.
type marshalerConfig struct {
// GetOrders returns all orders that are currently known to the store.
GetOrders func() ([]order.Order, error)
// Terms returns the current dynamic auctioneer terms like max account
// size, max order duration in blocks and the auction fee schedule.
Terms func(ctx context.Context) (*terms.AuctioneerTerms, error)
}
// marshaler is an internal struct type that implements the Marshaler interface.
type marshaler struct {
cfg *marshalerConfig
}
// NewMarshaler returns an internal type that implements the Marshaler interface.
func NewMarshaler(cfg *marshalerConfig) *marshaler { // nolint:golint
return &marshaler{
cfg: cfg,
}
}
// MarshallAccountsWithAvailableBalance returns the RPC representation of an account
// with the account.AvailableBalance value populated.
func (m *marshaler) MarshallAccountsWithAvailableBalance(ctx context.Context,
accounts []*account.Account) ([]*poolrpc.Account, error) {
rpcAccounts := make([]*poolrpc.Account, 0, len(accounts))
for _, acct := range accounts {
rpcAccount, err := MarshallAccount(acct)
if err != nil {
return nil, err
}
rpcAccounts = append(rpcAccounts, rpcAccount)
}
// For each account, we'll need to compute the available balance, which
// requires us to sum up all the debits from outstanding orders.
orders, err := m.cfg.GetOrders()
if err != nil {
return nil, err
}
// Get the current fee schedule so we can compute the worst-case
// account debit assuming all our standing orders were matched.
auctionTerms, err := m.cfg.Terms(ctx)
if err != nil {
return nil, fmt.Errorf("unable to query auctioneer terms: %v",
err)
}
// For each active account, consume the worst-case account delta if the
// order were to be matched.
accountDebits := make(map[[33]byte]btcutil.Amount)
auctionFeeSchedule := auctionTerms.FeeSchedule()
for _, acct := range accounts {
var (
debitAmt btcutil.Amount
acctKey [33]byte
)
copy(
acctKey[:],
acct.TraderKey.PubKey.SerializeCompressed(),
)
// We'll make sure to accumulate a distinct sum for each
// outstanding account the user has.
for _, o := range orders {
if o.Details().AcctKey != acctKey {
continue
}
debitAmt += o.ReservedValue(auctionFeeSchedule)
}
accountDebits[acctKey] = debitAmt
}
// Finally, we'll populate the available balance value for each of the
// existing accounts.
for _, rpcAccount := range rpcAccounts {
var acctKey [33]byte
copy(acctKey[:], rpcAccount.TraderKey)
accountDebit := accountDebits[acctKey]
availableBalance := rpcAccount.Value - uint64(accountDebit)
rpcAccount.AvailableBalance = availableBalance
}
return rpcAccounts, nil
}

52
mock_interfaces.go Normal file
View file

@ -0,0 +1,52 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: interfaces.go
// Package pool is a generated GoMock package.
package pool
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
account "github.com/lightninglabs/pool/account"
poolrpc "github.com/lightninglabs/pool/poolrpc"
)
// MockMarshaler is a mock of Marshaler interface.
type MockMarshaler struct {
ctrl *gomock.Controller
recorder *MockMarshalerMockRecorder
}
// MockMarshalerMockRecorder is the mock recorder for MockMarshaler.
type MockMarshalerMockRecorder struct {
mock *MockMarshaler
}
// NewMockMarshaler creates a new mock instance.
func NewMockMarshaler(ctrl *gomock.Controller) *MockMarshaler {
mock := &MockMarshaler{ctrl: ctrl}
mock.recorder = &MockMarshalerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockMarshaler) EXPECT() *MockMarshalerMockRecorder {
return m.recorder
}
// MarshallAccountsWithAvailableBalance mocks base method.
func (m *MockMarshaler) MarshallAccountsWithAvailableBalance(ctx context.Context, accounts []*account.Account) ([]*poolrpc.Account, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MarshallAccountsWithAvailableBalance", ctx, accounts)
ret0, _ := ret[0].([]*poolrpc.Account)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MarshallAccountsWithAvailableBalance indicates an expected call of MarshallAccountsWithAvailableBalance.
func (mr *MockMarshalerMockRecorder) MarshallAccountsWithAvailableBalance(ctx, accounts interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarshallAccountsWithAvailableBalance", reflect.TypeOf((*MockMarshaler)(nil).MarshallAccountsWithAvailableBalance), ctx, accounts)
}

View file

@ -64,6 +64,7 @@ type rpcServer struct {
auctioneer *auctioneer.Client
accountManager account.Manager
orderManager order.Manager
marshaler Marshaler
quit chan struct{}
wg sync.WaitGroup
@ -117,6 +118,10 @@ func newRPCServer(server *Server) *rpcServer {
Wallet: lndServices.WalletKit,
Signer: lndServices.Signer,
}),
marshaler: NewMarshaler(&marshalerConfig{
GetOrders: server.db.GetOrders,
Terms: server.AuctioneerClient.Terms,
}),
quit: make(chan struct{}),
}
}
@ -589,7 +594,7 @@ func (s *rpcServer) ListAccounts(ctx context.Context,
validAccounts = append(validAccounts, acct)
}
rpcAccounts, err := s.MarshallAccountsWithAvailableBalance(
rpcAccounts, err := s.marshaler.MarshallAccountsWithAvailableBalance(
ctx, validAccounts,
)
if err != nil {
@ -602,77 +607,6 @@ func (s *rpcServer) ListAccounts(ctx context.Context,
}, nil
}
// MarshallAccountsWithAvailableBalance returns the RPC representation of an account
// with the account.AvailableBalance value populated.
func (s *rpcServer) MarshallAccountsWithAvailableBalance(ctx context.Context,
accounts []*account.Account) ([]*poolrpc.Account, error) {
rpcAccounts := make([]*poolrpc.Account, 0, len(accounts))
for _, acct := range accounts {
rpcAccount, err := MarshallAccount(acct)
if err != nil {
return nil, err
}
rpcAccounts = append(rpcAccounts, rpcAccount)
}
// For each account, we'll need to compute the available balance, which
// requires us to sum up all the debits from outstanding orders.
orders, err := s.server.db.GetOrders()
if err != nil {
return nil, err
}
// Get the current fee schedule so we can compute the worst-case
// account debit assuming all our standing orders were matched.
auctionTerms, err := s.auctioneer.Terms(ctx)
if err != nil {
return nil, fmt.Errorf("unable to query auctioneer terms: %v",
err)
}
// For each active account, consume the worst-case account delta if the
// order were to be matched.
accountDebits := make(map[[33]byte]btcutil.Amount)
auctionFeeSchedule := auctionTerms.FeeSchedule()
for _, acct := range accounts {
var (
debitAmt btcutil.Amount
acctKey [33]byte
)
copy(
acctKey[:],
acct.TraderKey.PubKey.SerializeCompressed(),
)
// We'll make sure to accumulate a distinct sum for each
// outstanding account the user has.
for _, o := range orders {
if o.Details().AcctKey != acctKey {
continue
}
debitAmt += o.ReservedValue(auctionFeeSchedule)
}
accountDebits[acctKey] = debitAmt
}
// Finally, we'll populate the available balance value for each of the
// existing accounts.
for _, rpcAccount := range rpcAccounts {
var acctKey [33]byte
copy(acctKey[:], rpcAccount.TraderKey)
accountDebit := accountDebits[acctKey]
availableBalance := rpcAccount.Value - uint64(accountDebit)
rpcAccount.AvailableBalance = availableBalance
}
return rpcAccounts, nil
}
// MarshallAccount returns the RPC representation of an account.
func MarshallAccount(a *account.Account) (*poolrpc.Account, error) {
var rpcState poolrpc.AccountState
@ -770,7 +704,7 @@ func (s *rpcServer) DepositAccount(ctx context.Context,
return nil, err
}
rpcModifiedAccounts, err := s.MarshallAccountsWithAvailableBalance(
rpcModAccounts, err := s.marshaler.MarshallAccountsWithAvailableBalance(
ctx, []*account.Account{modifiedAccount},
)
if err != nil {
@ -779,7 +713,7 @@ func (s *rpcServer) DepositAccount(ctx context.Context,
txHash := tx.TxHash()
return &poolrpc.DepositAccountResponse{
Account: rpcModifiedAccounts[0],
Account: rpcModAccounts[0],
DepositTxid: txHash[:],
}, nil
}
@ -837,7 +771,7 @@ func (s *rpcServer) WithdrawAccount(ctx context.Context,
return nil, err
}
rpcModifiedAccounts, err := s.MarshallAccountsWithAvailableBalance(
rpcModAccounts, err := s.marshaler.MarshallAccountsWithAvailableBalance(
ctx, []*account.Account{modifiedAccount},
)
if err != nil {
@ -846,7 +780,7 @@ func (s *rpcServer) WithdrawAccount(ctx context.Context,
txHash := tx.TxHash()
return &poolrpc.WithdrawAccountResponse{
Account: rpcModifiedAccounts[0],
Account: rpcModAccounts[0],
WithdrawTxid: txHash[:],
}, nil
}
@ -895,7 +829,7 @@ func (s *rpcServer) RenewAccount(ctx context.Context,
return nil, err
}
rpcModifiedAccounts, err := s.MarshallAccountsWithAvailableBalance(
rpcModAccounts, err := s.marshaler.MarshallAccountsWithAvailableBalance(
ctx, []*account.Account{modifiedAccount},
)
if err != nil {
@ -904,7 +838,7 @@ func (s *rpcServer) RenewAccount(ctx context.Context,
txHash := tx.TxHash()
return &poolrpc.RenewAccountResponse{
Account: rpcModifiedAccounts[0],
Account: rpcModAccounts[0],
RenewalTxid: txHash[:],
}, nil
}