Closed ShookLyngs closed 5 months ago
Should the BtcAssetsApi to be queried once per build to avoid long waiting time of requests?
Yes, we should query once per build and check the inputs UTXO after the transaction is built.
The BtcAssetsApi
can provide a new API /bitcoin/v1/tx/:txid/outspend/:vout
to return the spending status of each UTXO. It would be faster and more efficient.
Even if assets-api#132 merged, adding a data cache may solve this case, but we should reduce query times if we can.
Ran some tests today:
BtcAssetsApi.getBtcUtxos()
API is used for querying UTXO[].BtcAssetsApi.getRgbppAssetsByBtcUtxo()
is used for checking each input's corresponding RGBPP assets, and if the UTXO is bound with any, it's excluded from the collection process.Step 2 takes about 8-15 seconds, and although a single request in step 3 doesn't take much time, for each UTXO, there is a request to check if it's qualified to be used as an input. The more inputs, the more requests, therefore costing more time.
BtcAssetsApi.getBtcUtxos()
: Despite the improvements that will be applied to the btc-assets-api, the SDK can cache and reuse the result of the API within the scope of a single Txbuilder.payFee()
call, instead of querying the UTXO[] list multiple times in the loop.BtcAssetsApi.getRgbppAssetsByBtcUtxo()
: We may also cache the result of this API to prevent querying the same UTXO multiple times in the loop, but for each UTXO, we MUST query at least once, so the overall time savings on this API could be small. (@ahonn suggests to use the p-limit for batch querying and speeding up, maybe 10 queries at a time)Even if all the above issues are resolved, the exmaple transaction is still not construct-able. Here's a log that shows how:
loop(1): time(16.99s) preFee(0) currentFee(13019) inputs(3)
loop(2): time(17.00s) preFee(13019) currentFee(54661) inputs(16)
loop(3): time(17.03s) preFee(54661) currentFee(185932) inputs(57)
loop(4): time(17.10s) preFee(185932) currentFee(605266) inputs(188)
loop(5): time(17.38s) preFee(605266) currentFee(1950218) inputs(608)
loop(6): time(18.87s) preFee(1950218) currentFee(6256922) inputs(1953)
You can see that in each round of the loop, the fee significantly increased. And in each loop, the transaction included more and more inputs. In the end, the transaction failed with the fee requirement of 6256922 satoshi, and it had included 1953 inputs in the transaction.
It was just a small transaction, with only a single goal: to transfer 1000 satoshi to himself. How could the transaction require 1953 inputs to be included, and it was still insufficient for paying the network fee? Simply put, it's because:
This is like a trap; no matter how many UTXOs (with 1000 satoshi value) you put into the transaction, you can never satisfy the network fee requirement. Therefore, even though the transaction included 1953 inputs, it still lacked enough satoshi for paying the fee.
I think a possible solution to The "the value-cost trap" problem is to start with the estimated number of sats needed, rather than continually getting new UTXOs until demand is met.
It was just a small transaction, with only a single goal: to transfer 1000 satoshi to himself. How could the transaction require 1953 inputs to be included, and it was still insufficient for paying the network fee?
If this is a normal BTC transfer, maybe the BTC SDK could try to find a UTXO with larger value as input.
Coin Selection Algorithm: Some Bitcoin wallets and libraries use a coin selection algorithm to choose the UTXOs to spend in a transaction. These algorithms are designed to minimize the number of inputs required while still covering the amount being sent and the fee.
Issue
A transaction with specific
from
address can take forever to build, for example:What happens:
BtcAssetsApi.getBtcUtxos()
API is called to get the latest UTXO[] listInvestigation & resolvers
BtcAssetsApi
to be queried once per build to avoid long waiting time of requests?