A command line application written in Python to simulate bitcoin transactions using Binary Integer Programming models to test selection policies and compare different coin selection algorithms.
This application uses Pulp for modeling the selection function and restrictions and it can compute the models in any of the solvers for which Pulp interface is implemented, although for the stored simulations, the Branch and Bound solver of coin-OR was used.
The currently implemented algorithms/models are five:
greatest-first
: a greedy implementation that always select the biggestUTxO
.single-random-draw
: randomly selectUTxOs
until meeting the criteria.avoid-change
: try to produce changeless transaction always. Inspired by one of the Binary Linear Programming models used by Daniel J. Diroff in its work Bitcoin Coin Selection with Leverage.minimize-waste
: the most similar to the actual implementation of Bitcoin Core'sbranch-and-bound
algorithm. It selectsUTxOs
trying to minimize theWaste
metric, proposed by Mark Erhardt.maximize-effective-value
: a tweak of theminimize-waste
policy blended with thedouble-target
strategy, mentioned in the Master Thesis of Mark Erhardt and in the same spirit of the Random Improve selection policy proposed by Edsko De Vries for Cardano. It tries to minimize waste but producing a change output closer to the median amount of the payments.
In case of selection failure of any of the above algorithms, the emergency
selection algorithm, single-random-draw
will try to solve the selection. If
it also fails, the payment obligation will stay in queue while the simulator
continues processing incoming transaction, until the next payment obligation is
added to the queue, and the simulated selection algorithms comes to action
again.
The data in the scenarios cames from the simulator implemented by Ava Chow and Mark Erhardt. It has been tweaked to process multi payment transactions, although the stored simulations didn't use the feature.
The application also implements a tool to generate scenarios. It allows the extraction of fee estimations from a personal node's blockchain, the generation of custom transaction profiles using mathematical distributions and the combination of those transactions and fee estimations in new scenarios.
You can also produce new transaction profiles, fee rate market fluctuations or even complete scenarios with external tools and load them usign a different data path.
- Install pdm.
- Create virtual environment with any python
>=3.10
:pdm venv create 3.10
- Check the virtual environment is activated:
eval $(pdm venv activate -v)
- Install dependencies:
pdm install
- Execute simulation:
python simulate.py --all
The data/
directory is a sample of how the data consumed by the tool should
be structured to run the simulations without hassle. To replace this default
directory by another use the option --data-path PATH
like:
./btc_selection --data-path <new-data-path> <command>
In data/fee_rates
you will find the fee rates extracted from coin-selection-simulation.
The files has been adapted to include a block_id
.
All files should be csv files, where each line follows the format:
<block_id>,<fee-rate-per-vKb>
.
The block_id
introduces temporality to these fee rate corpus, allowing the
generation of derived fee rate scenarios focusing on particular periods, with
high fees or rapid changes in the fee rate, for example.
The command to extract the fee rate estimation of each block from a node's blockchain is:
./btc_selection scenario extract-fees --help
In data/transactions
you will find the transactions extracted from coin-selection-simulation.
The files has been adapted to also include a block_id
.
All files should be csv files, where each line follows the format:
<block_id>,<positive-or-negative-transaction-amount-in-bitcoin>
.
The sign of the amount determines if the transaction is an income for the wallet or a payment obligation.
For transaction, the idea of block_id
, is to batch incoming amounts or
outgoing payments in a single transaction. For incoming amounts it doesn't have
any use yet, but for outgoing amounts it allows the creation of multi payment
transactions, i.e., with more than one output besides the possible change
output.
The command to generate transaction profiles based on mathematical distributions is:
./btc_selection scenario create-txs --help
In data/scenarios
you will find the transactions extracted from
coin-selection-simulation,
also with the extra block_id
added.
All files should be csv files, where each line follows the format:
<block_id>,<signed-transaction-amount-in-bitcoin>,<fee-rate-per-vKb>
.
These are the files consumed by the simulator.
You can create multiple scenarios combining the data stored in the
data/fee_rates
and the data/transactions
directories with the command:
./btc_selection scenario generate --help
It combines fee rates with transactions based on the block_id
mentioned
before, so to work properly the range of the ids in the transaction file should
include the range of the ids in the fee rate file.
In the directory data/simulations
are stored the resulting simulations. Its
format is the following:
/data/simulations/
├── <day>_<month>_<year>__<hour>_<minute>_<second>/
│ ├── <scenario-name>/
│ │ ├── <model-name-1>/
│ │ │ ├── failed_txs/
│ │ │ ├── transactions_summary.csv
│ │ │ └── utxo_activity.csv
│ │ └── <model-name-2>/
│ │ ├── failed_txs/
│ │ ├── transactions_summary.csv
│ │ └── utxo_activity.csv
│ └── simulation_summary.json
└── <day>_<month>_<year>__<hour>_<minute>_<second>/
-
transactions_summary.csv: a digest of each transaction produced by a model during the simulation. It records the following fields from first to last on each transaction:
id
: a transaction identifierpolicy
: the name of the algorithm/model applied.balance
: the remaining balance in the wallet after the selection.#wallet
: the remaining amount ofUTxOs
in wallet after the selection.#inputs
: the number of inputs.#payments
: the number of payment outputs.#change
: the number of change ouputs.#negative_effective_valued_utxos
: the number of inputs with negative effective value.excess
: the extra amount of bitcoin released to the miners.change_effective_value
: the effective amount of the change output created.waste
: the value of the Waste metric.fee
: the final feefinal_fee_rate
: the final fee ratetarget_fee_rate
: the fee rate the transaction was trying to achievecpu_time
: the time consumed by the solver/algorithm to produce the transaction without considering other processes.
-
utxo_activity.csv:
block_id
: the identifier of the transaction.wallet_id
: an internal unique identifier for eachUTxO
in the wallet. Payments use the same identifier as they don't belong to the wallet (-1).condition
: the endorsed condition in which theUTxO
participated of the wallet operation.amount
: the amount carried by theUTxO
.
-
failed_txs: a directory to store the failed models when there is an error with the
coin-or/Cbc
solver.
- Run all implemented models over all scenarios:
./btc_selection simulate
- Run bitcoin random coin selection for one model over all scenarios:
./btc_selection simulate --model single-random-draw
- Run bitcoin changeless coin selection for the scenario
random_blocks
../btc_selection simulate --model single-random-draw --scenario random_blocks.csv
- Run all model and scenario combinations except
maximize-effective-value
for all scenarios and the combination of therandom-blocks
scenario with theminimize-waste
model:./btc_selection simulate --exclude "*,maximize-effective-value" --exclude "random-blocks,minimize-waste"