First pass at GetBalance

This commit is contained in:
Patrick O'Grady 2020-10-27 10:25:02 -07:00
parent 272db602e4
commit 2200f52687
No known key found for this signature in database
GPG key ID: 8DE11C985C0C8D85
5 changed files with 99 additions and 26 deletions

View file

@ -84,11 +84,12 @@ type Indexer struct {
client Client client Client
asserter *asserter.Asserter asserter *asserter.Asserter
database storage.Database database storage.Database
blockStorage *storage.BlockStorage blockStorage *storage.BlockStorage
coinStorage *storage.CoinStorage balanceStorage *storage.BalanceStorage
workers []storage.BlockWorker coinStorage *storage.CoinStorage
workers []storage.BlockWorker
waiter *waitTable waiter *waitTable
} }
@ -208,6 +209,7 @@ func Initialize(
&BalanceStorageHelper{asserter}, &BalanceStorageHelper{asserter},
&BalanceStorageHandler{}, &BalanceStorageHandler{},
) )
i.balanceStorage = balanceStorage
i.workers = []storage.BlockWorker{coinStorage, balanceStorage} i.workers = []storage.BlockWorker{coinStorage, balanceStorage}
@ -758,7 +760,11 @@ func (i *Indexer) GetBlockTransaction(
blockIdentifier *types.BlockIdentifier, blockIdentifier *types.BlockIdentifier,
transactionIdentifier *types.TransactionIdentifier, transactionIdentifier *types.TransactionIdentifier,
) (*types.Transaction, error) { ) (*types.Transaction, error) {
return i.blockStorage.GetBlockTransaction(ctx, blockIdentifier, transactionIdentifier) return i.blockStorage.GetBlockTransaction(
ctx,
blockIdentifier,
transactionIdentifier,
)
} }
// GetCoins returns all unspent coins for a particular *types.AccountIdentifier. // GetCoins returns all unspent coins for a particular *types.AccountIdentifier.
@ -768,3 +774,27 @@ func (i *Indexer) GetCoins(
) ([]*types.Coin, *types.BlockIdentifier, error) { ) ([]*types.Coin, *types.BlockIdentifier, error) {
return i.coinStorage.GetCoins(ctx, accountIdentifier) return i.coinStorage.GetCoins(ctx, accountIdentifier)
} }
func (i *Indexer) GetBalance(
ctx context.Context,
accountIdentifier *types.AccountIdentifier,
currency *types.Currency,
blockIdentifier *types.PartialBlockIdentifier,
) (*types.Amount, *types.BlockIdentifier, error) {
blockResponse, err := i.GetBlockLazy(ctx, blockIdentifier)
if err != nil {
return nil, nil, err
}
amount, err := i.balanceStorage.GetBalance(
ctx,
accountIdentifier,
currency,
blockResponse.Block.BlockIdentifier,
)
if err != nil {
return nil, nil, err
}
return amount, blockResponse.Block.BlockIdentifier, nil
}

View file

@ -154,7 +154,7 @@ func main() {
// requests. // requests.
asserter, err := asserter.NewServer( asserter, err := asserter.NewServer(
bitcoin.OperationTypes, bitcoin.OperationTypes,
false, true,
[]*types.NetworkIdentifier{cfg.Network}, []*types.NetworkIdentifier{cfg.Network},
nil, nil,
) )

View file

@ -49,27 +49,51 @@ func (s *AccountAPIService) AccountBalance(
return nil, wrapErr(ErrUnavailableOffline, nil) return nil, wrapErr(ErrUnavailableOffline, nil)
} }
coins, block, err := s.i.GetCoins(ctx, request.AccountIdentifier) // If we are fetching the current balance,
if err != nil { // return all coins for an address and calculate
return nil, wrapErr(ErrUnableToGetCoins, err) // the balance from those coins.
if request.BlockIdentifier == nil {
coins, block, err := s.i.GetCoins(ctx, request.AccountIdentifier)
if err != nil {
return nil, wrapErr(ErrUnableToGetCoins, err)
}
balance := "0"
for _, coin := range coins {
balance, err = types.AddValues(balance, coin.Amount.Value)
if err != nil {
return nil, wrapErr(ErrUnableToParseIntermediateResult, err)
}
}
return &types.AccountBalanceResponse{
BlockIdentifier: block,
Coins: coins,
Balances: []*types.Amount{
{
Value: balance,
Currency: s.config.Currency,
},
},
}, nil
} }
balance := "0" // If we are fetching a historical balance,
for _, coin := range coins { // use balance storage and don't return coins.
balance, err = types.AddValues(balance, coin.Amount.Value) amount, block, err := s.i.GetBalance(
if err != nil { ctx,
return nil, wrapErr(ErrUnableToParseIntermediateResult, err) request.AccountIdentifier,
} s.config.Currency,
request.BlockIdentifier,
)
if err != nil {
return nil, wrapErr(ErrUnableToGetBalance, err)
} }
return &types.AccountBalanceResponse{ return &types.AccountBalanceResponse{
BlockIdentifier: block, BlockIdentifier: block,
Coins: coins,
Balances: []*types.Amount{ Balances: []*types.Amount{
{ amount,
Value: balance,
Currency: s.config.Currency,
},
}, },
}, nil }, nil
} }

