UTXO Management for Enterprise Wallets

Murch
Official BitGo Blog
14 min readJul 28, 2020

--

Just a bit more than three years ago, when I joined BitGo, Bitcoin Cash had not spun off yet, nor had segwit activated. Wallets were simpler, consolidation transactions were mostly theoretical and there was less awareness for payment batching. There were occasional periods of congestion, but then the price run-up to $20,000 at the end of the year would cause the biggest transaction queues the Bitcoin network has seen to date.

Mempool Size in MB, October 2017 – January 2018. Color denotes feerate bands. The full node sourcing the data does not enforce a mempool size limit. The graph shows transaction backlogs exceeding 300 MB, more than two days worth of blocks. Via https://jochen-hoenicke.de/queue

From early December until late January, feerates never dropped below 40 satoshi per byte. Most of that period saw feerates well above 100 satoshi per byte with just a breather of a few days around new years eve. Multiple times, the network’s transaction queue exceeded the default memory pool (mempool) size, causing most nodes to expel the transactions with the lowest feerates — at times to the point where all transactions below 100 satoshi per byte had been expelled. The next-block feerates peaked exceeding 1,000 satoshi per byte on December 22, 2017.

Mempool Size in MB, December 2017. Full node sourcing data uses the default mempool limit of 300 MB. The graph shows the eviction of all transactions below 100 satoshi/vB from this node’s mempool on 22 December 2017. Via https://jochen-hoenicke.de/queue

Segwit adoption had barely begun; many wallets were still building transactions using simplistic coin selection; hardly anything natively supported child-pays-for-parent (CPFP) fee bumping; and, many wallets’ unspent transaction output (UTXO) pools had become extremely fragmented. As transactions were stuck for weeks, services were swamped with support requests, even while paying enormous fees for their transactions. Some enterprise wallets ran out of confirmed UTXOs altogether. For numerous wallets, the majority of their UTXOs were practically unspendable: the cost of spending the UTXOs exceeded their respective values. In short, UTXO management wasn’t much of a thing yet. Neither wallet software nor wallet operators were prepared for a fee event of this magnitude.

The UTXO set ballooned from 50M UTXOs in October 2017 to over 64M in the middle of January 2018. After the backlog receded, an increased appreciation for wallet operation best practices was left. Many wallet operators used the following relative calm to consolidate their wallets, shrinking the UTXO set back to 51M by March 2018.

While there is still room for improvement, enterprise wallets have come a long way since 2017. In the following case study, I introduce two common wallet setups for enterprise operations and discuss how BitGo collaborated with our clients to reduce fee expenditures.

Setup: Send-Only Hot Wallet

Send-only hot wallet: withdrawals to external receivers are sent from the hot wallet. Deposits from external parties are received to a warm wallet which is restricted to sending only to the hot wallet (or an optional cold storage, not depicted).

This type of setup involves at least two, usually three wallets.

  • A hot wallet that is only used for sending
  • A warm wallet that is used to receive incoming funds
  • An optional cold wallet that is used to store the majority of the organization’s funds

Whenever the hot wallet runs low on funds, it is restocked with funds from the warm wallet. Excess funds from the warm wallet are moved to cold-storage. If both the warm and hot wallets run low, funds from the cold wallet can be injected into the hot wallet.

Typical User: Brokerage, Lending Service

Advantages

  • Strong security for large deposits
    Deposits go directly into the warm wallet, which can be set up to only be able to send to the hot wallet and cold wallet as well as requiring approval to send.
  • Simple unspent management in Hot Wallet
    As the hot wallet only sends, unspent management is only a matter of keeping sufficient funds and a sufficiently high count of unspents. Meanwhile, funds in the warm wallet can be consolidated with low time-preference.

Disadvantages

  • Homogeneous UTXO pool in send wallet
    As all funds in the send wallet are either change outputs from previous sends or sent into the wallet via top-ups, the wallet doesn’t have a broad variance of UTXO values. This is disadvantageous for transaction building in the sense that change avoidance is more difficult. Almost every payment will cause the creation of a change output. This downside can be largely mitigated by batching payments and thus, reducing the number of change outputs per payment.
  • Additional transactions to make funds available for sending
    As all funds are first received in the warm wallets, all funds must be redirected to the hot wallet before they become available for spending. Even when funds are consolidated for the transfer, additional transaction outputs are created versus a model in which funds are received directly to the hot wallet.

