Merge pull request #2 from zcash/cli-interface

Cli interface
This commit is contained in:
Jay Graber 2017-05-23 14:14:58 -07:00 committed by GitHub
commit 6fec17cd81
16 changed files with 1033 additions and 4 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.pyc
xcatdb/

161
bXcat.py Normal file
View File

@ -0,0 +1,161 @@
#!/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
from bitcoin.core import b2x, lx, b2lx, x, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160, CTransaction
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
from utils import *
import zcash
import zcash.rpc
import pprint, json
# SelectParams('testnet')
SelectParams('regtest')
bitcoind = bitcoin.rpc.Proxy()
FEE = 0.001*COIN
zcashd = zcash.rpc.Proxy()
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)
def hashtimelockcontract(funder, redeemer, secret, locktime):
funderAddr = CBitcoinAddress(funder)
redeemerAddr = CBitcoinAddress(redeemer)
h = sha256(secret)
blocknum = bitcoind.getblockcount()
redeemblocknum = blocknum + locktime
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
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)
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'zec_redeemScript': b2x(zec_redeemScript), 'redeemer': redeemer, 'funder': funder}
def fund_htlc(p2sh, amount):
send_amount = amount*COIN
fund_txid = bitcoind.sendtoaddress(p2sh, send_amount)
txid = b2x(lx(b2x(fund_txid)))
return txid
def check_funds(p2sh):
bitcoind.importaddress(p2sh, "", False)
# Get amount in address
amount = bitcoind.getreceivedbyaddress(p2sh, 0)
amount = amount/COIN
return amount
## TODO: FIX search for p2sh in block
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")
def get_tx_details(txid):
# must convert txid string to bytes x(txid)
fund_txinfo = bitcoind.gettransaction(lx(txid))
return fund_txinfo['details'][0]
def redeem(p2sh, action):
# ensure p2sh is imported
bitcoind.importaddress(p2sh, '', False)
contracts = get_contract()
trade = get_trade()
for key in contracts:
if key == p2sh:
contract = contracts[key]
if contract:
print("Redeeming tx in p2sh", p2sh)
# TODO: Have to get tx info from saved contract p2sh
redeemblocknum = contract['redeemblocknum']
zec_redeemScript = contract['zec_redeemScript']
txid = trade[action]['fund_tx']
details = get_tx_details(txid)
print("Txid for fund tx", txid)
# must be little endian hex
txin = CMutableTxIn(COutPoint(lx(txid), details['vout']))
redeemPubKey = CBitcoinAddress(contract['redeemer'])
amount = trade[action]['amount'] * COIN
print("amount: {0}, fee: {1}".format(amount, FEE))
txout = CMutableTxOut(amount - FEE, redeemPubKey.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
# TODO: these things like redeemblocknum should really be properties of a tx class...
# Need: redeemblocknum, zec_redeemScript, secret (for creator...), txid, redeemer...
# Is stored as hex, must convert to bytes
zec_redeemScript = CScript(x(zec_redeemScript))
tx.nLockTime = redeemblocknum
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey?
privkey = bitcoind.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
# TODO: Figure out where to store secret preimage securely. Parse from scriptsig of redeemtx
secret = trade['sell']['secret']
preimage = secret.encode('utf-8')
print('preimage', preimage)
# print('zec_redeemScript', zec_redeemScript)
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
# print("Redeem tx hex:", b2x(tx.serialize()))
# Can only call to_p2sh_scriptPubKey on CScript obj
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
# print("txin.scriptSig", b2x(txin.scriptSig))
# print("txin_scriptPubKey", b2x(txin_scriptPubKey))
# print('tx', tx)
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Script verified, sending raw tx...")
print("Raw tx of prepared redeem tx: ", b2x(tx.serialize()))
txid = bitcoind.sendrawtransaction(tx)
txhex = b2x(lx(b2x(txid)))
print("Txid of submitted redeem tx: ", txhex)
return txhex
else:
print("No contract for this p2sh found in database", p2sh)
def new_bitcoin_addr():
addr = bitcoind.getnewaddress()
print('new btc addr', addr.to_scriptPubKey)
return addr.to_scriptPubKey()

138
bitcoin-xcat.py Normal file
View File

@ -0,0 +1,138 @@
#!/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
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')
SelectParams('regtest')
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')
preimage = b'preimage'
h = hashlib.sha256(preimage).digest()
# ========================= LOCKTIME SCRIPT CREATION =========================
lockduration = 10
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.
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('Bob -- Assuming Alice has created other tx on Zcash blockchain, send funds to this p2sh address:', p2sh)
## TODO: IMPORT ZCASH XCAT FUNCTIONS
# ========================= 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):
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 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('Alice -- the fund tx has been confirmed, now you can redeem your Bitcoin with the secret!')
# ========================= 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
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
# 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(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 = bitcoind.sendrawtransaction(tx)
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
quit()
# ========================= 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(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 = bitcoind.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

View File

@ -11,7 +11,7 @@ if sys.version_info.major < 3:
import bitcoin
import bitcoin.rpc
from bitcoin import SelectParams
from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
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.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
@ -34,12 +34,15 @@ senderpubkey = proxy.getnewaddress()
# privkey of the recipient, used to sign the redeemTx
seckey = proxy.dumpprivkey(recipientpubkey)
blocknum = 7
lockduration = 10
blocknum = proxy.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, blocknum, OP_DROP, 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.
@ -53,8 +56,10 @@ print('Pay to:', p2sh)
# AUTOMATE Send funds to p2sh
amount = 1.0*COIN
# sendtoaddress return the id of the created tx
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
print('fund tx sent. It\'s id is:', b2x(lx(b2x(fund_tx))))
print('Now redeeming.........')
# AUTOMATE getting vout of funding tx
@ -92,4 +97,4 @@ VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Now sending redeem transaction.......")
txid = proxy.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(txid))
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

