Skip to content

Commit

Permalink
Return uncofirmed utxos when listing & Support filtering by address (#92
Browse files Browse the repository at this point in the history
)

* List utxos filtered by addresses

* Fix

* Return unconfirmed utxos

* Fix

* Include unconfirmed utxos in spendable

* Revert & Return unconf utxos in list of spendable
  • Loading branch information
altafan authored Jul 5, 2024
1 parent f20544d commit 71b452e
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 48 deletions.
6 changes: 3 additions & 3 deletions internal/core/application/account_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (as *AccountService) GetBalanceForAccount(
}

func (as *AccountService) ListUtxosForAccount(
ctx context.Context, accountName string,
ctx context.Context, accountName string, scripts [][]byte,
) (*UtxoInfo, error) {
w, err := as.repoManager.WalletRepository().GetWallet(ctx)
if err != nil {
Expand All @@ -173,14 +173,14 @@ func (as *AccountService) ListUtxosForAccount(
}

spendableUtxos, err := as.repoManager.UtxoRepository().GetSpendableUtxosForAccount(
ctx, account.Namespace,
ctx, account.Namespace, scripts,
)
if err != nil {
return nil, err
}

lockedUtxos, err := as.repoManager.UtxoRepository().GetLockedUtxosForAccount(
ctx, account.Namespace,
ctx, account.Namespace, scripts,
)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/core/application/account_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestAccountService(t *testing.T) {
require.NoError(t, err)
require.GreaterOrEqual(t, len(addresses), 2)

utxos, err := svc.ListUtxosForAccount(ctx, accountName)
utxos, err := svc.ListUtxosForAccount(ctx, accountName, nil)
require.NoError(t, err)
require.NotNil(t, utxos)
require.NotEmpty(t, utxos.Spendable)
Expand Down
6 changes: 3 additions & 3 deletions internal/core/application/transaction_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (ts *TransactionService) SelectUtxos(
}

utxos, err := ts.repoManager.UtxoRepository().GetSpendableUtxosForAccount(
ctx, account.Namespace,
ctx, account.Namespace, nil,
)
if err != nil {
return nil, 0, -1, err
Expand Down Expand Up @@ -396,7 +396,7 @@ func (ts *TransactionService) Transfer(
}

utxos, err := utxoRepo.GetSpendableUtxosForAccount(
ctx, account.Namespace,
ctx, account.Namespace, nil,
)
if err != nil {
return "", err
Expand Down Expand Up @@ -778,7 +778,7 @@ func (ts *TransactionService) scheduleUtxoUnlocker() {

for accountName := range w.Accounts {
utxos, _ := utxoRepo.GetLockedUtxosForAccount(
ctx, accountName,
ctx, accountName, nil,
)
if len(utxos) > 0 {
utxosToUnlock := make([]domain.UtxoKey, 0, len(utxos))
Expand Down
7 changes: 4 additions & 3 deletions internal/core/domain/utxo_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ type UtxoRepository interface {
GetAllUtxosForAccount(ctx context.Context, account string) ([]*Utxo, error)
// GetSpendableUtxosForAccount returns the list of spendable utxos for the
// given account. The list incldues only confirmed and unlocked utxos.
GetSpendableUtxosForAccount(ctx context.Context, account string) ([]*Utxo, error)
// Can be filtered by output scripts.
GetSpendableUtxosForAccount(ctx context.Context, account string, scripts [][]byte) ([]*Utxo, error)
// GetLockedUtxosForAccount returns the list of all currently locked utxos
// for the given account.
GetLockedUtxosForAccount(ctx context.Context, account string) ([]*Utxo, error)
// for the given account. Can be filtered by output scripts.
GetLockedUtxosForAccount(ctx context.Context, account string, scripts [][]byte) ([]*Utxo, error)
// GetBalanceForAccount returns the confirmed, unconfirmed and locked
// balances per each asset for the given account.
GetBalanceForAccount(ctx context.Context, account string) (map[string]*Balance, error)
Expand Down
48 changes: 42 additions & 6 deletions internal/infrastructure/storage/db/badger/utxo_repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dbbadger

import (
"bytes"
"context"
"fmt"
"sync"
Expand Down Expand Up @@ -69,7 +70,7 @@ func (r *utxoRepository) GetSpendableUtxos(
ctx context.Context,
) ([]*domain.Utxo, error) {
query := badgerhold.Where("SpentStatus").Eq(domain.UtxoStatus{}).
And("ConfirmedStatus").Ne(domain.UtxoStatus{}).And("LockTimestamp").Eq(int64(0))
And("LockTimestamp").Eq(int64(0))

return r.findUtxos(ctx, query)
}
Expand All @@ -83,22 +84,57 @@ func (r *utxoRepository) GetAllUtxosForAccount(
}

func (r *utxoRepository) GetSpendableUtxosForAccount(
ctx context.Context, accountName string,
ctx context.Context, accountName string, scripts [][]byte,
) ([]*domain.Utxo, error) {
query := badgerhold.Where("SpentStatus").Eq(domain.UtxoStatus{}).
And("ConfirmedStatus").Ne(domain.UtxoStatus{}).
And("LockTimestamp").Eq(int64(0)).And("AccountName").Eq(accountName)

return r.findUtxos(ctx, query)
utxos, err := r.findUtxos(ctx, query)
if err != nil {
return nil, err
}

if len(scripts) <= 0 {
return utxos, nil
}

filteredUtxos := make([]*domain.Utxo, 0, len(utxos))
for _, u := range utxos {
for _, script := range scripts {
if bytes.Equal(u.Script, script) {
filteredUtxos = append(filteredUtxos, u)
break
}
}
}
return filteredUtxos, nil
}

func (r *utxoRepository) GetLockedUtxosForAccount(
ctx context.Context, accountName string,
ctx context.Context, accountName string, scripts [][]byte,
) ([]*domain.Utxo, error) {
query := badgerhold.Where("SpentStatus").Eq(domain.UtxoStatus{}).
And("LockTimestamp").Gt(int64(0)).And("AccountName").Eq(accountName)

return r.findUtxos(ctx, query)
utxos, err := r.findUtxos(ctx, query)
if err != nil {
return nil, err
}

if len(scripts) <= 0 {
return utxos, nil
}

filteredUtxos := make([]*domain.Utxo, 0, len(utxos))
for _, u := range utxos {
for _, script := range scripts {
if bytes.Equal(u.Script, script) {
filteredUtxos = append(filteredUtxos, u)
break
}
}
}
return filteredUtxos, nil
}

func (r *utxoRepository) GetBalanceForAccount(
Expand Down
36 changes: 25 additions & 11 deletions internal/infrastructure/storage/db/inmemory/utxo_repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package inmemory

import (
"bytes"
"context"
"sync"

Expand Down Expand Up @@ -84,25 +85,25 @@ func (r *utxoRepository) GetAllUtxosForAccount(
r.store.lock.RLock()
defer r.store.lock.RUnlock()

return r.getUtxosForAccount(account, false, false)
return r.getUtxosForAccount(account, false, false, nil)
}

func (r *utxoRepository) GetSpendableUtxosForAccount(
_ context.Context, account string,
_ context.Context, account string, scripts [][]byte,
) ([]*domain.Utxo, error) {
r.store.lock.RLock()
defer r.store.lock.RUnlock()

return r.getUtxosForAccount(account, true, false)
return r.getUtxosForAccount(account, true, false, scripts)
}

func (r *utxoRepository) GetLockedUtxosForAccount(
_ context.Context, account string,
_ context.Context, account string, scripts [][]byte,
) ([]*domain.Utxo, error) {
r.store.lock.RLock()
defer r.store.lock.RUnlock()

return r.getUtxosForAccount(account, false, true)
return r.getUtxosForAccount(account, false, true, scripts)
}

func (r *utxoRepository) GetBalanceForAccount(
Expand All @@ -111,7 +112,7 @@ func (r *utxoRepository) GetBalanceForAccount(
r.store.lock.RLock()
defer r.store.lock.RUnlock()

utxos, _ := r.getUtxosForAccount(account, false, false)
utxos, _ := r.getUtxosForAccount(account, false, false, nil)
balance := make(map[string]*domain.Balance)
for _, u := range utxos {
if u.IsSpent() {
Expand Down Expand Up @@ -228,7 +229,7 @@ func (r *utxoRepository) getUtxos(spendableOnly bool) []*domain.Utxo {
utxos := make([]*domain.Utxo, 0, len(r.store.utxos))
for _, u := range r.store.utxos {
if spendableOnly {
if !u.IsLocked() && u.IsConfirmed() && !u.IsSpent() {
if !u.IsLocked() && !u.IsSpent() {
utxos = append(utxos, u)
}
continue
Expand All @@ -239,7 +240,7 @@ func (r *utxoRepository) getUtxos(spendableOnly bool) []*domain.Utxo {
}

func (r *utxoRepository) getUtxosForAccount(
account string, spendableOnly, lockedOnly bool,
account string, spendableOnly, lockedOnly bool, scripts [][]byte,
) ([]*domain.Utxo, error) {
keys := r.store.utxosByAccount[account]
if len(keys) == 0 {
Expand All @@ -251,22 +252,35 @@ func (r *utxoRepository) getUtxosForAccount(
u := r.store.utxos[k.Hash()]

if spendableOnly {
if !u.IsLocked() && u.IsConfirmed() && !u.IsSpent() {
if !u.IsLocked() && !u.IsSpent() {
utxos = append(utxos, u)
}
continue
}

if lockedOnly {
if u.IsLocked() {
if u.IsLocked() && !u.IsSpent() {
utxos = append(utxos, u)
}
continue
}
utxos = append(utxos, u)
}

return utxos, nil
if len(scripts) <= 0 {
return utxos, nil
}

filteredUtxos := make([]*domain.Utxo, 0, len(utxos))
for _, u := range utxos {
for _, script := range scripts {
if bytes.Equal(u.Script, script) {
filteredUtxos = append(filteredUtxos, u)
break
}
}
}
return filteredUtxos, nil
}

func (r *utxoRepository) spendUtxos(
Expand Down
33 changes: 26 additions & 7 deletions internal/infrastructure/storage/db/postgres/utxo_repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package postgresdb

import (
"bytes"
"context"
"database/sql"
"sync"
Expand Down Expand Up @@ -229,7 +230,7 @@ func (u *utxoRepositoryPg) GetSpendableUtxos(
}

for _, v := range utxosByKey {
if !v.IsLocked() && v.IsConfirmed() && !v.IsSpent() {
if !v.IsLocked() && !v.IsSpent() {
resp = append(resp, v)
}
}
Expand Down Expand Up @@ -268,7 +269,7 @@ func (u *utxoRepositoryPg) GetAllUtxosForAccount(
}

func (u *utxoRepositoryPg) GetSpendableUtxosForAccount(
ctx context.Context, account string,
ctx context.Context, account string, scripts [][]byte,
) ([]*domain.Utxo, error) {
resp := make([]*domain.Utxo, 0)
utxos, err := u.querier.GetUtxosForAccount(ctx, account)
Expand All @@ -291,16 +292,25 @@ func (u *utxoRepositoryPg) GetSpendableUtxosForAccount(
}

for _, v := range utxosByKey {
if !v.IsLocked() && v.IsConfirmed() && !v.IsSpent() {
resp = append(resp, v)
if !v.IsLocked() && !v.IsSpent() {
found := len(scripts) <= 0
for _, script := range scripts {
if bytes.Equal(v.Script, script) {
found = true
break
}
}
if found {
resp = append(resp, v)
}
}
}

return resp, nil
}

func (u *utxoRepositoryPg) GetLockedUtxosForAccount(
ctx context.Context, account string,
ctx context.Context, account string, scripts [][]byte,
) ([]*domain.Utxo, error) {
resp := make([]*domain.Utxo, 0)
utxos, err := u.querier.GetUtxosForAccount(ctx, account)
Expand All @@ -323,8 +333,17 @@ func (u *utxoRepositoryPg) GetLockedUtxosForAccount(
}

for _, v := range utxosByKey {
if v.IsLocked() {
resp = append(resp, v)
if v.IsLocked() && !v.IsSpent() {
found := len(scripts) <= 0
for _, script := range scripts {
if bytes.Equal(v.Script, script) {
found = true
break
}
}
if found {
resp = append(resp, v)
}
}
}

Expand Down
Loading

0 comments on commit 71b452e

Please sign in to comment.