Challenges

  • High count of payments
    As the sending wallet usually has a fairly low unspent count, sending only a single payment per transaction can quickly exhaust the confirmed funds in the wallet. In such cases, we have seen wallets in the past produce very large chains of unconfirmed transactions. Long chains of unconfirmed transactions will cause later transactions not to be submitted to the network at some point as the Bitcoin nodes do not relay chains of unconfirmed transactions longer than 25 by default.
  • Slow blocks or spike in outflow
    Since the hot wallet is only stocked with the necessary funds, a few slow blocks or a steep increase of the outflow can sometimes deplete the confirmed funds in the wallet quickly.

Recommendations

  • Maintain sufficient funds
    Keep sufficient funds in the hot wallet to sustain peak traffic for at least three hours. Statistically, Bitcoin has one 1-hour block per day. These often create a small backlog that can cause some transactions to be delayed in confirming. Therefore, it’s important to top up the wallet early enough and keep sufficient funds to bridge a period of congestion.
  • Batching
    Sending multiple payments in a single transaction smooths and lowers a wallet’s transaction count. As the input set, transaction header and change output overhead are shared between payments and the number of change outputs created are lowered, this can reduce the transaction fees per payment up to 80%. A send-only wallet that uses batching can bridge minor congestions easily, even with only a few hundred UTXOs. To make batching worthwhile, transactions should aim to cover at least five to ten payments. Depending on the amount of payments performed by the wallet and how quickly payments must be submitted to the network, we have seen batching intervals of one to fifteen minutes work well. We recommend starting with a batching interval of five minutes and adjusting from there.
  • Change splitting
    As a send-only wallet’s UTXO pool exclusively consists of top-up and change outputs, it is essential for a smooth operation that large UTXOs are quickly split up into a greater count. BitGo does this automatically for wallets with high transaction volume. However, when batching payments, the UTXO count should be kept to a lower level to avoid oversplitting.
  • Consolidate Warm Wallet
    As the funds in the warm wallet do not all have to be immediately available, it is possible to regularly submit low-fee consolidation transactions to collect the smaller UTXOs into bigger ones, and thus lower the future cost of restocking the hot wallet.
  • Native segwit change addresses
    Since a send-only wallet only receives funds from its own organization, it can immediately switch over to native segwit addresses, even if this might not be feasible on the receiving end of the operation. Native segwit multisig addresses (p2wsh) are slightly more expensive to send to than p2sh, but also cheaper to spend from. In the case of 2-of-3 multisig as used by BitGo, switching from wrapped to native segwit addresses reduces the wallet’s fees by approximately 15%.

BitGo perks

  • Fee-sensitive unspent selection
    BitGo’s coin selection takes the current fee level into consideration. At low fees, BitGo’s unspent selection will permit bigger input sets to consolidate smaller UTXOs, whereas, it will prefer small input sets at higher fees. The effect of fee-sensitive unspent selection is less pronounced for send-only wallets, as they tend to already have rather homogeneous UTXO pools.
  • Improved change splitting
    BitGo’s automatic change splitting spreads change out across a distribution of values. This has made change splitting more conservative in the creation of additional UTXOs for the wallet and increases the value variance among the wallet’s UTXOs. A higher variance among the wallet’s UTXOs increases the chance of finding a small, change-avoiding input set.

Case Study Send-Only Wallet: “SatSource”

SatSource has a send-only wallet. Starting out as a new business with little volume initially, they send a separate transaction for each recipient. As the volume of payments increases, SatSource finds that their wallet sometimes runs out of confirmed funds in the case of long blocks or spiking withdrawal demand.

SatSource switches to batching payments. Before batching, each recipient output created the overhead of at least one transaction input, one change output, and the transaction header. This translates to a transaction weight per payment of 215 virtual bytes (vB) or more. Now, by batching three payments into one transaction, this overhead cost is split to about 93 vB per payment, reducing the cost per payment by approximately 57%. As the company’s user base grows, the batching size increases over time — to about nine payments per transaction. The transaction weight falls to ~52 vB per payment, for a per-payment cost reduction of approximately 76% from the original one payment per transaction.

As batching in regular intervals has made the transaction cadence much more predictable, SatSource is able to decrease the unspent count maintained in their wallet. This reduces the average input set size for another slight reduction of the transaction fees.

In reviewing SatSource’s accounts, BitGo recommends that they start using native segwit change addresses. At the cost of increasing the output weight from 32 bytes to 43 bytes, their transaction inputs only weigh 105 bytes instead of 140 bytes, resulting in another 13% fee reduction.

Even with a send-only wallet, about 3.5% of SatSource’s transactions avoid creation of change outputs. At 1,000 transactions per day that means saving about 5kvB of transaction data daily which translates to ~1% in transaction fees saved.