View file

@ -40,6 +40,7 @@ var (
ErrUnableToGetCoins, ErrUnableToGetCoins,
ErrTransactionNotFound, ErrTransactionNotFound,
ErrCouldNotGetFeeRate, ErrCouldNotGetFeeRate,
ErrUnableToGetBalance,
} }
// ErrUnimplemented is returned when an endpoint // ErrUnimplemented is returned when an endpoint
@ -59,8 +60,9 @@ var (
// ErrNotReady is returned when bitcoind is not // ErrNotReady is returned when bitcoind is not
// yet ready to serve queries. // yet ready to serve queries.
ErrNotReady = &types.Error{ ErrNotReady = &types.Error{
Code: 2, //nolint Code: 2, //nolint
Message: "Bitcoind is not ready", Message: "Bitcoind is not ready",
Retriable: true,
} }
// ErrBitcoind is returned when bitcoind // ErrBitcoind is returned when bitcoind
@ -173,6 +175,14 @@ var (
Code: 17, // nolint Code: 17, // nolint
Message: "Could not get suggested fee rate", Message: "Could not get suggested fee rate",
} }
// ErrUnableToGetBalance is returned by the indexer
// when it is not possible to get the balance
// of a *types.AccountIdentifier.
ErrUnableToGetBalance = &types.Error{
Code: 18, //nolint
Message: "Unable to get balance",
}
) )
// wrapErr adds details to the types.Error provided. We use a function // wrapErr adds details to the types.Error provided. We use a function
@ -180,8 +190,8 @@ var (
// errors. // errors.
func wrapErr(rErr *types.Error, err error) *types.Error { func wrapErr(rErr *types.Error, err error) *types.Error {
newErr := &types.Error{ newErr := &types.Error{
Code: rErr.Code, Code: rErr.Code,
Message: rErr.Message, Message: rErr.Message,
Retriable: rErr.Retriable, Retriable: rErr.Retriable,
} }
if err != nil { if err != nil {

View file

@ -48,7 +48,10 @@ type Client interface {
// Indexer is used by the servicers to get block and account data. // Indexer is used by the servicers to get block and account data.
type Indexer interface { type Indexer interface {
GetBlockLazy(context.Context, *types.PartialBlockIdentifier) (*types.BlockResponse, error) GetBlockLazy(
context.Context,
*types.PartialBlockIdentifier,
) (*types.BlockResponse, error)
GetBlockTransaction( GetBlockTransaction(
context.Context, context.Context,
*types.BlockIdentifier, *types.BlockIdentifier,
@ -62,6 +65,12 @@ type Indexer interface {
context.Context, context.Context,
[]*types.Coin, []*types.Coin,
) ([]*bitcoin.ScriptPubKey, error) ) ([]*bitcoin.ScriptPubKey, error)
GetBalance(
context.Context,
*types.AccountIdentifier,
*types.Currency,
*types.PartialBlockIdentifier,
) (*types.Amount, *types.BlockIdentifier, error)
} }
type unsignedTransaction struct { type unsignedTransaction struct {