zbxcat/xcat.py

139 lines
6.9 KiB
Python
Raw Normal View History

2017-05-16 11:28:56 -07:00
#!/usr/bin/env python3
# Based on spend-p2sh-txout.py from python-bitcoinlib.
# Copyright (C) 2017 The Zcash developers
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
from bitcoin.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
2017-05-17 15:57:30 -07:00
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
2017-05-16 11:28:56 -07:00
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
# SelectParams('testnet')
SelectParams('regtest')
2017-05-17 15:57:30 -07:00
bitcoind = bitcoin.rpc.Proxy()
FEE = 0.001*COIN
# ========================= BITCOIN ADDRESSES =========================
alice_address = input("Enter alice bitcoin address: (type 'enter' for demo)")
bob_address = input("Enter bob bitcoin address: (type 'enter' for demo)")
alicepubkey = CBitcoinAddress('mshp4msfzc73ebg4VzwS6nAXj9t6KqX1wd')
bobpubkey = CBitcoinAddress('myRh2T5Kg7QJfGLeRzriT5zs9aoek5Jbha')
# bitcoind.getnewaddress() returns CBitcoinAddress
# bobpubkey = bitcoind.getnewaddress()
# alicepubkey = bitcoind.getnewaddress()
print("alicepubkey", alicepubkey)
print("bobpubkey", bobpubkey)
# privkey of the bob, used to sign the redeemTx
bob_seckey = bitcoind.dumpprivkey(bobpubkey)
# privkey of alice, used to refund tx in case of timeout
alice_seckey = bitcoind.dumpprivkey(alicepubkey)
# ========================= HASHLOCK SECRET PREIMAGE =========================
secret = input("Alice: Enter secret to lock funds: (type 'enter' for demo)")
# preimage = secret.encode('UTF-8')
2017-05-16 11:28:56 -07:00
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
2017-05-17 15:57:30 -07:00
# ========================= LOCKTIME SCRIPT CREATION =========================
2017-05-16 11:28:56 -07:00
lockduration = 10
2017-05-17 15:57:30 -07:00
blocknum = bitcoind.getblockcount()
2017-05-16 11:28:56 -07:00
redeemblocknum = blocknum + lockduration
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
2017-05-17 15:57:30 -07:00
btc_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
alicepubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
bobpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("Redeem script:", b2x(btc_redeemScript))
2017-05-16 11:28:56 -07:00
2017-05-17 15:57:30 -07:00
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
txin_scriptPubKey = btc_redeemScript.to_p2sh_scriptPubKey()
2017-05-16 11:28:56 -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-05-17 15:57:30 -07:00
print('Bob -- Assuming Alice has created other tx on Zcash blockchain, send funds to this p2sh address:', p2sh)
2017-05-16 11:28:56 -07:00
2017-05-17 15:57:30 -07:00
## TODO: IMPORT ZCASH XCAT FUNCTIONS
2017-05-17 15:57:30 -07:00
# ========================= FUND BITCOIN P2SH =========================
response = input("Bob -- Type 'enter' to allow zbxcat to fund the Bitcoin p2sh on your behalf:")
send_amount = 1.0*COIN
fund_tx = bitcoind.sendtoaddress(txin_p2sh_address, send_amount)
print('Alice -- Bitcoin fund tx was sent, please wait for confirmation. Txid:', b2x(lx(b2x(fund_tx))))
# ========================= PART 2: BITCOIN P2SH FUNDED, REDEEM OR REFUND =========================
# Check that fund_tx is on the blockchain to the right address, then notify receiver
# Importing address so we can watch it
bitcoind.importaddress(p2sh)
# Get details of funding transaction
fund_txinfo = bitcoind.gettransaction(fund_tx)
fund_details = fund_txinfo['details'] # "fund_details" is an array, for now we can assume it only has one destination address
outputAddress = fund_details[0]['address']
fund_vout = fund_details[0]['vout']
if (outputAddress != p2sh):
2017-05-17 15:57:30 -07:00
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
2017-05-16 11:28:56 -07:00
quit()
2017-05-17 15:57:30 -07:00
# Check amount by inspecting imported address
output_amount = bitcoind.getreceivedbyaddress(outputAddress, 0)
2017-05-16 11:28:56 -07:00
if (output_amount < send_amount):
2017-05-17 15:57:30 -07:00
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
2017-05-16 11:28:56 -07:00
quit()
2017-05-17 15:57:30 -07:00
print("P2SH {0} successfully funded with {1}".format(p2sh, send_amount))
print('Alice -- the fund tx has been confirmed, now you can redeem your Bitcoin with the secret!')
2017-05-16 11:28:56 -07:00
2017-05-17 15:57:30 -07:00
# ========================= CHECKLOCKIME FOR BITCOIN TX1 =========================
# Mock the timeout period passing for tx1 (comment this out to proceed to redeemtx)
# bitcoind.generate(20)
2017-05-16 11:28:56 -07:00
2017-05-17 15:57:30 -07:00
# ========================= BITCOIN REFUND CONDITION =========================
# AFTER 24 HRS (by blocknum): If locktime for first tx has passed, tx1 is refunded to alice
if(bitcoind.getblockcount() >= redeemblocknum):
print("Bob -- Alice did not redeem within the timeout period, so refunding your bitcoin....... ")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
2017-05-16 11:28:56 -07:00
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
txin.nSequence = 0
2017-05-17 15:57:30 -07:00
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
2017-05-16 11:28:56 -07:00
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
2017-05-17 15:57:30 -07:00
tx.nLockTime = redeemblocknum
2017-05-16 11:28:56 -07:00
# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
2017-05-17 15:57:30 -07:00
sighash = SignatureHash(btc_redeemScript, tx, 0, SIGHASH_ALL)
sig = bob_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, bob_seckey.pub, OP_FALSE, btc_redeemScript])
print("Time lock has passed, Bob redeeming his own tx:")
print("Refund tx hex:", b2x(tx.serialize()))
2017-05-16 11:28:56 -07:00
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
2017-05-17 15:57:30 -07:00
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
2017-05-16 11:28:56 -07:00
quit()
2017-05-17 15:57:30 -07:00
# ========================= BITCOIN REDEEM CONDITION =========================
# BEFORE 24 HRS (by blocknum): Alice redeems bitcoin tx bob funded
print("Alice -- Redeeming tx.....")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
txout = CMutableTxOut(send_amount - FEE, alicepubkey.to_scriptPubKey())
2017-05-16 11:28:56 -07:00
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
2017-05-17 15:57:30 -07:00
tx.nLockTime = redeemblocknum
sighash = SignatureHash(btc_redeemScript, tx, 0, SIGHASH_ALL)
sig = alice_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, alice_seckey.pub, preimage, OP_TRUE, btc_redeemScript])
2017-05-16 11:28:56 -07:00
print("Redeem tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
2017-05-17 15:57:30 -07:00
txid = bitcoind.sendrawtransaction(tx)
2017-05-16 11:28:56 -07:00
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))