Overall, send-only wallets heavily benefit from batching and moving to more efficient address formats for change outputs. SatSource achieved a total reduction of their transaction fees by almost 80%.

Setup: Send-and-Receive Hot Wallet

Send-and-receive hot wallet: The hot wallet both receives deposits and issues withdrawals. The hot wallet is restocked from a warm wallet when it is running low, and likewise, excessive funds from the hot wallet are removed to the warm wallet.

This type of setup involves at least two, usually three wallets.

  • A hot wallet that is used to send and receive
  • A warm wallet that stages additional funds for the hot wallet
  • An optional cold wallet that stores the majority of the funds

When the hot wallet has excessive funds, they get siphoned to the warm wallet, when the hot wallet runs low, it is restocked from the warm wallet. Excess funds from the warm wallet are put into cold storage.

Typical User: Trading Platform, Custodial Wallet Service

Advantages

  • Self-restocking
    Deposits go directly to the hot wallet, largely counterbalancing the withdrawals. This reduces the number of restocking events which often require manual sign-off and lowers the number of additional transactions made for managing funds internally.
  • Heterogeneous unspent pool
    As the hot wallet receives deposits directly, the UTXO pool composition tends to have a broader variety of unspent values. This increases the combinatorial space for selecting input sets and improves the number of change-less transactions.

Disadvantages

  • Potentially more challenging unspent management
    As this type of wallet frequently sees a larger count of incoming transactions than outgoing transactions, there are much broader considerations to account for in order to achieve optimal unspent management.
  • Larger input sets on average
    As the average received amount tends to be much smaller than the required amounts for input sets of batched withdrawals, send and receive wallets tend to use a larger average input set at high feerates than send-only wallets who get restocked with consolidated funds.

Challenges

  • High inbound transaction volume
    As send and receive hot wallets frequently get employed in conjunction with high-traffic retail scenarios, they often see a surplus of incoming transactions. For example, we have seen wallets at BitGo with incoming to outgoing transactions ratios of up to 20 to 1. As each incoming transaction creates at least one unspent transaction output, this type of traffic can quickly bloat a wallet into the hundred of thousands of UTXOs.
  • High fee cost
    Send and receive wallets frequently have their funds split up among so many unspent transaction outputs that they need to use multiple inputs to pay for the outputs. As transactions are often time-sensitive, wallets operate with low block targets, aiming for inclusion in the next couple blocks. Transaction fees can eat up a significant portion of the revenue.

Recommendations

  • Regular Consolidations
    Especially for wallets with a surplus of incoming transactions, we recommend regularly consolidating unspents to counter unspent pool bloat. While having some variety of unspent values benefits the unspent selection for transaction sending, the biggest fee savings are made by consolidating all smaller unspents. Regular consolidation could be implemented as an hourly cronjob which creates a consolidation transaction if there are more than 200 unspents that are smaller than 1 mBTC (100,000 satoshis). The consolidation transactions could be created with the current default minimum relay transaction feerate of 1,000 satoshis per kvB which in the past months has usually been clearing at least once per day. At times of congestion, small multiples of that feerate can get the consolidation transactions confirmed days earlier, as the 1,000–2,000 satoshi per kvB transaction band can grow to tens of MvB.
    An automatic consolidation process should pause at a limited number of consolidation transactions in flight. Tying up too many funds in consolidation transactions reduces the wallet’s flexibility for little benefit. When a longer transaction backlog occurs, the wallet operator may choose to consolidate at a higher feerate instead which is infeasible when the funds are already tied up. We have seen some wallets operating on smaller budgets run out of liquid funds due to tying up too many funds in backlogged consolidation transactions. One way to prevent running out of funds is by restricting consolidation not only to a maximum unspent value, but additionally to a minimum age, e.g. below 1 mBTC and at least 2,000 confirmations.
    The optimal parameters are highly dependent on the wallet’s spending patterns and may range to amounts up to 0.1 BTC, different age limits, and could include different feerates for consolidating different unspent value buckets to make greater liquidity available more quickly.
  • Maintain Amount Variance
    A variety of unspent values is useful for better unspent selection results. Depending on the wallet’s traffic profile, good unspent pool sizes tend to be somewhere between 200 and 5,000 UTXOs. If your funds are consolidated too much, you may run out of unconfirmed funds when a slow block and traffic peak occur in conjunction, or you’ll end up creating change outputs that could have been avoided with exactly matching input sets.
  • Batching
    As described above for send-only wallets, batching multiple payments into one transaction reduces the count of outgoing transactions, the amount of funds in flight for change outputs, and the overall transaction fees by up to 80%. Naturally, batching raises the ratio of incoming to outgoing transactions, so batching should be supplemented with regular consolidations on send-and-receive wallets to curb UTXO pool bloat.