15
checktx.py Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
import sys
import subprocess
subprocess.Popen("source /home/jay/Zcash/python3-xcat/venv/bin/activate", shell=True)
import bitcoin
import bitcoin.rpc
SelectParams('regtest')
bitcoind = bitcoin.rpc.Proxy()
txid = sys.argv[1]
print("Incoming txid:", txid)
tx = bitcoind.gettransaction(txid, 0)
print(tx)

1
contract.json Normal file
View File

@ -0,0 +1 @@
{"t2QrLFUqmp1v1xQSE3hmgwcYuinRb3BRWMm": {"funder": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "redeemer": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "redeemblocknum": 182, "zec_redeemScript": "63a820343e398e4e99a68e7de6ec57f00b6a14a8e6d0a7dd714efbab4dcbd385f4f3038876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702b600b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "p2sh": "t2QrLFUqmp1v1xQSE3hmgwcYuinRb3BRWMm"}, "2MuWU5BpLpqJvvzkCPq8gFHA4VFFGyvjaJf": {"funder": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "redeemer": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "redeemblocknum": 146, "zec_redeemScript": "63a820343e398e4e99a68e7de6ec57f00b6a14a8e6d0a7dd714efbab4dcbd385f4f3038876a9147788b4511a25fba1092e67b307a6dcdb6da125d967029200b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "p2sh": "2MuWU5BpLpqJvvzkCPq8gFHA4VFFGyvjaJf"}}

8
contract.py Normal file
View File

@ -0,0 +1,8 @@
class Contract(object):
def __init__(self, funder=None, redeemer=None, blockchain=None):
'''Create a new hash time-locked contract'''
self.funder = funder
self.redeemer = redeemer
self.blockchain = blockchain

106
htlc-redeemAfterTimeLock.py Normal file
View File

@ -0,0 +1,106 @@
#!/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, 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, OP_FALSE
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()
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
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(senderpubkey)
#proxy.importprivkey(seckey)
lockduration = 10
blocknum = proxy.getblockcount()
print("current block num:", blocknum)
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))
# 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)
# AUTOMATE Send funds to p2sh
amount = 1.0*COIN
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
print('Now redeeming.........')
print('Jumping forward till after timelock')
proxy.generate(lockduration)
print('block num is now:', proxy.getblockcount())
# AUTOMATE getting vout of funding tx
txinfo = proxy.gettransaction(fund_tx)
details = txinfo['details'][0]
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))
# 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(amount - default_fee, senderpubkey.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(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, OP_FALSE, 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)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))

