/jm_unmixer

JoinMarket Transaction Unmixer

Primary LanguagePythonMIT LicenseMIT

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$$$$$$$   JOINMARKET UNMIXER   $$$$$$$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$


==== ABOUT ====

This is a JoinMarket ("JM") transaction unmixing tool.  It includes scripts for finding JM
transactions ("jmtxs") on the blockchain, for analyzing the jmtxs found, and for inspecting
the "unmixed" txs.

A jmtx is unmixed by ruling out outputs from belonging to a taker.  In an N-party jmtx, if
the tool finds N-1 of the "mix" outputs to belong to makers, the tx is considered unmixed,
because we know (with high certainty) the last mix output belongs to the taker.
If the tool finds fewer than N-1 outputs to belong to makers (but >0), the tx is considered
partially unmixed. That is still meaningful, because the mixing was less effective than the
taker expected (and paid for), although it wasn't completely worthless.

==== CONTACT ====

Feel free to contact me at: fungibit@yandex.com
PGP fingerprint: A42F BB89 217E 4C20 F61C B88E 6A7D 2F9B C7BD E719
Bitcoin address for donations: 1J9xoksroyBQkVCoDZMUxBHympRpJVWmeB


==== DISCLAIMER ====

This is a prototype-quality proof-of-concept-level implementation.  It probably contains many
bugs.  Many modification and improvements can be added.

Feel free to clone it and/or submit pull requests.


==== REQUIREMENTS ====

In order to run the analysis, you would need:

* bitcoin rpc server, running with txindex=1
* python3
* numpy
* bitcoinrpc (pip install python-bitcoinrpc)
* click (pip install click) -- optional, for displaying nice progress bars


==== SETUP ====

get source:
 % git clone https://github.com/fungibit/jm_unmixer

set PYTHONPATH appropriately:
 % export PYTHONPATH=/path/to/jm_unmixer/
To test PYTHONPATH:
 % python3 -c 'import jm_unmixer; print("ok")'

Make sure BTCRPC_CONFIG_FILE variable is set correctly in btccommon.py.
 % python3 -c 'import os; from jm_unmixer.btccommon import BTCRPC_CONFIG_FILE as conf; print("%s: %s" % (conf, "ok" if os.path.exists(conf) else "DOES NOT EXIST"))'
If not, edit jm_unmixer/btccommon.py accordingly.


==== RUNNING IT ====

STEP 1: Collecting JMTXs

Run find_jmtxs.py script on all the blocks you want to scan.  E.g.:
 % python3 bin/find_jmtxs.py 360000 420000 -o data/jmtxs.pkl
Notes:
* There is no point scanning before block 360000 (no JMTXs)
* This step can take a very long time (a few days) to complete. Feel free to split it to chunks
  and/or play with the "-w" (num_workers) cli option.
* Instead of waiting this long, you may use the included txids.txt file (data/txids.txt), which
  includes jmtxids up to block 419999. Run:
 % python3 bin/find_jmtxs.py data/txids.txt -o data/jmtxs.pkl
* You can also use a hybrid approach: use data/txids.txt for blocks up to 419999, and run the
  "full" scan starting block 420000 (and use the two output files together in STEP 2)

STEP 2: Analyzing JMTXs

Run analyze_jmtxs.py on the jmtxs generated in the previous step:
 % python3 bin/analyze_jmtxs.py data/jmtxs.pkl -o data/tainted_jmtxs.pkl
Notes:
* This step can take an hour or more to complete
* You may pass multiple jmtxs files

STEP 3: Inspecting Unmixed JMTXs

Run print_unmixed_jmtx.py on the tainted jmtxs generated in the previous step:
 % python3 bin/print_unmixed_jmtx.py -i data/tainted_jmtxs.pkl
Notes:
* The script will automatically select the "most impressive" unmixed tx, but you can also choose
  another tx to inspect