BitGo Perks

  • Efficient addresses
    BitGo wallets offer the use of native segwit addresses. Spending funds received to a native segwit address is 25% less expensive — 105 vB instead of 140 vB. Even if switching over to native segwit addresses for all incoming transactions may not be possible immediately, at least switching to native segwit addresses for change outputs could result in up to a 15% cost reduction on change outputs.
  • Fee-sensitive unspent selection
    BitGo’s unspent selection has been improved to take into consideration the current feerate level. At low feerates, BitGo wallets will permit a higher count of inputs and prefer UTXOs of less efficient address formats. Meanwhile, at higher fees, the software will aim to build input sets with fewer unspents of larger value and prefer weight efficient address formats. As feerates in the Bitcoin network are constantly changing, this shifts some of the blockspace use to times when blockspace is more abundant and cheaper. Overall, this significantly reduces the overall fees and continuously shrinks the UTXO pool size.
  • Change avoidance
    BitGo’s unspent selection attempts to pick input sets that match the recipient amounts of transactions exactly. Transactions constructed in this manner lower the overall transaction fees as they are smaller by avoiding the creation of the change output and additionally avoiding the future cost of spending a change output later.
  • Change splitting
    When BitGo’s transaction building splits change into multiple pieces, the outputs are spread out across a range of values. As each output is larger, this causes change splitting to be more conservative in the creation of additional unspents for the wallet and helps increase the value variance among the wallet’s unspents. More variance among the wallet’s unspents facilitates change avoiding input sets to be selected more often.

Case Study Send and Receive Hot Wallet: “TradeCorp”

TradeCorp is a company with a send and receive hot wallet setup. As an established trading platform, they see about 500 deposits and 400 outgoing transactions per day. As this scenario begins, the wallet has accumulated about 63,000 unspents and is adding roughly another five hundred unspents per week. TradeCorp is batching withdrawals once per minute and has ruled against using consolidation transactions altogether.

As BitGo’s unspent selection is enabled for the wallet, the wallet generates 93% change-less transactions for the first four weeks. Avoiding the creation of change alone saves about 0.45 Bitcoin blocks worth of data per week from hitting the block chain. In the same period of time, the wallet’s unspent pool reverts from the previous growth to be reduced by 20% instead.

After a few months, the wallet settles below 500 unspents. The wallet now has a much higher average UTXO value, as it holds a similar amount of funds distributed among a fraction of the unspent count which significantly decreases the average input set sizes of TradeCorp’s transactions. We estimate that the UTXO pool defragmentation alone reduces total transaction fees by over 50%.

Even after the funds are consolidated, the wallet achieves an average of 58.5% changeless transactions across three months and 35k transactions for transaction fee savings of about 20%. The feerate sensitive unspent selection results in the average input set at low feerates to be about 1.4 times the size of input sets at higher feerates. Given about 32% of the transactions happening at low feerates, almost 45% of the wallet’s input data occurs at low feerates. This shift to spending more UTXOs at lower feerates reduces the overall fee expenditures of the wallet by at least 13%.

At this point, TradeCorp kicks off the transition to native segwit deposit addresses. The wallet is now using about 19.4% native segwit inputs for another 5% blockspace savings.

Overall, the biggest impact on this specific send and receive hot wallet came through the consolidation of its funds. The wallet further benefited significantly from change avoidance and fee sensitive unspent selection, and slightly from more efficient address formats for a total of more than 65% reduced transaction fees. TradeCorp additionally reports to BitGo that their transaction fee expenditures have been much smoother even through periods of elevated feerates. We suspect that extending these efforts with a longer batching interval (e.g. 5 minutes) would further reduce the cost per payment.

Outlook

These case studies showcase the utility of BitGo’s wallets and hopefully inspire experimentation with wallet procedures to improve blockspace efficiency across the industry.

Taproot will significantly improve transaction costs for multisig, meanwhile making inputs and outputs indistinguishable from single-sig spending when the key spending path is used

BitGo continues to iterate on our wallet design. For example, we are excited about the taproot proposal which opens up entirely new possibilities for wallet capabilities and will reduce multisig input sizes to the same size as single-sig in many cases. Even coming from wrapped segwit and native segwit inputs, pay-to-taproot inputs spent via the key path are 45–58% smaller for 2-of-3 multisig.

Thanks to Debra Bar, David Harding, and Adam Jonas for review.

--

--