0
user.py Normal file
View File

33
utils.py Normal file
View File

@ -0,0 +1,33 @@
import hashlib
import json
def sha256(secret):
preimage = secret.encode('utf8')
h = hashlib.sha256(preimage).digest()
return h
# TODO: Port these over to leveldb or some other database
def save_trade(trade):
with open('xcat.json', 'w') as outfile:
json.dump(trade, outfile)
def get_trade():
with open('xcat.json') as data_file:
try:
xcatdb = json.load(data_file)
return xcatdb
except:
return None
def erase_trade():
with open('xcat.json', 'w') as outfile:
outfile.write('')
def get_contract():
with open('contract.json') as data_file:
contractdb = json.load(data_file)
return contractdb
def save_contract(contracts):
with open('contract.json', 'w') as outfile:
json.dump(contracts, outfile)

2
watchdata Normal file
View File

@ -0,0 +1,2 @@
7af1b1d333431217958032adc11b84278939d400a7368d33bab4e8455b4203f0
7997c2eafeda0815b840bfbd60e5ad70d8879aac252bd594c732a04d6a7b85e9

4
watchtx.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
# /home/jay/Zcash/python3-xcat/protocol/checktx.py $@
echo "$@" >> "/home/jay/Zcash/python3-xcat/protocol/watchdata"

1
xcat.json Normal file
View File

