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

@ -87,6 +87,7 @@ type Indexer struct {
asserter *asserter.Asserter asserter *asserter.Asserter
database storage.Database database storage.Database
blockStorage *storage.BlockStorage blockStorage *storage.BlockStorage
balanceStorage *storage.BalanceStorage
coinStorage *storage.CoinStorage coinStorage *storage.CoinStorage
workers []storage.BlockWorker workers []storage.BlockWorker
@ -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,6 +49,10 @@ func (s *AccountAPIService) AccountBalance(
return nil, wrapErr(ErrUnavailableOffline, nil) return nil, wrapErr(ErrUnavailableOffline, nil)
} }
// If we are fetching the current balance,
// return all coins for an address and calculate
// the balance from those coins.
if request.BlockIdentifier == nil {
coins, block, err := s.i.GetCoins(ctx, request.AccountIdentifier) coins, block, err := s.i.GetCoins(ctx, request.AccountIdentifier)
if err != nil { if err != nil {
return nil, wrapErr(ErrUnableToGetCoins, err) return nil, wrapErr(ErrUnableToGetCoins, err)
@ -73,3 +77,23 @@ func (s *AccountAPIService) AccountBalance(
}, },
}, nil }, nil
} }
// If we are fetching a historical balance,
// use balance storage and don't return coins.
amount, block, err := s.i.GetBalance(
ctx,
request.AccountIdentifier,
s.config.Currency,
request.BlockIdentifier,
)
if err != nil {
return nil, wrapErr(ErrUnableToGetBalance, err)
}
return &types.AccountBalanceResponse{
BlockIdentifier: block,
Balances: []*types.Amount{
amount,
},
}, 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
@ -61,6 +62,7 @@ var (
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

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 {