From f78ac8dc10e58e7d048631a0927a5af6c112b57c Mon Sep 17 00:00:00 2001 From: mrbianchi Date: Wed, 8 May 2019 17:29:50 -0300 Subject: [PATCH 1/2] added broadcaster --- README.md | 10 + broadcaster.py | 1338 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1348 insertions(+) create mode 100644 broadcaster.py diff --git a/README.md b/README.md index ec8b7f8..3bc853b 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,14 @@ fbtcclaimer.py also requires aes.py to be in the same folder as the script. Than --- +--- + +There is another python script for just broadcast transactions. + +Usage: + + broadcaster.py + +--- + Any donations can be sent to BTC address `13qtaKvBQYN9RQuegqF8Ci7U7JgTtT8SNz` diff --git a/broadcaster.py b/broadcaster.py new file mode 100644 index 0000000..ffecb89 --- /dev/null +++ b/broadcaster.py @@ -0,0 +1,1338 @@ +import hashlib, os, struct, sys, socket, time, urllib2, json, argparse, cStringIO, traceback, hmac, ssl + +N = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fL +R = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141L +A = 0L +B = 7L +gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798L +gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L + +b58ab = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +bech32ab = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + +def bech32_polymod(values): + GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + chk = 1 + for v in values: + b = (chk >> 25) + chk = (chk & 0x1ffffff) << 5 ^ v + for i in range(5): + chk ^= GEN[i] if ((b >> i) & 1) else 0 + return chk + +def bech32_hrp_expand(s): + return [ord(x) >> 5 for x in s] + [0] + [ord(x) & 31 for x in s] + +def bech32decode(addr): + hrp, data = addr.lower().rsplit("1", 1) + data = [bech32ab.index(c) for c in data] + + assert bech32_polymod(bech32_hrp_expand(hrp) + data) == 1, "bech32 checksum failed" + assert data[0] == 0, "only support version 0 witness for now" + data = data[1:-6] + + n = 0 + for c in data: + n = n << 5 | c + + nbytes, extrabits = divmod(len(data) * 5, 8) + return long2byte(n >> extrabits, nbytes) + +def bech32encode(hrp, s): + extrabits = (5 - ((len(s) * 8) % 5)) % 5 + nchars = (len(s) * 8 + extrabits) / 5 + n = byte2long(s) << extrabits + + data = [] + for i in xrange(nchars): + data.insert(0, n & 31) + n >>= 5 + + data.insert(0, 0) # version 0 + + values = bech32_hrp_expand(hrp) + data + polymod = bech32_polymod(values + [0,0,0,0,0,0]) ^ 1 + data += [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] + return hrp + "1" + "".join(bech32ab[c] for c in data) + +def b58csum(s): + return hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4] + +def b58decode(s, checksum=True): + idx = 0 + while s[idx] == "1": + idx += 1 + + n = 0 + for c in s[idx:]: + n = n * 58 + b58ab.index(c) + + res = long2byte(n) + res = idx * "\x00" + res + + if checksum: + res, cs = res[:-4], res[-4:] + assert cs == b58csum(res), "base58 checksum failed" + + return res + +def b58encode(s, checksum=True): + if checksum: + s += b58csum(s) + + idx = 0 + while s[idx] == "\x00": + idx += 1 + + n = byte2long(s) + res = "" + while n > 0: + res = b58ab[n % 58] + res + n /= 58 + + return "1" * idx + res + +def byte2long(s): + res = 0 + for c in s: + res = (res << 8) | ord(c) + return res + +def long2byte(n, sz=None): + res = "" + while n > 0: + res = chr(n & 0xff) + res + n >>= 8 + + if sz is not None: + res = res.rjust(sz, "\x00") + + return res + +def read_varint(st): + value = ord(st.read(1)) + if value < 0xfd: + return value + if value == 0xfd: + return struct.unpack("> 1 + + return res + +def der_signature(r, s): + r = long2byte(r) + if ord(r[0]) >= 0x80: + r = "\x00" + r + + s = long2byte(s) + if ord(s[0]) >= 0x80: + s = "\x00" + s + + res = "\x02" + lengthprefixed(r) + "\x02" + lengthprefixed(s) + return "\x30" + lengthprefixed(res) + +def gen_k_rfc6979(privkey, m): + h1 = hashlib.sha256(m).digest() + x = long2byte(privkey, 32) + V = "\x01" * 32 + K = "\x00" * 32 + K = hmac.new(K, V + "\x00" + x + h1, hashlib.sha256).digest() + V = hmac.new(K, V, hashlib.sha256).digest() + K = hmac.new(K, V + "\x01" + x + h1, hashlib.sha256).digest() + V = hmac.new(K, V, hashlib.sha256).digest() + + while True: + V = hmac.new(K, V, hashlib.sha256).digest() + k = byte2long(V) + if k >= 1 and k < R: + return k + + K = hmac.new(K, V + "\x00", hashlib.sha256).digest() + V = hmac.new(K, V, hashlib.sha256).digest() + +def signdata(privkey, data): + h = doublesha(data) + r, s = sign(privkey, h) + return der_signature(r, s) + +def sign(privkey, h): + z = byte2long(h) + k = gen_k_rfc6979(privkey, h) + + p = scalar_mul(k, Point(gx, gy), N) + r = p.x % R + assert r != 0 + + ki = modinv(k, R) + s = (ki * (z + r * privkey)) % R + assert s != 0 + if s > (R / 2): + s = R - s + + return r, s + +def serializepubkey(p, compressed): + if compressed: + if p.y & 1 == 1: + return "\x03" + long2byte(p.x, 32) + else: + return "\x02" + long2byte(p.x, 32) + else: + return "\x04" + long2byte(p.x, 32) + long2byte(p.y, 32) + +def doublesha(s): + s = hashlib.sha256(s).digest() + return hashlib.sha256(s).digest() + +def hash160(s): + s = hashlib.sha256(s).digest() + h = hashlib.new("ripemd160") + h.update(s) + return h.digest() + +def pubkey2h160(p, compressed): + s = serializepubkey(p, compressed) + return hash160(s) + +def pubkey2segwith160(p): + s = pubkey2h160(p, 1) + return hash160("\x00\x14" + s) + +def pubkey2addr(p, compressed): + s = pubkey2h160(p, compressed) + return b58encode("\x00" + s) + +def pubkey2segwitaddr(p): + s = pubkey2segwith160(p) + return b58encode("\x05" + s) + +def wif2privkey(s): + s = b58decode(s) + keytype = ord(s[0]) + + if len(s) == 34 and s[-1] == "\x01": + compressed = 1 + elif len(s) == 33: + compressed = 0 + else: + raise Exception("Unknown private key WIF format!") + + return keytype, byte2long(s[1:33]), compressed + +def identify_keytype(wifkey, addr): + if addr.startswith("bc1"): + addrh160 = bech32decode(addr) + assert len(addrh160) == 20 + + privkeytype, privkey, compressed = wif2privkey(args.wifkey) + pubkey = scalar_mul(privkey, Point(gx, gy), N) + if addrh160 == pubkey2h160(pubkey, 1): + return "segwitbech32", privkey, pubkey, addrh160, 1 + + raise Exception("Unable to identify key type!") + else: + addrh160 = b58decode(addr)[-20:] + assert len(addrh160) == 20 + + privkeytype, privkey, compressed = wif2privkey(args.wifkey) + pubkey = scalar_mul(privkey, Point(gx, gy), N) + if addrh160 == pubkey2h160(pubkey, 0): + return "standard", privkey, pubkey, addrh160, 0 + + if addrh160 == pubkey2h160(pubkey, 1): + return "standard", privkey, pubkey, addrh160, 1 + + if addrh160 == pubkey2segwith160(pubkey): + return "segwit", privkey, pubkey, pubkey2h160(pubkey, 1), 1 + + raise Exception("Unable to identify key type!") + +def get_tx_details_from_blockchaininfo(txid, addr, hardforkheight): + print "Querying blockchain.info API about data for transaction %s" % txid + res = urllib2.urlopen("https://blockchain.info/rawtx/%s" % txid) + txinfo = json.loads(res.read()) + if hardforkheight < txinfo["block_height"]: + print "\n\nTHIS TRANSACTION HAPPENED AFTER THE COIN FORKED FROM THE MAIN CHAIN!" + print "(fork at height %d, this tx at %d)" % (hardforkheight, txinfo["block_height"]) + print "You will most likely be unable to claim these coins." + print "Please look for an earlier transaction before the fork point.\n\n" + get_consent("I will try anyway") + + found = None + for outinfo in txinfo["out"]: + if "addr" in outinfo and outinfo["addr"] == addr: + txindex = outinfo["n"] + script = outinfo["script"].decode("hex") + satoshis = outinfo["value"] + print "Candidate transaction, index %d with %d Satoshis (%.8f BTC)" % (txindex, satoshis, satoshis / 100000000.0) + if found is None: + found = txindex, script, satoshis + else: + raise Exception("Multiple outputs with that address found! Aborting!") + + if not found: + raise Exception("No output with address %s found in transaction %s" % (addr, txid)) + + return found + +def get_btx_details_from_chainz_cryptoid(addr): + print "Querying chainz.cryptoid.info about last unspent transaction for address" + url = "https://chainz.cryptoid.info/btx/api.dws?q=unspent&active=%s&key=a660e3112b78" % addr + + request = urllib2.Request(url) + request.add_header('User-Agent', 'Mozilla/5.0') + opener = urllib2.build_opener() + res = opener.open(request) + + txinfo = json.loads(res.read()) + unspent_outputs = txinfo["unspent_outputs"] + if len(unspent_outputs) == 0: + raise Exception("Block explorer didn't find any coins at that address") + + outinfo = unspent_outputs[0] + txid = outinfo["tx_hash"] + txindex = outinfo["tx_ouput_n"] + script = outinfo["script"].decode("hex") + satoshis = int(outinfo["value"]) + + return txid, txindex, script, satoshis + +def get_coin_details_from_electrum(coin, targettxid, sourceh160, keytype): + if keytype in ("segwit", "segwit_btcp"): + addr = b58encode(coin.SCRIPT_ADDRESS + hash160("\x00\x14" + sourceh160)) + else: + addr = b58encode(coin.PUBKEY_ADDRESS + sourceh160) + + sc = socket.create_connection((coin.electrum_server, coin.electrum_port)) + if coin.electrum_ssl: + sc = ssl.wrap_socket(sc) + sc.send('{ "id": 0, "method": "blockchain.address.listunspent", "params": [ "%s" ] }\n' % addr) + res = readline(sc) + + j = json.loads(res) + unspents = j["result"] + if len(unspents) == 0: + raise Exception("No %s at this address!" % coin.ticker) + + if len(unspents) == 1: + target = unspents[0] + else: + target = None + for tx in unspents: + if tx["tx_hash"] == targettxid: + target = tx + break + + if target is None: + print "Multiple potential outputs possible - please use one of these TXIDs to claim" + for tx in unspents: + coinamount = int(tx["value"]) * coin.coinratio / 100000000.0 + btcamount = int(tx["value"]) / 100000000.0 + print " TXID %s : %20.8f %s (equivalent to %.8f BTC)" % (tx["tx_hash"], coinamount, coin.ticker, btcamount) + + exit() + + + return target["tx_hash"], int(target["tx_pos"]), None, int(target["value"]) + +def readline(sc): + res = "" + while True: + c = sc.recv(1) + if c == "": + raise Exception("Disconnect when querying electrum server") + elif c == "\n": + break + else: + res += c + + return res + +def get_consent(consentstring): + print "\nWrite '%s' to continue" % consentstring + + answer = raw_input() + #if answer != consentstring: + # raise Exception("User did not write '%s', aborting" % consentstring) + +class Client(object): + + _MAX_MEMPOOL_CHECKS = 5 + _MAX_CONNECTION_RETRIES = 100 + + def __init__(self, coin): + self.coin = coin + self._transaction_sent = False + self._transaction_accepted = None + self._mempool_check_count = 0 + self._connection_retries = 0 + + def send(self, cmd, msg): + magic = struct.pack(" %s (%d bytes)" % (repr(cmd), len(msg)) + + def recv_msg(self): + def recv_all(length): + ret = "" + while len(ret) < length: + temp = self.sc.recv(length - len(ret)) + if len(temp) == 0: + raise socket.error("Connection reset!") + ret += temp + return ret + + header = recv_all(24) + if len(header) != 24: + raise Exception("INVALID HEADER LENGTH\n%s" % repr(header)) + + cmd = header[4:16].rstrip("\x00") + payloadlen = struct.unpack("BBBBH", data[24:30]) + else: + address = "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%d" % struct.unpack(">HHHHHHHHH", data[12:30]) + print "\tGot peer address: %s" % address + elif cmd not in ('sendcmpct', 'verack'): + print repr(cmd), repr(payload) + + except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: + if self._connection_retries >= self._MAX_CONNECTION_RETRIES: + raise + print "\tConnection failed with: %s" % repr(e) + print "\tWill retry %d more times." % (self._MAX_CONNECTION_RETRIES - self._connection_retries) + serverindex = (serverindex + 1) % len(self.coin.seeds) + self._connection_retries += 1 + time.sleep(2) + + +class BitcoinFork(object): + def __init__(self): + self.coinratio = 1.0 + self.versionno = 70015 + self.maketx = self.maketx_segwitsig + self.extrabytes = "" + self.BCDgarbage = "" + self.BCLsalt = "" + self.txversion = 1 + self.signtype = 0x01 + self.signid = self.signtype + self.PUBKEY_ADDRESS = chr(0) + self.SCRIPT_ADDRESS = chr(5) + self.bch_fork = False + self.address_size = 21 + self.electrum_server = None + + def maketx_segwitsig(self, sourcetx, sourceidx, sourceh160, signscript, sourcesatoshis, sourceprivkey, pubkey, compressed, outputs, fee, keytype): + verifytotal = fee + + version = struct.pack(" Date: Tue, 3 Aug 2021 22:42:36 -0300 Subject: [PATCH 2/2] Update README.md --- README.md | 122 +----------------------------------------------------- 1 file changed, 1 insertion(+), 121 deletions(-) diff --git a/README.md b/README.md index 3bc853b..52667cb 100644 --- a/README.md +++ b/README.md @@ -1,121 +1 @@ -!!! UPDATE TO THE LATEST VERSION IF YOU WANT TO CLAIM WBTC - MAJOR BUG FIXED !!! - -This is a small script that enables you to transfer/claim various coins in Bitcoin forks -without downloading the full blockchains or messing with the official clients. - -Requires Python 2.7 - -The following coins are recognized, although may not be fully tested: - -* B2X - [Segwit 2X](https://b2x-segwit.io/) -* BBC - [Big Bitcoin](http://bigbitcoins.org/) -* BCA - [Bitcoin Atom](https://bitcoinatom.io/) -* BCBC - [Bitcoin@CBC](https://cleanblockchain.org/) -* BCD - [Bitcoin Diamond](http://www.btcd.io/) -* BCH - [Bitcoin Cash](https://www.bitcoincash.org/) -* BCI - [Bitcoin Interest](http://bitcoininterest.io/) -* BCK - [Bitcoin King](https://btcking.org/) - NOT A TRUE FORK, NOT CLAIMABLE AT THE MOMENT -* BCL - [BitcoinClean](https://bitcoinclean.org/) -* BCP - [Bitcoin Cash Plus](http://www.bitcoincashplus.org/) -* BCX - [Bitcoin X](https://bcx.org/) -* BICC - [BitClassic Coin](http://bicc.io/) -* BIFI - [Bitcoin File](https://www.bitcoinfile.org) -* BPA - [Bitcoin Pizza](http://p.top/en/index.html) -* BTCC - [Bitcoin Core](https://bitcoincore.cm/) -* BTCH - [Bitcoin Hush](http://btchush.org/) -* BTCP - [Bitcoin Private](https://btcprivate.org/) -* BTF - [Bitcoin Faith](http://bitcoinfaith.org/) -* BTG - [Bitcoin Gold](https://bitcoingold.org/) -* BTH - [Bitcoin Hot](https://www.bithot.org/) -* BTN - [Bitcoin New](http://btn.kim/) -* BTP - [Bitcoin Pay](http://www.btceasypay.com/) -* BTSQ - [Bitcoin Community](http://btsq.top/) -* BTT - [Bitcoin Top](https://bitcointop.org/) -* BTV - [Bitcoin Vote](https://bitvote.one/) -* BTW - [Bitcoin World](http://www.btw.one/) -* BTX - [Bitcore](https://bitcore.cc/) -* CDY - [Bitcoin Candy](https://cdy.one/) - Forked from Bitcoin Cash, not Bitcoin -* GOD - [Bitcoin God](https://www.bitcoingod.org/) -* LBTC - [Lightning Bitcoin](http://lbtc.io/) -* MBC - [MicroBitcoin](https://microbitcoin.org/) -* NBTC - [NewBitcoin](http://www.newbitcoin.org/index_en.html) -* SBTC - [Super Bitcoin](http://superbtc.org/) -* UBTC - [United Bitcoin](https://www.ub.com/) -* WBTC - [World Bitcoin](http://www.wbtcteam.org/) - -At the moment it supports standard P2PKH and Segwit P2SH-P2WPKH addresses. Segwit mode has been verified to work with these coins: BTG, BCX, B2X, UBTC, BTF, BTW, SBTC, BCD, BPA, BTN, BTH, BTV, BTT, BTP, BTSQ, WBTC, BCA, BICC, BCI, BTCP, BCL, BIFI, MBC - -It also has experimental support for bech32 P2WPKH, but this has only been tested on the BTG, BTN, BCD, BTH, BTV, BTT, BTP, BTSQ, WBTC, BCA, BICC, BCI, BCL, MBC networks so far. - -It should support old-style Pay-2-Public-Key that were in use in 2009-2010 (use command line switch --p2pk) but this is UNTESTED at the moment. - -USAGE OF THIS SCRIPT IS RISKY AND IF YOU MISTYPE ANYTHING YOU CAN LOSE ALL YOUR COINS - -It has two modes of operation - blockchain.info assisted mode and standalone mode. -* In blockchain.info mode it uses the blockchain.info API to query and validate information about the transaction you're spending from. -This only works for transferring/claiming coins that existed on the BTC main chain pre-fork. -* In standalone mode the user provides all the information including transaction source output index and the number of satoshis in the source output - there is no verification done, but this mode allows you to transfer coins that are entirely on-fork. - -blockchain.info mode: - - claimer.py - claimer.py BTG 4adc427d330497992710feaa32f85c389ef5106f74e7006878bd14b54500dfff 5K2YUVmWfxbmvsNxCsfvArXdGXm7d5DC9pn4yD75k2UaSYgkXTh 1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE 1aa5cmqmvQq8YQTEqcTmW7dfBNuFwgdCD - -Standalone mode: - - claimer.py --txindex --satoshis - claimer.py BTG 4adc427d330497992710feaa32f85c389ef5106f74e7006878bd14b54500dfff 5K2YUVmWfxbmvsNxCsfvArXdGXm7d5DC9pn4yD75k2UaSYgkXTh 1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE 1aa5cmqmvQq8YQTEqcTmW7dfBNuFwgdCD --txindex 0 --satoshis 3053 - -Default fee is set to 1000 satoshis, but can be changed with the `--fee` option. - -You can specify multiple destination addresses in the destination address field, the format is: - -
[,
][,
][,
]... - -where `
` is either a plain address or an address plus an amount in satoshis, separated by a colon. Examples: - - 13PuTPQjuZ5Vh1RCrTLqYK79scG2T45LGB - 13PuTPQjuZ5Vh1RCrTLqYK79scG2T45LGB:1000000 - 13PuTPQjuZ5Vh1RCrTLqYK79scG2T45LGB:1000000,1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE - 13PuTPQjuZ5Vh1RCrTLqYK79scG2T45LGB:1000000,1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE:1000000 - 13PuTPQjuZ5Vh1RCrTLqYK79scG2T45LGB,1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE:1000000 - -One of the destination addresses can be without a specified amount, which makes all the remaining coins (minus the fee) go to that address. - -Full example: - - claimer.py BTG db4f2348b92b4cd34675df66b49855e66869d7e98eb97141e85b558c28390fb3 5K2YUVmWfxbmvsNxCsfvArXdGXm7d5DC9pn4yD75k2UaSYgkXTh 1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE 13PuTPQjuZ5Vh1RCrTLqYK79scG2T45LGB:1000000,1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE:1000000 - -USAGE OF THIS SCRIPT IS RISKY AND IF YOU MISTYPE ANYTHING YOU CAN LOSE ALL YOUR COINS - -Advanced parameters for usage with scripting - -`--force` - Normally, the script creates the transaction and then requires the user to manually verify and enter a string to signal consent, before submitting the transaction to the network. When this flag is used, it skips this step and automatically submits it. Use only when you know what you are doing. - -`--noblock` - Without this flag, the script waits until the transaction is included in the next block. If you have a lot of addresses and use a script to process them, this can take a long time. When this flag is set, the script will finish after the transaction is included in the target network mempool. It's useful in combination with the `--force` parameter, because it allows mass processing of many addresses in an automated way. - ---- - -There is another python script for claiming FBTC (Fast Bitcoin). The FBTC network is based on the BitShares codebase, so it does not support Segwit. There are no TXIDs or change addresses, -and you can transfer arbitrary amounts from an address multiple times. - -Usage: - - fbtcclaimer.py - fbtcclaimer.py 5K2YUVmWfxbmvsNxCsfvArXdGXm7d5DC9pn4yD75k2UaSYgkXTh 1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE 1aa5cmqmvQq8YQTEqcTmW7dfBNuFwgdCD 3053 - -fbtcclaimer.py also requires aes.py to be in the same folder as the script. Thanks to https://github.com/ricmoo/pyaes for the implementation. - ---- - ---- - -There is another python script for just broadcast transactions. - -Usage: - - broadcaster.py - ---- - -Any donations can be sent to BTC address `13qtaKvBQYN9RQuegqF8Ci7U7JgTtT8SNz` +fork of ymgve/bitcoin_fork_claimer with added broadcaster [didnt get update, idk if still working]