Refactor xcat and zcash-xcat.py

This commit is contained in:
Jay Graber 2017-05-17 15:57:30 -07:00
parent 3617edd788
commit afc3451413
2 changed files with 206 additions and 155 deletions

234
xcat.py
View File

@ -12,203 +12,127 @@ import bitcoin
import bitcoin.rpc
from bitcoin import SelectParams
from bitcoin.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
from bitcoin.core.script import OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
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
import hashlib
# SelectParams('testnet')
# To get transactions not in your wallet, must set -txindex=1
SelectParams('regtest')
proxy = bitcoin.rpc.Proxy()
bitcoind = bitcoin.rpc.Proxy()
FEE = 0.001*COIN
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
# ========================= 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')
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# proxy.getnewaddress() returns CBitcoinAddress
recipientpubkey = proxy.getnewaddress()
senderpubkey = proxy.getnewaddress()
# privkey of the recipient, used to sign the redeemTx
seckey = proxy.dumpprivkey(recipientpubkey)
# ========================= LOCKTIME SCRIPT CREATION =========================
lockduration = 10
blocknum = proxy.getblockcount()
blocknum = bitcoind.getblockcount()
redeemblocknum = blocknum + lockduration
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
recipientpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("redeem script:", b2x(txin_redeemScript))
# Create P2SH scriptPubKey from redeemScript.
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
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))
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
txin_scriptPubKey = btc_redeemScript.to_p2sh_scriptPubKey()
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Pay to:', p2sh)
print('Bob -- Assuming Alice has created other tx on Zcash blockchain, send funds to this p2sh address:', p2sh)
## TODO: IMPORT ZCASH XCAT FUNCTIONS
# AUTOMATE Send funds to p2sh
# ========================= FUND BITCOIN P2SH =========================
response = input("Bob -- Type 'enter' to allow zbxcat to fund the Bitcoin p2sh on your behalf:")
send_amount = 1.0*COIN
# sendtoaddress return the id of the created tx
fund_tx = proxy.sendtoaddress(txin_p2sh_address, send_amount)
print('fund tx sent. Its id is:', b2x(lx(b2x(fund_tx))))
# Import p2sh address and watch
# proxy.importaddress(p2sh)
# # Returns list of recently observed transaction, includes p2sh if imported and conf sets txindex=1
# txs = proxy.listtransactions(p2sh, "*", 10, 10, True)
# print('txs from listtransaction', txs)
# Now receiver receives txid and checks that it is on the blockchain to the right address
txinfo = proxy.gettransaction(fund_tx)
details = txinfo['details']
print('details', details) # "details" is an array, for now we can assume it only has one destination address
outputAddress = details[0]['address']
print('outputAddress', outputAddress)
# Let's check amount by importing address and inspecting
proxy.importaddress(outputAddress)
# Get amount in address
output_amount = proxy.getreceivedbyaddress(outputAddress, 0)
print('output amount', output_amount)
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):
print('fund tx to wrong address!')
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
quit()
# Check amount by inspecting imported address
output_amount = bitcoind.getreceivedbyaddress(outputAddress, 0)
if (output_amount < send_amount):
print('fund tx to small!')
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
quit()
print("P2SH {0} successfully funded with {1}".format(p2sh, send_amount))
print('sender fund tx has been confirmed, now receiver making their fund tx......')
print('Alice -- the fund tx has been confirmed, now you can redeem your Bitcoin with the secret!')
rec_fund_tx = proxy.sendtoaddress(txin_p2sh_address, send_amount)
print('rec fund tx sent. Its id is:', b2x(lx(b2x(fund_tx))))
# Now sender checks if the lock time is passed, if so she redeems her own tx
if(proxy.getblockcount()>=redeemblocknum):
# ========================= CHECKLOCKIME FOR BITCOIN TX1 =========================
# Mock the timeout period passing for tx1 (comment this out to proceed to redeemtx)
# bitcoind.generate(20)
# ========================= 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))
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
txin.nSequence = 0
# Create the txout. Pays out to recipient, so uses recipient's pubkey
# Withdraw full amount minus fee
default_fee = 0.001*COIN
txout = CMutableTxOut(send_amount - default_fee, senderpubkey.to_scriptPubKey())
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
# 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
tx.nLockTime=redeemblocknum
tx.nLockTime = redeemblocknum
# 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
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, seckey.pub, OP_FALSE, txin_redeemScript])
print("Time lock has passed, sender redeeming their own tx:")
print("Redeem tx hex:", b2x(tx.serialize()))
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()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = proxy.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
quit()
#Otherwise, check that receiver fund tx is on blockchain to correct address with sufficient amount
send_txinfo = proxy.gettransaction(rec_fund_tx)
details = send_txinfo['details']
print('details', details) # "details" is an array, for now we can assume it only has one destination address
outputAddress = details[0]['address']
print('outputAddress', outputAddress)
# Let's check amount by importing address and inspecting
proxy.importaddress(outputAddress)
# Get amount in address
output_amount = proxy.getreceivedbyaddress(outputAddress, 0)
print('output amount', output_amount)
print('output amount', output_amount)
if (outputAddress != p2sh):
print('fund tx to wrong address!')
quit()
if (output_amount < send_amount):
print('fund tx to small!')
quit()
print('receiver fund tx confirmed, redeeming it with the hash preimage:')
# Create the txout. Pays out to recipient, so uses recipient's pubkey
# Withdraw full amount minus fee
default_fee = 0.001*COIN
txout = CMutableTxOut(send_amount - default_fee, senderpubkey.to_scriptPubKey())
# ========================= 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())
# 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
tx.nLockTime=redeemblocknum
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, seckey.pub, OP_FALSE, txin_redeemScript])
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])
print("Redeem tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = proxy.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
print('Now redeeming.........')
# AUTOMATE getting vout of funding tx
txinfo = proxy.gettransaction(fund_tx)
details = txinfo['details'][0] # what is the zero here
vout = details['vout']
# Create the txin structure. scriptSig defaults to being empty.
# The input is the p2sh funding transaction txid, vout is its index
txin = CMutableTxIn(COutPoint(fund_tx, vout))
# Create the txout. Pays out to recipient, so uses recipient's pubkey
# Withdraw full amount minus fee
default_fee = 0.001*COIN
txout = CMutableTxOut(send_amount - default_fee, recipientpubkey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
# 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
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([ sig, seckey.pub, preimage, OP_TRUE, txin_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
# Verify the signature worked.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Now sending redeem transaction.......")
txid = proxy.sendrawtransaction(tx)
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

127
zcash-xcat.py Normal file
View File

@ -0,0 +1,127 @@
#!/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 zcash
import zcash.rpc
from zcash import SelectParams
from zcash.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from zcash.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 zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from zcash.wallet import CBitcoinAddress, CBitcoinSecret
import hashlib
# SelectParams('testnet')
SelectParams('regtest')
zcashd = zcash.rpc.Proxy()
FEE = 0.001*COIN
# ========================= ZCASH ADDRESSES =========================
alice_address = input("Enter alice zcash address: (type 'enter' for demo)")
bob_address = input("Enter bob zcash address: (type 'enter' for demo)")
# These mock addresses come from regtest on the server
alicepubkey = CBitcoinAddress('tmFUm31B9wzHWJ9jGe9L9Qb549zfC7zFsEK')
bobpubkey = CBitcoinAddress('tmFm8R6b22485uDYm6dryC4f8R6oXUTUe5i')
# zcashd.getnewaddress() returns CBitcoinAddress
# bobpubkey = zcashd.getnewaddress()
# alicepubkey = zcashd.getnewaddress()
print("alicepubkey", alicepubkey)
print("bobpubkey", bobpubkey)
# privkey of the bob, used to sign the redeemTx
bob_seckey = zcashd.dumpprivkey(bobpubkey)
# privkey of alice, used to refund tx in case of timeout
alice_seckey = zcashd.dumpprivkey(alicepubkey)
print("bob_seckey", bob_seckey)
# ======= secret from Alice, other file ====
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# ========================= LOCKTIME SCRIPT CREATION =========================
lockduration = 20 # Must be more than first tx
blocknum = zcashd.getblockcount()
redeemblocknum = blocknum + lockduration
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
bobpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
alicepubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("TX2 Redeem script on Zcash blockchain:", b2x(zec_redeemScript))
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
print('Alice -- send funds to this p2sh address to initiate atomic swap:', p2sh)
response = input("Alice -- Type 'enter' to allow zbxcat to fund the Zcash p2sh on your behalf:")
send_amount = 10.0*COIN
fund_tx = zcashd.sendtoaddress(txin_p2sh_address, send_amount)
print('Bob -- Alice send fund tx to the Zcash p2sh. Please wait for confirmation. Txid:', b2x(lx(b2x(fund_tx))))
# ========================= CONFIRM ZCASH FUNDING TX TO P2SH =========================
zcashd.importaddress(p2sh)
fund_txinfo = zcashd.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):
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
quit()
# Get amount in address
output_amount = zcashd.getreceivedbyaddress(outputAddress, 0)
if (output_amount < send_amount):
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
quit()
print('Bob -- Alice Zcash funding tx confirmed, now send funds to the Bitcoin p2sh: (other file)')
# ========================= PART 2: ZCASH P2SH FUNDED, REDEEM OR REFUND =========================
# ================= AFTER 48 HRS: ALICE REFUNDS AFTER BOB TIMES OUT =========================
# Mock passage of time -- comment out to test normal redeem condition
# zcashd.generate(25)
if(zcashd.getblockcount() >= redeemblocknum):
print("Alice -- Bob did not redeem the Zcash you put in escrow within the timeout period, so refunding you..... ")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
txin.nSequence = 0
txout = CMutableTxOut(send_amount - FEE, alicepubkey.to_scriptPubKey())
# 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
tx.nLockTime = redeemblocknum
# 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
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
sig = alice_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, alice_seckey.pub, OP_FALSE, zec_redeemScript])
print("Time lock has passed, Alice redeeming her own tx:")
print("Refund tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = zcashd.sendrawtransaction(tx)
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
quit()
# ================= BEFORE 48 HRS: BOB REDEEMS WITH ALICE'S REVEALED SECRET =========================
print("Bob -- Redeeming tx.....")
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
# 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
tx.nLockTime = redeemblocknum
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
sig = bob_seckey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, bob_seckey.pub, preimage, OP_TRUE, zec_redeemScript])
print("Redeem tx hex:", b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = bitcoind.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))