Skip to content

Instantly share code, notes, and snippets.

@brianddk
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianddk/db762a50c6fa5049ff63 to your computer and use it in GitHub Desktop.
Save brianddk/db762a50c6fa5049ff63 to your computer and use it in GitHub Desktop.
Uses Bitcoin-RPC to create CPFP to sweep an unconfirmed transaction output into another transaction with higher fees
# [rights] Copyright Dan B. (brianddk) 2015 https://github.com/brianddk
# [license] Licensed under Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
# [repo] https://gist.github.com/brianddk/db762a50c6fa5049ff63
# [version] 0.3
# [tips] 18MDTTiqPM8ZEo29Cig1wfGdkLNtvyorW5
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
import argparse
import os
import os.path
import simplejson
import getpass
from decimal import Decimal, ROUND_DOWN
def main():
satPcoin = 100000000
coinPsat = 0.00000001
minTxFee = 1000
(rpcuser, rpcpassword, rpcbind) = parse_conf(args.conf)
url='http://%s:%s@%s' % (rpcuser, rpcpassword, rpcbind)
btcrpc = AuthServiceProxy(url)
uctxo = get_uctxo(btcrpc)
fee = (Decimal(args.txfee) / Decimal(satPcoin))
vout = [{'txid': uctxo['txid'], 'vout': uctxo['vout']}]
amt = uctxo['amount'] - fee
amt = amt.quantize(Decimal(str(coinPsat)), rounding=ROUND_DOWN)
sendto = {uctxo['address']: amt}
print "\nFROM:"
prettyPrint(vout)
print "\nTO:"
prettyPrint(sendto)
print "\nFEE:"
prettyPrint({'FeeInBtc': fee})
txhex = btcrpc.createrawtransaction(vout, sendto)
passphrase = getpass.getpass("Enter Wallet Passphrase: ")
btcrpc.walletpassphrase(passphrase, 1)
sigtx = btcrpc.signrawtransaction(txhex)
btcrpc.walletlock()
dectx = btcrpc.decoderawtransaction(sigtx['hex'])
txlen = len(sigtx['hex'])
feePkb = 1000 * int(args.txfee) / txlen
print "\nTRANSACTION:"
prettyPrint(dectx)
print "\nHEX:\n%s" % sigtx['hex']
print "\nLEN: %d" % txlen
print "\nFEE / KB: %d" % feePkb
if feePkb < minTxFee:
print "Ohh No... The fee is too small [Err 2]"
print "INFO: http://bitcoinexchangerate.org/fees"
exit(2)
if args.send:
btcrpc.sendrawtransaction(sigtx['hex'])
def conf_default():
appdata=os.environ["APPDATA"]
#return os.path.join(appdata, "Bitcoin", "testnet3", "bitcoin.conf")
return os.path.join(appdata, "Bitcoin", "bitcoin.conf")
def parse_args():
parser=argparse.ArgumentParser()
parser.add_argument("-conf", help="Bitcoin Config file to reference",
action="store", type=str, default=conf_default())
parser.add_argument("-txfee", help="TXFEE in satoshi to spend",
action="store", type=int, default=111111)
parser.add_argument("-uctxo", help="UCTXO index from '-list' to spend",
action="store", type=int, default=0)
parser.add_argument("-debug", help="Debug run of the script",
action="store_true")
parser.add_argument("-list", help="List unconf TXOs to choose from",
action="store_true")
parser.add_argument("-send", help="Send the transaction",
action="store_true")
return parser.parse_args()
def parse_conf(conf_file):
conff=open(conf_file, "r")
if args.list: print "\nCONFIGURATION:"
for line in conff:
line=line.strip()
if line.startswith("#"): continue
if args.list and len(line): print ' ',line
if line.startswith("rpcuser="):
rpcuser=line[len("rpcuser="):]
if line.startswith("rpcpassword="):
rpcpassword=line[len("rpcpassword="):]
if line.startswith("rpcbind="):
rpcbind=line[len("rpcbind="):]
conff.close()
return (rpcuser, rpcpassword, rpcbind)
def prettyPrint(obj):
print simplejson.dumps(obj, use_decimal=True, sort_keys=True, indent=4 * ' ')
def get_uctxo(btcrpc):
uctxos = []
ustxos = btcrpc.listunspent(0)
for ustxo in ustxos:
if args.debug:
if ustxo['confirmations'] == 0: continue #test
else:
if ustxo['confirmations'] != 0: continue #final
uctxos += [ustxo]
addr = ustxo['address']
data = btcrpc.validateaddress(addr)
if not data.get('ismine'):
print "Ohh No... almost sent to wrong address [Err 1]"
exit(1)
if args.list:
i = 0
for uctxo in uctxos:
print "\nUCTXO[%d]" % i
i += 1
prettyPrint(uctxo)
exit()
return uctxos[args.uctxo]
if __name__ == "__main__":
args = parse_args()
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment