Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make kaspawallet store the utxos sorted by amount #1924

Merged
merged 15 commits into from
Feb 18, 2022

Conversation

svarogg
Copy link
Collaborator

@svarogg svarogg commented Jan 4, 2022

This is a preliminary PR for #1922 .

I'm sending this as a separate PR, since it will immidiately help, even without the compound feature, which might take some time to implement properly.

This changes kaspawallet daemon to always keeps a sorted list of UTXOs.
Thus, on average, less UTXOs would be needed for any created transaction, making it less likely that a too-big transaction would be created.

Note this does incur some performance hit, especially for large wallets, but profiling showed it's probably negligible.

… utxos are spent first - making it less likely a compound will be required
@svarogg svarogg force-pushed the kaspawallet-order-by-amount branch from 49b32ef to 8370138 Compare January 4, 2022 20:35
@codecov
Copy link

codecov bot commented Jan 4, 2022

Codecov Report

Merging #1924 (799c494) into master (14b2bcb) will decrease coverage by 0.25%.
The diff coverage is 0.00%.

❗ Current head 799c494 differs from pull request most recent head 76a34a2. Consider uploading reports for the commit 76a34a2 to get more accurate results

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1924      +/-   ##
==========================================
- Coverage   61.60%   61.35%   -0.26%     
==========================================
  Files         627      631       +4     
  Lines       29502    29610     +108     
==========================================
- Hits        18176    18166      -10     
- Misses       8751     8865     +114     
- Partials     2575     2579       +4     
Impacted Files Coverage Δ
app/appmessage/error.go 0.00% <0.00%> (ø)
app/appmessage/message.go 71.42% <ø> (ø)
app/appmessage/rpc_get_balances_by_addresses.go 0.00% <0.00%> (ø)
app/rpc/rpc.go 63.88% <ø> (ø)
app/rpc/rpchandlers/get_balance_by_address.go 0.00% <0.00%> (ø)
app/rpc/rpchandlers/get_balances_by_addresses.go 0.00% <0.00%> (ø)
cmd/kaspawallet/daemon/server/balance.go 0.00% <0.00%> (ø)
...allet/daemon/server/create_unsigned_transaction.go 0.00% <0.00%> (ø)
cmd/kaspawallet/daemon/server/server.go 0.00% <0.00%> (ø)
cmd/kaspawallet/daemon/server/sync.go 0.00% <0.00%> (ø)
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 14b2bcb...76a34a2. Read the comment docs.

@michaelsutton
Copy link
Collaborator

Note that this approach also has some negative effect, since also a medium-size send will use the largest UTXO, while it could of merged a few small UTXOs with out passing the mempool/block limit

@michaelsutton
Copy link
Collaborator

Note that this approach also has some negative effect, since also a medium-size send will use the largest UTXO, while it could of merged a few small UTXOs with out passing the mempool/block limit

As a simple compromise, perhaps we should first start by accumulating the smallest UTXOs, and only if passing the limit, go to the largest UTXOs. Although I'm sure that with a bit more thinking we can come up with some better greedy approximation algo.

@svarogg
Copy link
Collaborator Author

svarogg commented Jan 5, 2022

Note that this approach also has some negative effect, since also a medium-size send will use the largest UTXO, while it could of merged a few small UTXOs with out passing the mempool/block limit

I'll push back, saying that it is in the interest of the sender to use as little UTXOs as possible, since:
A. Smaller transaction means less mass (== fees).
B. The less UTXOs he uses, the less addresses does he have to connect.

Thus I'm not convinced what you presented as a downside is indeed a downside (it might be a downside for the network, I'd say a minor one, but not for the user).

@svarogg
Copy link
Collaborator Author

svarogg commented Jan 5, 2022

Anyway, the main thing this PR does is make sure the utxos are sorted, from which end do we pick them can be discussed and decided upon later down the road.

func (s *server) insertUTXO(utxo *walletUTXO) {
s.utxosSortedByAmount = append(s.utxosSortedByAmount, utxo)
// bubble up the new UTXO to keep the UTXOs sorted by value
index := len(s.utxosSortedByAmount) - 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a binary search

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use sort.Search, like in findTransactionIndex

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it's more readable to use code such as func (tobf *TransactionsOrderedByFeeRate) Push, and maybe even more efficient

@@ -244,13 +256,14 @@ func (s *server) refreshExistingUTXOs() error {
return err
}

s.utxos = make(map[externalapi.DomainOutpoint]*walletUTXO, len(getUTXOsByAddressesResponse.Entries))
s.utxosSortedByAmount = make([]*walletUTXO, 0, len(getUTXOsByAddressesResponse.Entries))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Binary search above is especially important here since it will reduce the overall insertion cost from n^2 to nlogn

func (s *server) insertUTXO(utxo *walletUTXO) {
s.utxosSortedByAmount = append(s.utxosSortedByAmount, utxo)
// bubble up the new UTXO to keep the UTXOs sorted by value
index := len(s.utxosSortedByAmount) - 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use sort.Search, like in findTransactionIndex

func (s *server) insertUTXO(utxo *walletUTXO) {
s.utxosSortedByAmount = append(s.utxosSortedByAmount, utxo)
// bubble up the new UTXO to keep the UTXOs sorted by value
index := len(s.utxosSortedByAmount) - 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it's more readable to use code such as func (tobf *TransactionsOrderedByFeeRate) Push, and maybe even more efficient

@svarogg svarogg force-pushed the kaspawallet-order-by-amount branch from d1ead01 to 5a0b056 Compare January 9, 2022 08:12
@svarogg svarogg force-pushed the kaspawallet-order-by-amount branch from 5a0b056 to 4926054 Compare January 9, 2022 08:58
@svarogg svarogg force-pushed the kaspawallet-order-by-amount branch from 4926054 to c1fdc7c Compare January 9, 2022 09:08
@@ -9,7 +9,7 @@ type GetBalanceByAddressRequestMessage struct {

// Command returns the protocol command string for the message
func (msg *GetBalanceByAddressRequestMessage) Command() MessageCommand {
return CmdGetBalanceByAddressRequestMessage
return CmdGetBalancesByAddressesRequestMessage
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this file suppose to change?

@@ -30,7 +30,7 @@ type GetBalanceByAddressResponseMessage struct {

// Command returns the protocol command string for the message
func (msg *GetBalanceByAddressResponseMessage) Command() MessageCommand {
return CmdGetBalanceByAddressResponseMessage
return CmdGetBalancesByAddressesResponseMessage
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, seems like a mistake and it should remain CmdGetBalanceByAddressResponseMessage

svarogg and others added 11 commits January 11, 2022 15:34
* Report progress percentage when downloading headers in IBD.

* Extract reporting logic to a separate type.

* Report progress for IBD missing block bodies.
…aspanet#1937)

* Split ApplyPruningPointProof to multiple small database transactions.

* Increase the timeout duration in TestIBDWithPruning.

* Increase the timeout duration in simple-sync.

* Explain that if ApplyPruningPointProof fails, the database must be discarded.
)

* Increase timeout for pruning proof and add some logs

* Show resolving virtual progress as whole percents
)

Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
* Drop support for p2p v3

* Remove redundant aliases

* Remove redundant condition
…aspanet#1945)

* Use a time format without ":" to support all file systems

* go fmt
Co-authored-by: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com>
}

getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(addressSet.strings())
sort.Slice(utxos, func(i, j int) bool { return utxos[i].UTXOEntry.Amount() > utxos[j].UTXOEntry.Amount() })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's better to test it on testnet first with a very big wallet?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n log n should be fine any way

@someone235 someone235 merged commit 16336b0 into kaspanet:master Feb 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants