Skip to content

Instantly share code, notes, and snippets.

@ottosch
Last active January 18, 2023 22:06
Show Gist options
  • Save ottosch/c5ba96ff296ddf95b36e498654faf871 to your computer and use it in GitHub Desktop.
Save ottosch/c5ba96ff296ddf95b36e498654faf871 to your computer and use it in GitHub Desktop.
A script to help using ymgve's bitcoin_fork_claimer script
#!/usr/bin/env python
# This script is meant to be used with bitcoin_fork_claimer: https://github.com/ymgve/bitcoin_fork_claimer
# The outputs of this script are the inputs to that script.
# Python 2.x is required
import urllib2
import json
import sys
import collections
# Insert your BTC addresses, one per line
addresses = """
15ZvPgCkTrkKsUzw8PaianK6W7sZQhTMK1
1HTmbaeSZn7faPjxcSeEHJoxgBGMxJYYem
"""
# Forks to check. No need to touch, unless you want to add or remove a fork
fork_list = {
"CLAM": { "name": "Clam", "block": 300377 },
"BCH": { "name": "BCash", "block": 478559 },
"BCD": { "name": "Bitcoin Diamond", "block": 495866 },
"SBTC": { "name": "Super Bitcoin", "block": 498888 },
"BTG": { "name": "Bitcoin Gold", "block": 491407 },
"B2X": { "name": "Segwit2x", "block": 501451 },
"BCX": { "name": "BitcoinX", "block": 498888 },
"BTP": { "name": "BitcoinPay", "block": 499345},
"BTF": { "name": "Bitcoin Faith", "block": 500000 },
"BPA": { "name": "Bitcoin Pizza", "block": 501888},
"BTH": { "name": "Bitcoin Hot", "block": 498848 },
"BTN": { "name": "Bitcoin New", "block": 501000 },
"BTW": { "name": "Bitcoin World", "block": 499777 },
"BTV": { "name": "Bitcoin Vote", "block": 505050 },
"BTT": { "name": "Bitcoin Top", "block": 501118 },
"WBTC": { "name": "World Bitcoin", "block": 503888 },
"BTSQ": { "name": "Bitcoin Community", "block": 506066 },
"BICC": { "name": "BitClassic Coin", "block": 498888 },
"UBTC": { "name": "United Bitcoin", "block": 498777 },
"BTCP": { "name": "Bitcoin Private", "block": 511346 },
"LBTC": { "name": "Lightning Bitcoin", "block": 499999 },
"GOD": { "name": "Bitcoin God", "block": 501226 }
}
desired_forks = {}
def main():
addr_list = addresses.strip().split("\n")
addr_list = [addr.strip() for addr in addr_list]
global desired_forks
desired_forks = get_desired_forks()
if len(desired_forks) == 0:
print "Retrieving all forks..."
print
desired_forks = fork_list
# Add balance entry per fork
for coincode, coindata in desired_forks.viewitems():
coindata["balances"] = {}
for addr in addr_list:
coindata["balances"][addr] = 0
for addr in addr_list:
a = urllib2.urlopen("https://blockchain.info/rawaddr/" + addr).read()
txs = json.loads(a)["txs"]
for coincode, coindata in desired_forks.viewitems():
valid = process_txs(addr, txs, coindata)
for value in valid:
if not coindata.has_key("commands"):
coindata["commands"] = []
coindata["commands"].append(" python claimer.py " + coincode + " " + " ".join(value) + " " + coincode + "_ADDR")
coindata["total_value"] = sum(coindata["balances"].values())
if not "-balance" in sys.argv:
print_commands()
print_balances()
def process_txs(addr, txs, coin):
txs_before_fork = [tx for tx in txs if tx.has_key("block_height") and tx["block_height"] <= coin["block"]]
valid_txs = txs_before_fork[:]
valid = []
# Remove spent transactions
for txid in valid_txs[:]:
for tx in txs_before_fork:
tx_from_mining = tx["vin_sz"] == 1 and not tx["inputs"][0].has_key("prev_out")
if tx_from_mining:
continue
for input_tx in tx["inputs"]:
if input_tx["prev_out"]["tx_index"] == txid["tx_index"] and input_tx["prev_out"]["addr"] == addr:
try:
valid_txs.remove(txid)
except ValueError:
pass # Was probably removed before. Skipping.
for tx in valid_txs:
for tx_out in tx["out"]:
if addr == tx_out["addr"]:
coin["balances"][addr] += tx_out["value"]
valid.append([tx["hash"], "PRIV_KEY_OF_" + addr, addr])
break
return valid
def print_commands():
for coincode, coindata in desired_forks.viewitems():
if coindata.has_key("commands"):
print coindata["name"] + " (" + coincode + ")"
print "\n".join(coindata["commands"])
print
def print_balances():
decimals = 100000000.0
for coincode, coindata in desired_forks.viewitems():
if coindata["total_value"] > 0:
print
coin_fmt = (coindata["name"] + " (" + coincode + ")").ljust(50, " ")
total_fmt = format((coindata["total_value"] / decimals), ".8f")
print coin_fmt + total_fmt.rjust(15, " ") + " BTC"
ordered_addresses = collections.OrderedDict(sorted(coindata["balances"].items(), reverse=True, key=lambda t: t[1]))
for addr, balance in ordered_addresses.viewitems():
if balance > 0:
addr_fmt = addr.ljust(50, " ")
balance_fmt = format((balance / decimals), ".8f")
print addr_fmt + balance_fmt.rjust(15, " ") + " BTC "
def get_cli_args():
if len(sys.argv) == 1:
print "You can also specify which forks you want. Example: python " + sys.argv[0] + " btv bcx"
return None
return [arg.upper() for arg in sys.argv[1:]]
def get_desired_forks():
cli_args = get_cli_args()
if cli_args is None:
return {}
return { k : v for k, v in fork_list.iteritems() if k in cli_args }
main()
@emanuelelaface
Copy link

emanuelelaface commented Feb 4, 2018

Hi, I did a little change in order to be easier to adapt to the needs of the user. In practice I specify the addresses and the private keys in a comma separated value text file as well as the fork_list adding also the address to send.
I load then the two files with

with open('keys') as f:
    lines = f.read().splitlines()
wallet={}
for line in lines:
    addr, key = line.split(',')
    wallet[addr]=key

with open('fork_list') as f:
    lines = f.read().splitlines()
fork_list={}
for line in lines:
    if line[0] != '#':
        coin, name, block, address = line.split('#')[0].split(',')
        fork_list[coin]={'name': name, 'block': int(block), 'address': address}

and I call the address of the fork as

coindata["commands"].append("python claimer.py " + coincode + " " + " ".join(value) + " " + fork_list[coincode]['address'])

and the private key as

valid.append([tx["hash"], wallet[addr], addr])

Maybe you can add this as an option in your code.

@serevan
Copy link

serevan commented Mar 22, 2018

Hi, it doesn't work with p2wpkh-p2sh, p2sh private keys

@host2gate
Copy link

claimer.py is not accepting bch addr.
I added BCH fork details to your script.

Getting below error when trying with a BCH dest address

Candidate transaction, index 1 with 107889 Satoshis (0.00107889 BTC)
Traceback (most recent call last):
File "C:\Sundar\BTC\bitcoin_fork_claimer-master\claimer.py", line 1356, in
rawaddr = b58decode(destaddr)
File "C:\Sundar\BTC\bitcoin_fork_claimer-master\claimer.py", line 68, in b58decode
n = n * 58 + b58ab.index(c)
ValueError: substring not found

@P0u1us
Copy link

P0u1us commented Aug 16, 2018

Hi, very much appreciate the tool, in combination with fork_claimer excellent,
EXCEPT... it is worthy to mention that blockchain API limits the number of TXIDs to 50 !!! and thus the tool does not further show older/remaining TXIDs;
I needed to manually modify and add "?offset=50/100/etc to get all TXIDs ;
just needed to mention this ;-)

@woeisme
Copy link

woeisme commented Nov 18, 2019

Can you add support for CLAMS?

@ottosch
Copy link
Author

ottosch commented Nov 20, 2019

Can you add support for CLAMS?

Done.

To all others that commented before, I'm sorry but I didn't see your messages. This is the first time I received an e-mail about someone saying something here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment