{"id":42997597,"url":"https://github.com/crocs-muni/coinjoin-analysis","last_synced_at":"2026-01-31T03:35:17.418Z","repository":{"id":293422844,"uuid":"903846578","full_name":"crocs-muni/coinjoin-analysis","owner":"crocs-muni","description":"Processing and analysis of datasets created by Wallet Wasabi 1.x, Wallet Wasabi 2.x, Samourai Whirlpool and JoinMarket clients and coordinators","archived":false,"fork":false,"pushed_at":"2025-12-31T21:14:12.000Z","size":18537,"stargazers_count":2,"open_issues_count":11,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-01T04:54:00.371Z","etag":null,"topics":["coinjoin","dumplings","joinmarket","wasabi","whirlpool"],"latest_commit_sha":null,"homepage":"https://coinjoin-stats.github.io/www/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/crocs-muni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-15T17:49:52.000Z","updated_at":"2025-12-31T21:14:16.000Z","dependencies_parsed_at":"2025-07-10T19:07:13.855Z","dependency_job_id":"f877ea73-a598-44dd-9186-100280e3ecbc","html_url":"https://github.com/crocs-muni/coinjoin-analysis","commit_stats":null,"previous_names":["crocs-muni/coinjoin-analysis"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/crocs-muni/coinjoin-analysis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crocs-muni%2Fcoinjoin-analysis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crocs-muni%2Fcoinjoin-analysis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crocs-muni%2Fcoinjoin-analysis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crocs-muni%2Fcoinjoin-analysis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crocs-muni","download_url":"https://codeload.github.com/crocs-muni/coinjoin-analysis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crocs-muni%2Fcoinjoin-analysis/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28928148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T02:59:34.861Z","status":"ssl_error","status_checked_at":"2026-01-31T02:59:05.369Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coinjoin","dumplings","joinmarket","wasabi","whirlpool"],"created_at":"2026-01-31T03:35:16.648Z","updated_at":"2026-01-31T03:35:17.410Z","avatar_url":"https://github.com/crocs-muni.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Wallet Wasabi 1.x, Wallet Wasabi 2.x, Whirlpool and JoinMarket coinjoin analysis \n\nSet of scripts for processing, analysis, and visualization of coinjoin transactions. Performs processing and visualization of 1) real coinjoins as extracted from Bitcoin mainnet by [Dumplings](https://github.com/nopara73/dumplings) tool (no ground truth knowledge about coins to wallets mapping) and 2) base files with coinjoins for  Wallet Wasabi 1.x, Wallet Wasabi 2.x and JoinMarket clients and coordinators executed in emulated environment by [EmuCoinJoin](https://github.com/crocs-muni/coinjoin-emulator) (known mapping between coins and wallets). \n\n## Setup\nClone repository:\n```\ngit clone https://github.com/crocs-muni/coinjoin-analysis.git\ncd coinjoin-analysis\n```\n\nOptional: make Python virtual environment\n```\npython3 -m venv venv\nsource venv/bin/activate\n```\n\nInstall requirements:\n```\npip install -r requirements.txt\n```\n\n\n\n## Supported operations\n\n1. [Process mainnet coinjoins collected by Dumplings (```parse_dumplings.py```)](#process-dumplings)\n    1. [Execute Dumplings tool](#run-dumplings)\n    1. [Parse Dumplings results into intermediate coinjoin_tx_info.json (```--action process_dumplings```)](#process-dumplings)\n    1. [Detect and filter false positives (```--action detect_false_positives```)](#detect-false-positives)\n    1. [Analyze and plot results (```--action plot_coinjoins```)](#plot-coinjoins)\n    1. [Example results](#dumplings-examples)\n1. [Process Wallet Wasabi 2.x emulations from EmuCoinJoin (```parse_cj_logs.py```)](#ecj-process)\n    1. [Execute EmuCoinJoin emulator](#run-ecj)\n    1. [Extract coinjoin information from original raw files (```--action collect_docker```)](#ecj-extract)\n    1. [Re-run analysis from already extracted coinjoins (```--action analyze_only```)](#ecj-rerun)\n    1. [Run client-side analysis from already extracted coinjoins (```ww2_analyze_client.py --action process_emulations```)](#ecj-client-analysis)\n    1. [Example results](#ecj-examples)\n---\n\n\u003ca id=\"process-dumplings\"\u003e\u003c/a\u003e\n## Usage: Parse, analyze, and visualize mainnet coinjoins from Dumplings (```parse_dumplings.py```)\nThis usage scenario processes data from real coinjoins (Wasabi 1.x, Wasabi 2.x, Whirlpool, and others) stored on the Bitcoin mainnet as detected and extracted using [Dumplings tool](https://github.com/nopara73/dumplings). \n\n\u003ca id=\"run-dumplings\"\u003e\u003c/a\u003e\n### 1. Execute Dumplings tool\nSee [Dumplings instructions](https://github.com/nopara73/dumplings?tab=readme-ov-file#1-synchronization) for detailed setup and run of the tool.\n```\ndotnet run --sync --rpcuser=user --rpcpassword=password\n```\nAfter Dumplings tool execution, the relevant files with coinjoin premix, mix, and postmix transactions are serialized as plan files into ```/dumplings_output_path``` folder with the following structure:\n```\n  ..\n  Scanner            (Dumplings results, to be processed)\n  Stats              (Aggregated Dumplings results, not processed at the moment)\n```\n\n\u003ca id=\"process-dumplings\"\u003e\u003c/a\u003e\n### 2. Parse Dumplings results into intermediate coinjoin_tx_info.json (```--action process_dumplings```)\nTo parse coinjoin information from Dumplings files (step 1.) into unified json format (```coinjoin_tx_info.json```) used later for analysis, run:\n```\nparse_dumplings.py --cjtype ww2 --action process_dumplings --target-path path_to_results\n```\nThe example is given for Wasabi 2.x coinjoins (```--cjtype ww2```). Use ```--cjtype ww1``` for Wasabi 1.x or ```--cjtype sw``` for Samourai Whirlpool instead. \n\nThe extraction process creates the following files into a subfolder of ```Scanner``` named after processed coinjoin protocols (e.g., ```\\Scanner\\wasabi2\\```): \n  * ```coinjoin_tx_info.json``` ... basic information about all detected coinjoins, etc.. Used for subsequent analysis.\n  * ```coinjoin_tx_info_extended.json``` ... additional information extracted about coins and wallets. For real coinjoins, the mapping between coins and wallets is mostly unknown, so this information is separated from ```coinjoin_tx_info.json``` to decrease its size and speed up processing.     \n  * ```wasabi2_events.json``` ... Human-readable information about detected coinjoins with most information stripped for readability.\n  * ```wasabi2_inputs_distribution.json``` \n\nAdditionally, a subfolder for every month of detected coinjoin activity is created (e.g., ```2022-06-01 00-00-00--2022-07-01 00-00-00...```), containing ```coinjoin_tx_info.json``` and ```wasabi2_events.json``` with coinjoin transactions created that specific month for easier handling during analysis later (smaller files). \n\nNote that based on the coinjoin protocol analyzed, the name of some files may differ. E.g., ```whirlpool_events.json``` for Samourai Whirlpool or ```wasabi1_events.json``` for Wasabi 1.x. \n\n\u003ca id=\"detect-false-positives\"\u003e\u003c/a\u003e\n### 3. Detect and filter false positives (```--action detect_false_positives```)\nThe Dumplings heuristic coinjoin detection algorithm is not flawless and occasionally selects a transaction that looks like a coinjoin but is not. We, therefore, apply another pass of heuristics to detect such false positives. This step is iterative and requires human interaction to confirm the potential false positives. Note that false positives are *not* directly removed from ```coinjoin_tx_info.json```. Instead, they are filtered after loading based on the content of ```false_cjtxs.json``` file. As a result, only modification of ```false_cjtxs.json``` is required without change of (large) base files like ```coinjoin_tx_info.json``` and can be quickly recomputed.\n\nThe detection in each iteration utilizes already known false positives loaded from ```false_cjtxs.json``` file. You may download pre-prepared files for different coinjoin protocols already manually filtered by us here (file commit date corresponds approximately to ):\n  - Wasabi 1.x: [false_cjtxs.json](https://github.com/crocs-muni/coinjoin-analysis/blob/main/data/wasabi1/false_cjtxs.json)  (last coinjoin 2024-05-30)\n  - Wasabi 2.x: [false_cjtxs.json](https://github.com/crocs-muni/coinjoin-analysis/blob/main/data/wasabi2/false_cjtxs.json)  (new coinjoins still created, needs update)\n  - Whirlpool: [false_cjtxs.json](https://github.com/crocs-muni/coinjoin-analysis/blob/main/data/whirlpool/false_cjtxs.json) (last coinjoin 2024-04-25, empty file, no false positives by Dumplings)\n\nTo perform one iteration of false positives detection (repeat until no new false positives are found):\n\n#### 3.1. Run detection (this command utilizes already known false positives from ```false_cjtxs.json``` file):\n```\nparse_dumplings.py --cjtype ww2 --action detect_false_positives --target-path path_to_results\n```\n#### 3.2. Inspect created file ```no_remix_txs.json``` and ```no_remix_txs_ext.json``` (this file is enriched by name of identified coordinators or clusters) containing *potential* false positives\n\nThe detected potential false positives need to be manually analyzed one by one. If confirmed to be a real false positive, the transaction id shall be placed into ```false_cjtxs.json``` file to be excluded from later analyses. Rerun analysis after each step as by marking some transactions as false positives, additional ones can be identified (e.g., if tx1 is remixed by tx2 false positive, then next pass of analysis will mark tx1 as with no remix after tx2 is removed.) \nHere are some tips for detection of false positives:\n  - 'both_reuse_0_70' txs are almost certainly false positives (too many addresses reused, default threshold is 70% of reused addresses, normal coinjoins have almost all addresses freshly generated). Put them all into false_cjtxs.json and rerun.\n  - 'outputs_address_reuse_0_70' txs are almost certainly false positives as standard coinjoin clients will not reuse addresses heavily. In rare cases, small number of address reuse can be present (e.g., due to  direct registration of output address in WW2's coinjoin pay feature), but it shall not reach the high threshold of 70%.\n  - 'inputs_address_reuse_0_70' txs are very likely false positives, but needs to be manually verified - in rare cases, new fresh mix inflows can be from previously reused addresses, but it shall not reach the high threshold of 70%. \n  - 'both_noremix' txs are transactions with no input and no output connected to other known coinjoin transactions. Very likely a false positive, but it needs to be analyzed one by one to confirm. The exception are the transactions from a corodinator barely reaching the minimum number of inputs set during initial Dumplings scanning (e.g., 20), fluctating around this value. If previous and next coinjoin transactions are below this threshold, the middle transaction will seemingly have no remixes, despite being real coinjoin transaction (and not false positive).  \n  - txs left in \"inputs_noremix\" after all are typically the starting cjtx of some pool (no previous coinjoin was executed).\n  - txs left in \"outputs_noremix\" are typically the last cjtx of some pool - either the pool closed and no longer produces transactions, or is the last mined cjtx(s) wrt Dumpling sync date. \n  - after false positives are confirmed (e.g., at https://mempool.space), put them into false_cjtxs.json \n\n#### 3.3. Repeat the whole process again (=\u003e smaller no_remix_txs.json). \nThe typical stop point is when \"both_noremix\", \"inputs_address_reuse_*\", \"outputs_address_reuse_*\" and \"both_reuse_*\" are empty.\n  \nOnce finished (no new false positives detected), copy ```false_cjtxs.json``` into other folders if multiple pools of the same coinjoin protocol exist (e.g., wasabi2, wasabi2_others, wasabi2_zksnacks)\n\n\u003ca id=\"plot-coinjoins\"\u003e\u003c/a\u003e\n### 4. Analyze and plot results (```--action plot_coinjoins```)\nTo analyze and plot various analysis graphs from processed coinjoins, run:\n```\nparse_dumplings.py --cjtype ww2 --action plot_coinjoins --target-path path_to_results\n```\nThis command generates several files containing an analysis and visualization of executed coinjoins. For visualizations, both png and pdf file formats are generated - use *.pdf where necessary as not all details may be visible in larger *.png files. \n\nThe files are named using the following convention: \n  - ```_values_``` means visualization of values of coinjoin inputs  \n  - ```_nums_``` means visualization of number of coinjoin inputs  \n  - ```_norm_``` means normalization of values before analysis  \n  - ```_notnorm_``` means no normalization is performed before analysis  \n\nThe following files are generated:\n  - ```*_remixrate_[values/nums]_[norm/notnorm].json``` contains remix rate (fraction of incoming value or number of inputs coming from previous coinjoins) for each coinjoin transaction. remix_ratios_all considers all inputs, remix_ratios_std considers only inputs with Wasabi 2.x standard denomination, and remix_ratios_nonstd only inputs with non-standard denomination.   \n- ```*_cummul_[values/nums]_[norm/notnorm].pdf``` contains visualization of whole period aggregated per week.\n  - ```*_input_[values/nums]_[norm/notnorm].pdf``` contains visualization of coinjoins splitted per each month.  \n\n\u003ca id=\"dumplings-examples\"\u003e\u003c/a\u003e\n### 5. Example results\nVizualized liquidity changes in Wasabi 1.x, Wasabi 2.x and Whirlpool coinjoins \n![image](https://github.com/user-attachments/assets/33af36a6-8650-47dc-b92a-f5c611962b72)\n\nValue of Wasabi 2.x coinjoin inputs during (March-August 2023): \n![image](https://github.com/user-attachments/assets/2a79e7ca-8a81-42c0-9c5e-3296132893c1)\n\nNormalized ratio of different input types of Wasabi 2.x coinjoin inputs during (June-November 2023): \n![image](https://github.com/user-attachments/assets/3a6c69b8-b850-4176-973b-35422f6a111b)\n\nValue of Wasabi 2.x coinjoins for post-zkSNACKS coordinators (June-December 2024): \n![image](https://github.com/user-attachments/assets/69ebb029-83f0-493c-bbb7-11b9b86fd746)\n\n---\n\n\u003ca id=\"ecj-process\"\u003e\u003c/a\u003e\n## Usage: Parse Wallet Wasabi 2.x emulations from EmuCoinJoin (```parse_cj_logs.py```)\nThe scenario assumes the previous execution of Wasabi 2.x and JoinMarket coinjoins (produced by containerized coordinator and clients) using [EmuCoinJoin](https://github.com/crocs-muni/coinjoin-emulator) orchestration tool. \n\n\u003ca id=\"run-ecj\"\u003e\u003c/a\u003e\n### 1. Execute EmuCoinJoin emulator\nSee [EmuCoinJoin](https://github.com/crocs-muni/coinjoin-emulator) for a detailed setup and run of the tool.\nAfter EmuCoinJoin execution, relevant files from containers are serialized as subfolders into ```/path_to_experiments/experiment_1/data/``` folder with the following structure. \n```\n  ..\n  btc-node           (bitcoin core, regtest blocks)\n  wasabi-backend     (wasabi 2.x coordinator container)\n  wasabi-client-000  (wasabi 2.x client logs)\n  wasabi-client-001\n  ...  \n  wasabi-client-499\n```\nNote that multiple experiments can be stored inside the ```/path_to_experiments/``` path. All found folders are checked for the ```/data/``` subfolder, and if found, the experiment is processed.\n\n\u003ca id=\"ecj-extract\"\u003e\u003c/a\u003e\n### 2. Extract coinjoin information from original raw files (```--action collect_docker```)\nTo extract all executed coinjoins into a unified json format and perform analysis, run:\n```\nparse_cj_logs.py --action collect_docker --target-path path_to_experiments\n```\n\nThe extraction process creates the following files: \n  * ```coinjoin_tx_info.json``` ... basic information about all detected coinjoins, mapping of all wallets to their coins, started rounds, etc.. Used for subsequent analysis.\n  * ```wallets_coins.json``` ... information about every output created during execution, mapped to its coinjoin.\n  * ```wallets_info.json``` ... information about every address controlled by a given wallet. \n\n\u003ca id=\"ecj-rerun\"\u003e\u003c/a\u003e\n### 3. Re-run analysis from already extracted coinjoins (```--action analyze_only```)\nThe coinjoin extraction part is time-consuming. If new analysis methods are added or updated, only the analysis part can be rerun. To execute again only analysis (extraction must be already done with files like ```coinjoin_tx_info.json``` already created), run:\n```\nparse_cj_logs.py --action analyze_only --target-path path_to_experiments\n```\n\nIf the analysis finishes successfully, the following files are created:\n  * ```coinjoin_stats.3.pdf, coinjoin_stats.3.pdf``` ... multiple graphs capturing various analysis results obtained from coinjoin data. \n  * ```coinjoin_tx_info_stats.json``` ... captures information about the participation of every wallet in a given coinjoin transaction.\n\n\n\u003ca id=\"ecj-client-analysis\"\u003e\u003c/a\u003e\n### 4. Run client-side analysis from already extracted coinjoins  (``ww2_analyze_client.py --action process_emulations```)\nAdditional analysis of client-side data collected from emulations or real coinjoin participation In both cases, ```coinjoin_tx_info.json``` files are already expected to exist, run:\n```\nww2_analyze_client.py --action process_emulations --target-path path_to_experiments\n```\n\nIf the analysis finishes successfully, the following files are created:\n  * ```asX_coinjoin_stats.pdf/png``` ... multiple graphs capturing various analysis results obtained from client-side coinjoin data. \n  * ```emu_stats.json`` ... raw values used to generate asX_coinjoin_stats.pdf/png graphs\n  * ```coinjoin_tx_info_stats.json`` ... basic extracted statistics for each wallet \n  * ```emu_coinjoin_tx_info.json`` ... coinjoin_tx_info.json with additional metadata extracted\n\n\n\n\u003ca id=\"ecj-examples\"\u003e\u003c/a\u003e\n### 5. Example results\n![image](https://github.com/user-attachments/assets/2e5406bc-b8f8-4725-8ff9-6484e805f682)\n\n![image](https://github.com/user-attachments/assets/5325a4ae-468b-4b52-b58f-95d521c15b1c)\n\n---\n\n## Similar and related projects\n\n[Dumplings project](https://github.com/nopara73/dumplings): Extraction of Wasabi 1.0, Wasabi 2.0, Whirlpool and other equal output (potential) coinjoin transactions. Written in C#, used by this repository for basic extraction. Very limited analysis. \n\n[Ashi-Whirlpool-Analysis](https://github.com/Ziya-Sadr/Ashi-Whirlpool-Analysis): Analysis of Ashigaru Whirlpool: Unspent Capacity \u0026 Anonymity Sets. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrocs-muni%2Fcoinjoin-analysis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrocs-muni%2Fcoinjoin-analysis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrocs-muni%2Fcoinjoin-analysis/lists"}