mirror of
https://github.com/JoinMarket-Org/joinmarket-clientserver.git
synced 2026-08-15 12:50:57 +02:00
116 lines
4.4 KiB
Python
Executable file
116 lines
4.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
description="""Find SNICKER candidate transactions on
|
|
the blockchain.
|
|
|
|
Using a connection to Bitcoin Core, which allows retrieving
|
|
full blocks, this script will list the transaction IDs of
|
|
transactions that fit the pattern of SNICKER, as codified in
|
|
https://gist.github.com/AdamISZ/2c13fb5819bd469ca318156e2cf25d79
|
|
and as checked in the `jmbitcoin.snicker` module function
|
|
`is_snicker_tx`, and also optionally, transactions that fit the
|
|
pattern of Joinmarket coinjoins (see -j).
|
|
|
|
Pass a starting and finishing block value as argument. If the
|
|
finishing block is not provided, it is assumed to be the latest
|
|
block.
|
|
|
|
**Note that this is slow.**
|
|
|
|
This script does *NOT* require a wallet, but it does require
|
|
a connection to Core, so does not work with `no-blockchain`.
|
|
Note that this script obviates the need to have txindex enabled
|
|
in Bitcoin Core in order to get full transactions, since it
|
|
parses the raw blocks.
|
|
"""
|
|
|
|
import sys
|
|
from optparse import OptionParser
|
|
from jmbase import bintohex, EXIT_ARGERROR, jmprint
|
|
import jmbitcoin as btc
|
|
from jmclient import (jm_single, add_base_options, load_program_config,
|
|
check_regtest)
|
|
from jmclient.configure import get_log
|
|
|
|
log = get_log()
|
|
|
|
def found_str(ttype, tx, b):
|
|
return "Found {} transaction: {} in block: {}".format(
|
|
ttype, bintohex(tx.GetTxid()[::-1]), b)
|
|
|
|
def write_candidate_to_file(ttype, candidate, blocknum, unspents, filename):
|
|
""" Appends the details for the candidate
|
|
transaction to the chosen textfile.
|
|
"""
|
|
with open(filename, "a") as f:
|
|
f.write(found_str(ttype, candidate, blocknum) + "\n")
|
|
f.write(btc.human_readable_transaction(candidate)+"\n")
|
|
f.write("Full transaction hex for creating a proposal is "
|
|
"found in the above.\n")
|
|
f.write("The unspent indices are: " + " ".join(
|
|
(str(u) for u in unspents)) + "\n")
|
|
def main():
|
|
parser = OptionParser(
|
|
usage=
|
|
'usage: %prog [options] startingblock [endingblock]',
|
|
description=description
|
|
)
|
|
add_base_options(parser)
|
|
parser.add_option('-f',
|
|
'--filename',
|
|
action='store',
|
|
type='str',
|
|
dest='candidate_file_name',
|
|
help='filename to write details of candidate '
|
|
'transactions, default ./candidates.txt',
|
|
default='candidates.txt')
|
|
parser.add_option(
|
|
'-j',
|
|
'--include-jm',
|
|
action='store_true',
|
|
dest='include_joinmarket',
|
|
default=True,
|
|
help="scan for Joinmarket coinjoin outputs, as well as SNICKER.")
|
|
|
|
(options, args) = parser.parse_args()
|
|
load_program_config(config_path=options.datadir)
|
|
if len(args) not in [1,2]:
|
|
log.error("Invalid arguments, see --help")
|
|
sys.exit(EXIT_ARGERROR)
|
|
|
|
startblock = int(args[0])
|
|
if len(args) == 1:
|
|
endblock = jm_single().bc_interface.get_current_block_height()
|
|
else:
|
|
endblock = int(args[1])
|
|
|
|
check_regtest()
|
|
|
|
for b in range(startblock, endblock + 1):
|
|
block = jm_single().bc_interface.get_block(b)
|
|
for t in btc.get_transactions_in_block(block):
|
|
if btc.is_snicker_tx(t):
|
|
log.info(found_str("SNICKER", t, b))
|
|
# get list of unspent outputs; if empty, skip,
|
|
# otherwise, persist to candidate file with unspents
|
|
# marked.
|
|
unspents = jm_single().bc_interface.get_unspent_indices(t)
|
|
if len(unspents) == 0:
|
|
continue
|
|
write_candidate_to_file("SNICKER", t, b, unspents,
|
|
options.candidate_file_name)
|
|
# note elif avoids wasting computation if we already found SNICKER:
|
|
elif options.include_joinmarket:
|
|
cj_amount, n = btc.is_jm_tx(t)
|
|
# here we don't care about the stats; the tx is printed anyway.
|
|
if cj_amount:
|
|
log.info(found_str("Joinmarket coinjoin", t, b))
|
|
unspents = jm_single().bc_interface.get_unspent_indices(t)
|
|
if len(unspents) == 0:
|
|
continue
|
|
write_candidate_to_file("Joinmarket coinjoin", t, b,
|
|
unspents, options.candidate_file_name)
|
|
log.info("Finished processing block: {}".format(b))
|
|
if __name__ == "__main__":
|
|
main()
|
|
jmprint('done', "success")
|