Merge pull request #11 from zcash/find-secret

Find secret
This commit is contained in:
arcalinea 2017-08-02 12:42:01 -07:00 committed by GitHub
commit 06b4711ad2
9 changed files with 119 additions and 293 deletions

121
README.md
View File

@ -1,4 +1,54 @@
# Workflow for a new trade
# ZBXCAT
A work-in-progress for Zcash Bitcoin Cross-Chain Atomic Transactions
Bitcoin scripts use python-bitcoinlib, and Zcash scripts use python-zcashlib (a Zcash fork of python-bitcoinlib).
## Setup
To successfully run this, you'll need python3, the dependencies installed, and a bitcoin and zcash daemon synced on whichever chain you want to trade on.
It's recommended that you install python3 in a virtualenv. Run this command from the top level of the directory:
```
virtualenv -p python3 venv
source venv/bin/activate
```
To install dependencies, run:
```
pip install -r requirements.txt
```
To install python-zcashlib for testing and editing, clone the repository to your local filesystem.
```
git clone https://github.com/arcalinea/python-zcashlib.git
```
# Testing
Install modules locally in editable mode through pip, so that you can make changes to the code and they will be applied immediately.
To use pip to install a package in editable mode, use the `-e` flag to pass in the path on your local filesystem:
`pip install -e <path-to-package-repo>`
## Run Zcash and Bitcoin daemons locally
To test, run a Zcash daemon and bitcoin daemon in regtest mode.
To run a bitcoin daemon in regtest mode, with the ability to inspect transactions outside your wallet (useful for testing purposes), use the command
```
bitcoind -regtest -txindex=1 -daemon
```
Be sure to run a Zcash daemon in regtest mode as well.
```
zcashd -regtest -txindex=1 --daemon
```
## Workflow for a new trade
Install our code as a python package in editable mode. Installing relative to the directory containing `setup.py` should work.
@ -42,75 +92,10 @@ Redeems buyer p2sh.
`xcat checktrade testtrade`
**At this stage, we need to manually export the trade again, because we haven't added the `walletnotify` functionality which will let the buyer determine what the seller's redeem tx was.**
So seller exports trade again and sends to buyer, so they will have the seller's redeem_tx. (this is a temporary measure)
`xcat exportrade testtrade`
### Buyer:
Imports trade hexstring received from seller.
`xcat importtrade <hexstring> testtrade`
Redeems seller p2sh.
Redeems seller p2sh. The secret they need to redeem will be automatically parsed from the seller's redeemtx on the blockchain.
`xcat checktrade testtrade`
Tx is done! Buyer or seller can check the trade again, but the status will indicate that it is complete.
# ZBXCAT
A work-in-progress for Zcash Bitcoin Cross-Chain Atomic Transactions
Contains basic scripts we're still testing in regtest mode on both networks. This may all be refactored as we go.
Bitcoin scripts use the rpc proxy code in python-bitcoinlib, and Zcash script will use python-zcashlib (a Zcash fork of python-bitcoinlib).
## Setup
To successfully run this, you'll need python3, the dependencies installed, and a bitcoin daemon running in regtest mode.
To install python3 in a virtualenv, run this command from the top level of the directory:
```
virtualenv -p python3 venv
source venv/bin/activate
```
To install dependencies, run:
```
pip install -r requirements.txt
```
To install python-zcashlib for testing and editing, clone the repository to your local filesystem. It is currently on a branch of python-bitcoinlib maintained by @arcalinea.
```
git clone https://github.com/arcalinea/python-bitcoinlib.git
cd python-bitcoinlib
git checkout zcashlib
```
Then, install the module locally in editable mode through pip, so that you can make changes to the code of python-zcashlib and they will be applied immediately. It is necessary to install python-zcashlib this way for now because the fork of the library likely contains many bugs, which need to be fixed before `zec-p2sh-htlc.py` will work properly.
To install python-zcashlib from your local filesystem path in editable mode:
`pip install --editable (-e) <path-to-zcashlib-fork-of-python-bitcoinlib>`
## Run Zcash and Bitcoin daemons locally
To test, run a Zcash daemon and bitcoin daemon in regtest mode. You may have to change the port one of them runs on, for example with the flag `-port=18445`.
To run a bitcoin daemon in regtest mode, with the ability to inspect transactions outside your wallet (useful for testing purposes), use the command
```
bitcoind -regtest -txindex=1 -daemon -port=18445
```
Be sure to run a Zcash daemon in regtest mode.
```
zcashd -regtest -txindex=1 --daemon
```
## Misc
I used the module [future](http://python-future.org/futurize.html) to make existing python2 code for the rpc interface compatible with python3.
Trade is done! Buyer or seller can check the status again, but it will indicate that it is complete.

View File

@ -15,11 +15,6 @@ from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
from xcat.utils import *
import zcash
import zcash.rpc
import pprint, json
from xcat.zcashRPC import parse_script
# SelectParams('testnet')
@ -28,10 +23,25 @@ SelectParams('regtest')
bitcoind = bitcoin.rpc.Proxy(service_url="http://user:password@127.0.0.1:18332")
FEE = 0.001*COIN
def validateaddress(addr):
return bitcoind.validateaddress(addr)
def find_secret(p2sh, fundtx_input):
print("fundtx_input:", fundtx_input)
txs = bitcoind.call('listtransactions', "*", 20, 0, True)
print('Length of txs from listtransactions():', len(txs))
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):
decoded = bitcoind.getrawtransaction(lx(txid), 1)
print("Decoded", decoded)
@ -128,16 +138,12 @@ def get_tx_details(txid):
fund_txinfo = bitcoind.gettransaction(lx(txid))
return fund_txinfo['details'][0]
# redeems automatically after buyer has funded tx, by scanning for transaction to the p2sh
# i.e., doesn't require buyer telling us fund txid
def auto_redeem(contract, secret):
print("Parsing script for auto_redeem...")
def redeem_contract(contract, secret):
print("Parsing script for redeem_contract...")
scriptarray = parse_script(contract.redeemScript)
redeemblocknum = scriptarray[8]
redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
# How to find redeemScript and redeemblocknum from blockchain?
print("Contract in auto redeem", contract.__dict__)
p2sh = contract.p2sh
#checking there are funds in the address
amount = check_funds(p2sh)
@ -151,83 +157,9 @@ def auto_redeem(contract, secret):
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
# Parsing redeemblocknum from the redeemscript of the p2sh
# redeemblocknum = find_redeemblocknum(contract)
blockcount = bitcoind.getblockcount()
print("\nCurrent blocknum at time of redeem on Bitcoin:", blockcount)
if blockcount < int(redeemblocknum):
# redeemPubKey = find_redeemAddr(contract)
print('redeemPubKey', redeemPubKey)
else:
print("nLocktime exceeded, refunding")
# refundPubKey = find_refundAddr(contract)
redeemPubKey = refundPubkey
print('refundPubKey', redeemPubKey)
# redeemPubKey = CBitcoinAddress.from_scriptPubKey(redeemPubKey)
# exit()
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])
# 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...
if blockcount >= int(redeemblocknum):
print("\nLocktime exceeded")
tx.nLockTime = redeemblocknum # Ariel: This is only needed when redeeming with the timelock
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])
print("SECRET", secret)
preimage = secret.encode('utf-8')
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
# exit()
print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
print('Redeem txhex', b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("script verified, sending raw tx")
txid = bitcoind.sendrawtransaction(tx)
fund_tx = str(fundtx['outpoint'])
redeem_tx = b2x(lx(b2x(txid)))
print("Returning fund_tx", fund_tx)
print("Txid of submitted redeem tx: ", redeem_tx)
print("TXID SUCCESSFULLY REDEEMED")
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else:
print("No contract for this p2sh found in database", p2sh)
def redeem_contract(contract, secret):
# How to find redeemScript and redeemblocknum from blockchain?
print("Contract in redeem_contract", contract.__dict__)
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
print("Found fundtx:", fundtx)
p2sh = P2SHBitcoinAddress(p2sh)
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
# TODO: Decodescript is not working, add back in.
# redeemblocknum = find_redeemblocknum(contract)
blockcount = bitcoind.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
if blockcount < contract.redeemblocknum:
# redeemPubKey = find_redeemAddr(contract)
redeemPubKey = P2PKHBitcoinAddress.from_bytes(x('7788b4511a25fba1092e67b307a6dcdb6da125d9'))
if blockcount < int(redeemblocknum):
print('redeemPubKey', redeemPubKey)
zec_redeemScript = CScript(x(contract.redeemScript))
txin = CMutableTxIn(fundtx['outpoint'])
@ -235,11 +167,11 @@ def redeem_contract(contract, secret):
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey
# TODO: protect privkey better, separate signing from rawtx creation
privkey = bitcoind.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
print("SECRET", secret)
preimage = b(secret)
preimage = secret.encode('utf-8')
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
print("txin.scriptSig", b2x(txin.scriptSig))
@ -256,7 +188,6 @@ def redeem_contract(contract, secret):
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else:
print("nLocktime exceeded, refunding")
refundPubKey = find_refundAddr(contract)
print('refundPubKey', refundPubKey)
txid = bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
fund_tx = str(fundtx['outpoint'])
@ -285,26 +216,6 @@ def find_refundAddr(contract):
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
return refundAddr
# def find_recipient(contract):
# initiator = CBitcoinAddress(contract.initiator)
# fulfiller = CBitcoinAddress(contract.fulfiller)
# print("Initiator", b2x(initiator))
# print("Fulfiler", b2x(fulfiller))
# make this dependent on actual fund tx to p2sh, not contract
# print("Contract fund_tx", contract.fund_tx)
# txid = contract.fund_tx
# raw = bitcoind.gettransaction(lx(txid))['hex']
# print("Raw tx", raw)
# # print("Raw", raw)
# decoded = zcashd.decoderawtransaction(raw + '00')
# scriptSig = decoded['vin'][0]['scriptSig']
# print("Decoded", scriptSig)
# asm = scriptSig['asm'].split(" ")
# pubkey = asm[1]
# print('pubkey', pubkey)
# redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
# print('redeemPubkey', redeemPubkey)
def find_transaction_to_address(p2sh):
bitcoind.importaddress(p2sh, "", False)
txs = bitcoind.listunspent()

View File

@ -28,7 +28,6 @@ def checkSellStatus(tradeid):
print("SECRET found in checksellactions", secret)
txs = seller_redeem_p2sh(trade, secret)
print("TXS IN SELLER REDEEM BUYER TX", txs)
trade.buy.fund_tx = txs['fund_tx']
trade.buy.redeem_tx = txs['redeem_tx']
print("TRADE SUCCESSFULLY REDEEMED", trade)
save_state(trade, tradeid)
@ -85,16 +84,19 @@ def checkBuyStatus(tradeid):
# If the two p2sh match...
# if buyer_p2sh == trade.buy.p2sh:
fund_tx = fund_contract(trade.buy)
print("Fund tx coming back in cli", fund_tx)
trade.buy.fund_tx = fund_tx
print("trade buy with redeemscript?", trade.buy.__dict__)
save_state(trade, tradeid)
# else:
# print("Compiled p2sh for htlc does not match what seller sent.")
elif status == 'sellerRedeemed':
secret = parse_secret(trade.buy.currency, trade.buy.redeem_tx)
print("FUND TX CLI", trade.buy.fund_tx)
secret = find_secret_from_fundtx(trade.buy.currency, trade.buy.p2sh, trade.buy.fund_tx)
print("Secret in cli", secret)
# secret = parse_secret(trade.buy.currency, trade.buy.redeem_tx)
if secret != None:
print("Found secret", secret)
txs = auto_redeem_p2sh(trade.sell, secret)
txs = redeem_p2sh(trade.sell, secret)
print("TXS IN SELLER REDEEMED", txs)
# trade.sell.fund_tx = txs['fund_tx']
trade.sell.redeem_tx = txs['redeem_tx']
@ -196,6 +198,9 @@ def main():
#TODO: implement
print("Run as daemon process")
# Ad hoc testing of workflow starts here
elif command == "step1":
tradeid = args.argument[0]
checkSellStatus(tradeid)
elif command == "step2":
# trade = get_trade()
tradeid = args.argument[0]

View File

@ -7,6 +7,14 @@ from xcat.utils import *
from xcat.trades import Contract, Trade
import xcat.userInput as userInput
def find_secret_from_fundtx(currency, p2sh, fundtx):
print("Fund tx in protocol.py", fundtx)
if currency == 'bitcoin':
secret = bitcoinRPC.find_secret(p2sh, fundtx)
else:
secret = zcashRPC.find_secret(p2sh, fundtx)
return secret
def import_addrs(trade):
check_fund_status(trade.sell.currency, trade.sell.p2sh)
check_fund_status(trade.buy.currency, trade.buy.p2sh)
@ -40,6 +48,7 @@ def fund_htlc(currency, p2sh, amount):
txid = bitcoinRPC.fund_htlc(p2sh, amount)
else:
txid = zcashRPC.fund_htlc(p2sh, amount)
print("fund_htlc txid", txid )
return txid
#
# def fund_buy_contract(trade):
@ -51,6 +60,7 @@ def fund_htlc(currency, p2sh, amount):
def fund_contract(contract):
txid = fund_htlc(contract.currency, contract.p2sh, contract.amount)
print("TXID coming back from fund_contract", txid)
return txid
def fund_sell_contract(trade):
@ -87,15 +97,6 @@ def create_buy_p2sh(trade, commitment, locktime):
save(trade)
def auto_redeem_p2sh(contract, secret):
currency = contract.currency
if currency == 'bitcoin':
res = bitcoinRPC.auto_redeem(contract, secret)
else:
res = zcashRPC.auto_redeem(contract, secret)
return res
def redeem_p2sh(contract, secret):
currency = contract.currency
if currency == 'bitcoin':

View File

@ -1 +1 @@
secret
fLBT1ywt

View File

@ -18,6 +18,7 @@ class CliTest(SimpleTestCase):
trade = cli.findtrade('test')
def test_newtrade(self):
cli.newtrade('test2')
cli.newtrade('test2')
cli.checkBuyStatus('test2')
cli.checkSellStatus('test2')

View File

@ -63,16 +63,16 @@ def get_fulfiller_addresses():
btc_addr = input("Enter the bitcoin address of the party you want to trade with: ")
# btc_addr = bXcat.new_bitcoin_addr()
# btc_addr = 'mgRG44X4PQC1ZCA4V654UZjJGJ3pxbApj2' # testnet
# btc_addr = "mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b"
btc_addr = "mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b" # regtest
# btc_addr = "mpFD3Knp5znDKAHyiYdXMGEYvxmShjdwSS" # server
btc_addr = 'mtRrCpixF7EvScmoeym3iY9dQeaMFyNGS2'
# btc_addr = 'mtRrCpixF7EvScmoeym3iY9dQeaMFyNGS2' # ariel
print(btc_addr)
zec_addr = input("Enter the zcash address of the party you want to trade with: ")
# zec_addr = zXcat.new_zcash_addr()
# zec_addr = 'tmLZu7MdjNdA6vbPTNTwdsZo91LnnrVTYB5' #testnet
# zec_addr = "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc"
zec_addr = "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc" # regtest
# zec_addr = "tmEGtCab8BJWq3fUa7TK4qhWuY9Ab7SHRh2" #server
zec_addr = 'tmYak55ijTBrx83oBnp9RHmqPZSp1uTnA61' # ariel
# zec_addr = 'tmYak55ijTBrx83oBnp9RHmqPZSp1uTnA61' # ariel
print(zec_addr)
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
return addresses

View File

@ -1 +1 @@
{"buy": {"amount": 0.02, "redeem_tx": "cac2f4fadf431ebb63d72dfea9a8b17792f6fca0d72f325b55d4798a880abc2d", "redeemScript": "63a820c34f63ae5db1f9d5eff9f174617fd21cfdd8450d22432eac0db334c80c3553708876a91456ffb9b1b332411bb4401093351c3cd7e28ebfc76703f7c501b17576a914fada8c2f0edaa8352c1b9df192cd92473fcf0af26888ac", "currency": "zcash", "fulfiller": "tmYak55ijTBrx83oBnp9RHmqPZSp1uTnA61", "fund_tx": "08d763de2b2d7ff6e8153980a5334bfb87ee8cfb406df0c674b4f6934fd89ecd:1", "initiator": "tmHeMnn93YuuBRHFDCLrenuufM3Crgw2Udu", "p2sh": "t2Lm6sh7VQWrW1gDTAJ4PgnfWmehitEMdKt", "redeemblocknum": 116215, "locktime": 10}, "commitment": "c34f63ae5db1f9d5eff9f174617fd21cfdd8450d22432eac0db334c80c355370", "sell": {"amount": 0.01, "redeemScript": "63a820c34f63ae5db1f9d5eff9f174617fd21cfdd8450d22432eac0db334c80c3553708876a9148da278b7ce5f31d2e723ae64e73248cd9addbce3670351a311b17576a91404fac4b9ff3758aeccf3f0e54831934e23b5e0876888ac", "currency": "bitcoin", "fulfiller": "mtRrCpixF7EvScmoeym3iY9dQeaMFyNGS2", "fund_tx": "f88ef71f871ab5f8a0477305bff5180fcfcccf14cde5df19e7592c9620e8a3d9", "initiator": "mfyHQX6EypNbZVYfZ6qekgeyzjkT9JZ4et", "p2sh": "2MtpFpJ8D4sNVAit4TgZbDfuTr9jnwVeSHJ", "redeemblocknum": 1155921}}
{"commitment": "ecd72edc27561378233ce2208358238fbd329f330bdd37596ff333a6a01323cd", "buy": {"redeemblocknum": 133, "p2sh": "t2PBPZSfLa9jBm2XyXtBF4GxnV65zdor9Q3", "locktime": 10, "fund_tx": "3b8ddbda1204f92d338e719772804395de0e9c8baaf50535edfa2a24ee4f22f2", "amount": 0.02, "redeem_tx": "549b73694a435f5db79e6dd21d39f9ac60cfbdf8ca15ed96f20cc5e66fb63e21", "currency": "zcash", "redeemScript": "63a820ecd72edc27561378233ce2208358238fbd329f330bdd37596ff333a6a01323cd8876a91465741c273525f5f4622d4b22b47168dab3055dc167028500b17576a914c04b595fc34f553ee756f9d9ad824462e75f4bfc6888ac", "initiator": "tmJxng1U3EMJaTRSDFT2SZZxCmXdJ7NRhZv", "fulfiller": "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc"}, "sell": {"redeemblocknum": 1081, "p2sh": "2NAycoLt43cPiXobAbASMkS6MwTWGpHbhio", "fund_tx": "e00144584303af28028bd51053bd67ae2cc085461b84bf4ee3fda30d14690763", "amount": 0.01, "redeem_tx": "57c4997351a588fd7694648aa5e7a6326cd83a4195e3f843340918334ed32d3f", "currency": "bitcoin", "redeemScript": "63a820ecd72edc27561378233ce2208358238fbd329f330bdd37596ff333a6a01323cd8876a914a581a5faa98ffb1cbe3ee75e1b4945ff18ec231e67023904b17576a914208462e3b373cf9e2b0b16e0d59eeb066f4873856888ac", "initiator": "miUtWZ2n71X3yahNu52eradR8vJxQRGw3Z", "fulfiller": "mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b"}}

View File

@ -28,7 +28,6 @@ def x2s(hexstring):
"""Convert hex to a utf-8 string"""
return binascii.unhexlify(hexstring).decode('utf-8')
def validateaddress(addr):
return zcashd.validateaddress(addr)
@ -74,9 +73,10 @@ def fund_htlc(p2sh, amount):
zcashd.importaddress(p2sh, "", False)
fund_txid = zcashd.sendtoaddress(p2sh, send_amount)
txid = b2x(lx(b2x(fund_txid)))
print("txid at end of fund_htlc in zcashRPC", txid)
# print("Dif version of txid", b2x(lx(fund_txid)))
return txid
# Following two functions are about the same
def check_funds(p2sh):
zcashd.importaddress(p2sh, "", False)
@ -105,110 +105,39 @@ def find_transaction_to_address(p2sh):
zcashd.importaddress(p2sh, "", False)
txs = zcashd.listunspent(0, 100)
for tx in txs:
# print("tx addr:", tx['address'])
# print(type(tx['address']))
# print(type(p2sh))
if tx['address'] == CBitcoinAddress(p2sh):
print("Found tx to p2sh", p2sh)
print(tx)
return tx
def find_secret(p2sh):
return parse_secret('4c25b5db9f3df48e48306891d8437c69308afa122f92416df1a3ba0d3604882f')
zcashd.importaddress(p2sh, "", False)
# is this working?
txs = zcashd.listtransactions()
def find_secret(p2sh, fundtx_input):
print("fundtx_input:", fundtx_input)
txs = zcashd.call('listtransactions', "*", 20, 0, True)
print('Length of txs from listtransactions():', len(txs))
for tx in txs:
# print("tx addr:", tx['address'])
# print(type(tx['address']))
# print(type(p2sh))
if (tx['address'] == p2sh ) and (tx['category'] == "send"):
print(type(tx['txid']))
print(str.encode(tx['txid']))
raw = zcashd.getrawtransaction(lx(tx['txid']),True)['hex']
decoded = zcashd.decoderawtransaction(raw)
print("deo:", decoded['vin'][0]['scriptSig']['asm'])
raw = zcashd.gettransaction(lx(tx['txid']))['hex']
decoded = zcashd.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):
raw = zcashd.gettransaction(lx(txid), True)['hex']
# print("Raw", raw)
raw = zcashd.gettransaction(txid, True)['hex']
decoded = zcashd.decoderawtransaction(raw)
scriptSig = decoded['vin'][0]['scriptSig']
print("Decoded", scriptSig)
asm = scriptSig['asm'].split(" ")
pubkey = asm[1]
secret = x2s(asm[2])
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
print('redeemPubkey', redeemPubkey)
print(secret)
print('Veryify redeemPubkey: ', redeemPubkey)
print("Found secret in parse_secret:", secret)
return secret
# redeems automatically after buyer has funded tx, by scanning for transaction to the p2sh
# i.e., doesn't require buyer telling us fund txid
def auto_redeem(contract, secret):
# How to find redeemScript and redeemblocknum from blockchain?
print("Contract in auto redeem", contract.__dict__)
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
p2sh = P2SHBitcoinAddress(p2sh)
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
# Where can you find redeemblocknum in the transaction?
redeemblocknum = find_redeemblocknum(contract)
blockcount = zcashd.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
if blockcount < redeemblocknum:
redeemPubKey = find_redeemAddr(contract)
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: figure out how to better protect privkey
privkey = zcashd.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
print("SECRET", secret)
preimage = secret.encode('utf-8')
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
print('Redeem txhex', b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("script verified, sending raw tx")
txid = zcashd.sendrawtransaction(tx)
redeem_tx = b2x(lx(b2x(txid)))
print("Txid of submitted redeem tx: ", redeem_tx)
fund_tx = str(fundtx['outpoint'])
print("Returning fund_tx", fund_tx)
print("TXID SUCCESSFULLY REDEEMED")
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else:
# if blockcount >= redeemblocknum:
# tx.nLockTime = redeemblocknum
print("nLocktime exceeded, refunding")
refundPubKey = find_refundAddr(contract)
print('refundPubKey', refundPubKey)
txid = zcashd.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
refund_tx = b2x(lx(b2x(txid)))
print("Txid of submitted refund tx: ", refund_tx)
fund_tx = str(fundtx['outpoint'])
print("Returning fund_tx", fund_tx)
print("TXID SUCCESSFULLY REFUNDED")
return {"refund_tx": redeem_tx, "fund_tx": fund_tx}
else:
print("No contract for this p2sh found in database", p2sh)
def redeem_contract(contract, secret):
# How to find redeemScript and redeemblocknum from blockchain?
print("Contract in redeem contract", contract.__dict__)
@ -302,7 +231,6 @@ def find_recipient(contract):
# make this dependent on actual fund tx to p2sh, not contract
txid = contract.fund_tx
raw = zcashd.gettransaction(lx(txid), True)['hex']
# print("Raw", raw)
decoded = zcashd.decoderawtransaction(raw)
scriptSig = decoded['vin'][0]['scriptSig']
print("Decoded", scriptSig)
@ -316,11 +244,6 @@ def find_recipient(contract):
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
print('redeemPubkey', redeemPubkey)
# addr = CBitcoinAddress('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ')
# print(addr)
# # print(b2x('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'))
# print(b2x(addr))
def new_zcash_addr():
addr = zcashd.getnewaddress()
return str(addr)