zbxcat/xcat/bitcoinRPC.py

225 lines
8.9 KiB
Python
Raw Normal View History

2017-05-22 18:00:34 -07:00
#!/usr/bin/env python3
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import bitcoin
import bitcoin.rpc
from bitcoin import SelectParams
2017-05-23 11:22:21 -07:00
from bitcoin.core import b2x, lx, b2lx, x, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160, CTransaction
2017-05-22 18:00:34 -07:00
from bitcoin.base58 import decode
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
2017-05-22 18:00:34 -07:00
2017-07-28 17:23:32 -07:00
from xcat.utils import *
from xcat.zcashRPC import parse_script
2017-07-28 13:57:44 -07:00
2017-08-25 11:59:37 -07:00
SelectParams('testnet')
# SelectParams('regtest')
2017-07-31 14:01:20 -07:00
# TODO: Accurately read user and pw info
2017-08-25 11:59:37 -07:00
bitcoind = bitcoin.rpc.Proxy(timeout=900)
2017-05-22 18:00:34 -07:00
FEE = 0.001*COIN
2017-07-12 19:45:11 -07:00
def validateaddress(addr):
return bitcoind.validateaddress(addr)
def find_secret(p2sh, fundtx_input):
txs = bitcoind.call('listtransactions', "*", 20, 0, True)
for tx in txs:
raw = bitcoind.gettransaction(lx(tx['txid']))['hex']
decoded = bitcoind.decoderawtransaction(raw)
print("TXINFO", decoded['vin'][0])
if('txid' in decoded['vin'][0]):
sendid = decoded['vin'][0]['txid']
if (sendid == fundtx_input ):
print("Found funding tx: ", sendid)
return parse_secret(lx(tx['txid']))
print("Redeem transaction with secret not found")
return
def parse_secret(txid):
2017-08-02 21:41:55 -07:00
raw = zcashd.gettransaction(txid, True)['hex']
decoded = zcashd.call('decoderawtransaction', raw)
scriptSig = decoded['vin'][0]['scriptSig']
asm = scriptSig['asm'].split(" ")
pubkey = asm[1]
secret = x2s(asm[2])
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
2017-07-31 19:13:46 -07:00
return secret
2017-05-22 18:00:34 -07:00
def get_keys(funder_address, redeemer_address):
fundpubkey = CBitcoinAddress(funder_address)
redeempubkey = CBitcoinAddress(redeemer_address)
# fundpubkey = bitcoind.getnewaddress()
# redeempubkey = bitcoind.getnewaddress()
return fundpubkey, redeempubkey
def privkey(address):
bitcoind.dumpprivkey(address)
2017-07-26 13:23:12 -07:00
def hashtimelockcontract(funder, redeemer, commitment, locktime):
2017-05-22 21:18:44 -07:00
funderAddr = CBitcoinAddress(funder)
redeemerAddr = CBitcoinAddress(redeemer)
2017-07-28 13:57:44 -07:00
if type(commitment) == str:
commitment = x(commitment)
2017-07-26 13:23:12 -07:00
# h = sha256(secret)
2017-05-22 18:00:34 -07:00
blocknum = bitcoind.getblockcount()
2017-08-02 21:41:55 -07:00
print("Current blocknum on Bitcoin: ", blocknum)
2017-05-22 18:00:34 -07:00
redeemblocknum = blocknum + locktime
2017-08-02 21:41:55 -07:00
print("Redeemblocknum on Bitcoin: ", redeemblocknum)
2017-07-28 13:57:44 -07:00
redeemScript = CScript([OP_IF, OP_SHA256, commitment, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
2017-05-22 21:18:44 -07:00
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
2017-08-02 21:41:55 -07:00
# print("Redeem script for p2sh contract on Bitcoin blockchain: {0}".format(b2x(redeemScript)))
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
2017-05-22 18:00:34 -07:00
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
2017-07-31 19:13:46 -07:00
# Import address at same time you create
bitcoind.importaddress(p2sh, "", False)
2017-07-28 13:57:44 -07:00
print("p2sh computed", p2sh)
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
2017-05-22 18:00:34 -07:00
def fund_htlc(p2sh, amount):
send_amount = float(amount) * COIN
2017-07-31 16:25:49 -07:00
# Import address at same time that you fund it
bitcoind.importaddress(p2sh, "", False)
2017-07-31 19:13:46 -07:00
fund_txid = bitcoind.sendtoaddress(p2sh, send_amount)
2017-05-22 18:00:34 -07:00
txid = b2x(lx(b2x(fund_txid)))
return txid
# Following two functions are about the same
2017-05-22 18:00:34 -07:00
def check_funds(p2sh):
2017-05-23 11:22:21 -07:00
bitcoind.importaddress(p2sh, "", False)
2017-05-22 18:00:34 -07:00
# Get amount in address
amount = bitcoind.getreceivedbyaddress(p2sh, 0)
amount = amount/COIN
return amount
def get_fund_status(p2sh):
bitcoind.importaddress(p2sh, "", False)
amount = bitcoind.getreceivedbyaddress(p2sh, 0)
amount = amount/COIN
2017-08-02 21:41:55 -07:00
print("Amount in bitcoin p2sh: ", amount, p2sh)
if amount > 0:
return 'funded'
else:
return 'empty'
2017-05-22 23:59:32 -07:00
## TODO: FIX search for p2sh in block
2017-05-22 18:00:34 -07:00
def search_p2sh(block, p2sh):
print("Fetching block...")
blockdata = bitcoind.getblock(lx(block))
print("done fetching block")
txs = blockdata.vtx
print("txs", txs)
for tx in txs:
txhex = b2x(tx.serialize())
# Using my fork of python-zcashlib to get result of decoderawtransaction
txhex = txhex + '00'
rawtx = zcashd.decoderawtransaction(txhex)
# print('rawtx', rawtx)
print(rawtx)
for vout in rawtx['vout']:
if 'addresses' in vout['scriptPubKey']:
for addr in vout['scriptPubKey']['addresses']:
print("Sent to address:", addr)
if addr == p2sh:
print("Address to p2sh found in transaction!", addr)
print("Returning from search_p2sh")
2017-05-22 23:59:32 -07:00
def get_tx_details(txid):
2017-05-23 11:22:21 -07:00
# must convert txid string to bytes x(txid)
2017-05-23 12:28:48 -07:00
fund_txinfo = bitcoind.gettransaction(lx(txid))
2017-05-22 23:59:32 -07:00
return fund_txinfo['details'][0]
def redeem_contract(contract, secret):
print("Parsing script for redeem_contract...")
2017-07-26 13:23:12 -07:00
scriptarray = parse_script(contract.redeemScript)
redeemblocknum = scriptarray[8]
2017-07-28 13:57:44 -07:00
redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
2017-07-26 13:23:12 -07:00
p2sh = contract.p2sh
#checking there are funds in the address
amount = check_funds(p2sh)
if(amount == 0):
print("address ", p2sh, " not funded")
quit()
fundtx = find_transaction_to_address(p2sh)
amount = fundtx['amount'] / COIN
2017-08-02 21:41:55 -07:00
# print("Found fund_tx: ", fundtx)
2017-07-26 13:23:12 -07:00
p2sh = P2SHBitcoinAddress(p2sh)
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
blockcount = bitcoind.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
if blockcount < int(redeemblocknum):
2017-07-26 13:23:12 -07:00
print('redeemPubKey', redeemPubKey)
zec_redeemScript = CScript(x(contract.redeemScript))
txin = CMutableTxIn(fundtx['outpoint'])
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
# TODO: protect privkey better, separate signing from rawtx creation
2017-07-26 13:23:12 -07:00
privkey = bitcoind.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
preimage = secret.encode('utf-8')
2017-07-26 13:23:12 -07:00
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
2017-08-02 21:41:55 -07:00
# print("txin.scriptSig", b2x(txin.scriptSig))
2017-07-26 13:23:12 -07:00
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
2017-08-02 21:41:55 -07:00
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
2017-07-26 13:23:12 -07:00
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
2017-08-02 21:41:55 -07:00
print("Script verified, sending raw transaction...")
2017-07-26 13:23:12 -07:00
txid = bitcoind.sendrawtransaction(tx)
fund_tx = str(fundtx['outpoint'])
redeem_tx = b2x(lx(b2x(txid)))
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
2017-07-26 13:23:12 -07:00
else:
print("nLocktime exceeded, refunding")
print('refundPubKey', refundPubKey)
txid = bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
fund_tx = str(fundtx['outpoint'])
refund_tx = b2x(lx(b2x(txid)))
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
2017-07-26 13:23:12 -07:00
else:
print("No contract for this p2sh found in database", p2sh)
def find_redeemblocknum(contract):
scriptarray = parse_script(contract.redeemScript)
redeemblocknum = scriptarray[8]
return int(redeemblocknum)
def find_redeemAddr(contract):
scriptarray = parse_script(contract.redeemScript)
redeemer = scriptarray[6]
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
return redeemAddr
def find_refundAddr(contract):
scriptarray = parse_script(contract.redeemScript)
funder = scriptarray[13]
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
return refundAddr
def find_transaction_to_address(p2sh):
bitcoind.importaddress(p2sh, "", False)
txs = bitcoind.listunspent()
for tx in txs:
if tx['address'] == CBitcoinAddress(p2sh):
print("Found tx to p2sh", p2sh)
return tx
2017-05-23 12:28:48 -07:00
def new_bitcoin_addr():
addr = bitcoind.getnewaddress()
2017-07-31 19:13:46 -07:00
return str(addr)
def generate(num):
blocks = bitcoind.generate(num)
return blocks