@ -0,0 +1 @@
{"id": 1, "buy": {"initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "fund_tx": "9df973f468ce0b4398a9db2e8710b0ec268a3b3118f996f67f808af97e53a161", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "p2sh": "t2QrLFUqmp1v1xQSE3hmgwcYuinRb3BRWMm", "amount": 1.2, "currency": "zcash"}, "sell": {"status": "funded", "fund_tx": "d1f679a3ee51fd563224e8114bed1d79fa6dc54d48163ec12ba17ba1d9e76baa", "p2sh": "2MuWU5BpLpqJvvzkCPq8gFHA4VFFGyvjaJf", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "amount": 3.5, "currency": "bitcoin", "secret": "rabbits"}}

278
xcat.py Normal file
View File

@ -0,0 +1,278 @@
import zXcat
import bXcat
from utils import *
from waiting import *
from time import sleep
import json
import os, sys
from pprint import pprint
def check_p2sh(currency, address):
if currency == 'bitcoin':
print("Checking funds in Bitcoin p2sh")
return bXcat.check_funds(address)
else:
print("Checking funds in Zcash p2sh")
return zXcat.check_funds(address)
def set_price():
trade = {}
#TODO: make currencies interchangeable. Save to a tuple?
sell = input("Which currency would you like to trade out of? (bitcoin)")
sell = 'bitcoin'
buy = 'zcash'
sell_amt = input("How much {0} do you want to sell?".format(sell))
sell_amt = 3.5
print(sell_amt)
buy_amt = input("How much {0} do you want to receive in exchange?".format(buy))
buy_amt = 1.2
print(buy_amt)
sell = {'currency': sell, 'amount': sell_amt}
buy = {'currency': buy, 'amount': buy_amt}
trade['sell'] = sell
trade['buy'] = buy
save_trade(trade)
def create_htlc(currency, funder, redeemer, secret, locktime):
if currency == 'bitcoin':
sell_p2sh = bXcat.hashtimelockcontract(funder, redeemer, secret, locktime)
else:
sell_p2sh = zXcat.hashtimelockcontract(funder, redeemer, secret, locktime)
return sell_p2sh
def fund_htlc(currency, p2sh, amount):
if currency == 'bitcoin':
txid = bXcat.fund_htlc(p2sh, amount)
else:
txid = zXcat.fund_htlc(p2sh, amount)
return txid
def initiate_trade():
trade = get_trade()
currency = trade['sell']['currency']
secret = input("Initiating trade: Enter a password to place the {0} you want to sell in escrow: ".format(currency))
# TODO: hash and store secret only locally.
# secret = 'test'
print('Remember your password:', secret)
locktime = 20 # Must be more than first tx
# Returns contract obj
contracts = {}
contract = create_htlc(currency, trade['sell']['initiator'], trade['sell']['fulfiller'], secret, locktime)
sell_p2sh = contract['p2sh']
contracts[contract['p2sh']] = contract
save_contract(contracts)
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(trade['sell']['amount'], currency, contract['p2sh']))
response = input("Type 'enter' to allow this program to send funds on your behalf.")
print("Sent")
sell_amt = trade['sell']['amount']
txid = fund_htlc(currency, sell_p2sh, sell_amt)
trade['sell']['p2sh'] = sell_p2sh
trade['sell']['fund_tx'] = txid
trade['sell']['status'] = 'funded'
# TODO: Save secret locally for seller
trade['sell']['secret'] = secret
save_trade(trade)
buy_currency = trade['buy']['currency']
buy_initiator = trade['buy']['initiator']
buy_fulfiller = trade['buy']['fulfiller']
print("Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy_currency))
buy_contract = create_htlc(buy_currency, buy_fulfiller, buy_initiator, secret, locktime)
buy_p2sh = buy_contract['p2sh']
contracts[buy_contract['p2sh']] = buy_contract
save_contract(contracts)
print("Now contact the buyer and tell them to send funds to this p2sh: ", buy_p2sh)
trade['buy']['p2sh'] = buy_p2sh
save_trade(trade)
def get_addresses():
trade = get_trade()
sell = trade['sell']['currency']
buy = trade['buy']['currency']
init_offer_addr = input("Enter your {0} address: ".format(sell))
# init_offer_addr = bXcat.new_bitcoin_addr()
init_offer_addr = 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp'
print(init_offer_addr)
init_bid_addr = input("Enter your {0} address: ".format(buy))
# init_bid_addr = zXcat.new_zcash_addr()
init_bid_addr = 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'
print(init_bid_addr)
trade['sell']['initiator'] = init_offer_addr
trade['buy']['initiator'] = init_bid_addr
fulfill_offer_addr = input("Enter the {0} address of the party you want to trade with: ".format(sell))
# fulfill_offer_addr = bXcat.new_bitcoin_addr()
fulfill_offer_addr = 'mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z'
print(fulfill_offer_addr)
fulfill_bid_addr = input("Enter the {0} address of the party you want to trade with: ".format(buy))
# fulfill_bid_addr = zXcat.new_zcash_addr()
fulfill_bid_addr = 'tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY'
print(fulfill_bid_addr)
trade['sell']['fulfiller'] = fulfill_offer_addr
trade['buy']['fulfiller'] = fulfill_bid_addr
# zec_funder, zec_redeemer = zXcat.get_keys(zec_fund_addr, zec_redeem_addr)
trade['id'] = 1
save_trade(trade)
def buyer_fulfill():
trade = get_trade()
buy_p2sh = trade['buy']['p2sh']
sell_p2sh = trade['sell']['p2sh']
buy_amount = check_p2sh(trade['buy']['currency'], buy_p2sh)
sell_amount = check_p2sh(trade['sell']['currency'], sell_p2sh)
amount = trade['buy']['amount']
currency = trade['buy']['currency']
if buy_amount == 0:
input("The seller's p2sh is funded with {0} {1}, type 'enter' if this is the amount you want to buy in {1}.".format(trade['sell']['amount'], trade['sell']['currency']))
input("You have not send funds to the contract to buy {1} (requested amount: {0}), type 'enter' to allow this program to send the agreed upon funds on your behalf.".format(amount, currency))
p2sh = trade['buy']['p2sh']
txid = fund_htlc(currency, p2sh, amount)
trade['buy']['fund_tx'] = txid
save_trade(trade)
else:
print("It looks like you've already funded the contract to buy {1}, the amount in escrow in the p2sh is {0}.".format(amount, currency))
print("Please wait for the seller to remove your funds from escrow to complete the trade.")
def check_blocks(p2sh):
# blocks = []
with open('watchdata', 'r') as infile:
for line in infile:
res = bXcat.search_p2sh(line.strip('\n'), p2sh)
# blocks.append(line.strip('\n'))
# print(blocks)
# for block in blocks:
# res = bXcat.search_p2sh(block, p2sh)
def redeem_p2sh(currency, p2sh, action):
# action is buy or sell
if currency == 'bitcoin':
res = bXcat.redeem(p2sh, action)
else:
res = zXcat.redeem(p2sh, action)
return res
def seller_redeem():
# add locktime as variable?
trade = get_trade()
if 'status' in trade['buy'] and trade['buy']['status'] == 'redeemed':
print("You already redeemed the funds and acquired {0} {1}".format(trade['buy']['amount'], trade['buy']['currency']))
exit()
else:
# Seller redeems buyer's funded tx (contract in p2sh)
p2sh = trade['buy']['p2sh']
currency = trade['buy']['currency']
redeem_tx = redeem_p2sh(currency, p2sh, 'buy')
trade['buy']['redeem_tx'] = redeem_tx
trade['buy']['status'] = 'redeemed'
save_trade(trade)
def buyer_redeem():
trade = get_trade()
if 'status' in trade['sell'] and trade['sell']['status'] == 'redeemed':
print("You already redeemed the funds and acquired {0} {1}".format(trade['sell']['amount'], trade['sell']['currency']))
exit()
else:
# Buyer redeems seller's funded tx
p2sh = trade['sell']['p2sh']
currency = trade['sell']['currency']
redeem_tx = redeem_p2sh(currency, p2sh, 'sell')
trade['sell']['redeem_tx'] = redeem_tx
trade['sell']['status'] = 'redeemed'
save_trade(trade)
def print_trade(role):
print("Trade status:")
trade = get_trade()
if role == 'seller':
pprint(trade)
else:
del trade['sell']['secret']
pprint(trade)
if __name__ == '__main__':
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
# TODO: Get trade indicated by id number
# TODO: pass trade into functions?
# TODO: workflow framed as currency you're trading out of being sell. appropriate?
# Have initiator propose amounts to trade
trade = get_trade()
try:
if sys.argv[1] == 'new':
erase_trade()
role = 'seller'
trade = get_trade()
else:
role = sys.argv[1]
print("Your role in demo:", role)
except:
if trade == None:
print("No active trades available.")
res = input("Would you like to initiate a trade? (y/n) ")
if res == 'y':
role = 'seller'
else:
exit()
else:
print("Trade exists, run script as buyer or seller to complete trade.")
exit()
if trade is not None:
if trade['sell']['status'] == 'redeemed' and trade['buy']['status'] == 'redeemed':
print("This trade is already complete! Trade details:")
pprint(trade)
exit()
if role == "seller":
if trade == None or 'status' not in trade['sell']:
set_price()
get_addresses()
initiate_trade()
print_trade('seller')
elif 'status' in trade['sell']:
if 'fund_tx' in trade['buy']:
# Means buyer has already funded the currency the transaction initiator wants to exchange into
print("Buyer funded the contract where you offered to buy {0}, redeeming funds from {1}...".format(trade['buy']['currency'], trade['buy']['p2sh']))
seller_redeem()
print("You have redeemed {0} {1}!".format(trade['buy']['amount'], trade['buy']['currency']))
print_trade('seller')
else:
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(trade['buy']['currency']))
print_trade('seller')
else:
# Need better way of preventing buyer from having secret
if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
print("One active trade available, fulfilling buyer contract...")
trade = get_trade()
buyer_fulfill()
# How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
# For regtest, can mock in a function
# p2sh = trade['buy']['p2sh']
# check_blocks(p2sh)
print_trade('buyer')
elif trade['buy']['status'] == 'redeemed':
# Seller has redeemed buyer's tx, buyer can now redeem.
print("The seller has redeemed the contract where you paid them in {0}, now redeeming your funds from {1}".format(trade['buy']['currency'], trade['sell']['p2sh']))
buyer_redeem()
print("XCAT trade complete!")
print_trade('buyer')
# Note: there is some little endian weirdness in the bXcat and zXcat files, need to handle the endianness of txids better & more consistently

137
zXcat.py Normal file
View File

@ -0,0 +1,137 @@
#!/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, x, 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
from utils import *
# SelectParams('testnet')
SelectParams('regtest')
zcashd = zcash.rpc.Proxy()
FEE = 0.001*COIN
def get_keys(funder_address, redeemer_address):
fundpubkey = CBitcoinAddress(funder_address)
redeempubkey = CBitcoinAddress(redeemer_address)
# fundpubkey = zcashd.getnewaddress()
# redeempubkey = zcashd.getnewaddress()
return fundpubkey, redeempubkey
def privkey(address):
zcashd.dumpprivkey(address)
def hashtimelockcontract(funder, redeemer, secret, locktime):
funderAddr = CBitcoinAddress(funder)
redeemerAddr = CBitcoinAddress(redeemer)
h = sha256(secret)
blocknum = zcashd.getblockcount()
redeemblocknum = blocknum + locktime
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("Redeem script for p2sh contract on Zcash blockchain:", b2x(zec_redeemScript))
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)
# Returning all this to be saved locally in p2sh.json
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'zec_redeemScript': b2x(zec_redeemScript), 'redeemer': redeemer, 'funder': funder}
def fund_htlc(p2sh, amount):
send_amount = amount*COIN
fund_txid = zcashd.sendtoaddress(p2sh, send_amount)
txid = b2x(lx(b2x(fund_txid)))
return txid
def check_funds(p2sh):
zcashd.importaddress(p2sh, "", False)
print("Imported address", p2sh)
# Get amount in address
amount = zcashd.getreceivedbyaddress(p2sh, 0)
print("Amount in address", amount)
amount = amount/COIN
return amount
def get_tx_details(txid):
fund_txinfo = zcashd.gettransaction(txid)
return fund_txinfo['details'][0]
def redeem(p2sh, action):
contracts = get_contract()
trade = get_trade()
for key in contracts:
if key == p2sh:
contract = contracts[key]
if contract:
print("Redeeming tx in p2sh", p2sh)
# TODO: Have to get tx info from saved contract p2sh
redeemblocknum = contract['redeemblocknum']
zec_redeemScript = contract['zec_redeemScript']
txid = trade[action]['fund_tx']
details = get_tx_details(txid)
print("Txid for fund tx", txid)
# must be little endian hex
txin = CMutableTxIn(COutPoint(lx(txid), details['vout']))
redeemPubKey = CBitcoinAddress(contract['redeemer'])
amount = trade[action]['amount'] * COIN
print("amount: {0}, fee: {1}".format(amount, FEE))
txout = CMutableTxOut(amount - FEE, redeemPubKey.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
# TODO: these things like redeemblocknum should really be properties of a tx class...
# Need: redeemblocknum, zec_redeemScript, secret (for creator...), txid, redeemer...
# Is stored as hex, must convert to bytes
zec_redeemScript = CScript(x(zec_redeemScript))
tx.nLockTime = redeemblocknum
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey?
privkey = zcashd.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
# TODO: Figure out where to store secret preimage securely. Parse from scriptsig of redeemtx
secret = trade['sell']['secret']
preimage = secret.encode('utf-8')
print('preimage', preimage)
# print('zec_redeemScript', zec_redeemScript)
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
# print("Redeem tx hex:", b2x(tx.serialize()))
# Can only call to_p2sh_scriptPubKey on CScript obj
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
# print("txin.scriptSig", b2x(txin.scriptSig))
# print("txin_scriptPubKey", b2x(txin_scriptPubKey))
# print('tx', tx)
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Script verified, sending raw tx...")
print("Raw tx of prepared redeem tx: ", b2x(tx.serialize()))
txid = zcashd.sendrawtransaction(tx)
txhex = b2x(lx(b2x(txid)))
print("Txid of submitted redeem tx: ", txhex)
return txhex
else:
print("No contract for this p2sh found in database", p2sh)
def new_zcash_addr():
addr = zcashd.getnewaddress()
print('new ZEC addr', addr.to_p2sh_scriptPubKey)
return addr.to_scriptPubKey()

139
zcashcli.py Normal file
View File

@ -0,0 +1,139 @@
#!/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)
def get_keys(funder_address, redeemer_address):
# fundpubkey = CBitcoinAddress(funder_address)
# redeempubkey = CBitcoinAddress(redeemer_address)
fundpubkey = zcashd.getnewaddress()
redeempubkey = zcashd.getnewaddress()
return fundpubkey, redeempubkey
# ======= 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()))
print("txin.scriptSig", b2x(txin.scriptSig))
print("txin_scriptPubKey", b2x(txin_scriptPubKey))
print('tx', tx)
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
txid = zcashd.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))