commit
158519ae0b
33
README.md
33
README.md
|
@ -1,15 +1,14 @@
|
|||
# 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).
|
||||
|
||||
## BTC p2sh HTLC script
|
||||
## Setup
|
||||
|
||||
The script `btc-p2sh-htlc.py` creates and redeems a p2sh transaction on Bitcoin regtest using a preimage. TODO: Locktime scripting still needs work.
|
||||
|
||||
To successfully run it, you'll need python3, the dependencies installed, and a bitcoin daemon running in regtest mode.
|
||||
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:
|
||||
```
|
||||
|
@ -22,15 +21,6 @@ To install dependencies, run:
|
|||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## ZEC p2sh HTLC script
|
||||
|
||||
The script `zec-p2sh-htlc.py` is the same as the BTC script, but uses python-zcashlib, which is still a work in progress.
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
|
@ -45,13 +35,26 @@ 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
|
||||
```
|
||||
|
||||
## XCAT CLI interface
|
||||
|
||||
Run `xcat.py` to go through the protocol. `xcat.py new` creats a new trade, `xcat.py seller` progresses with the seller's next step, `xcat.py buyer` progresses with the buyer's next step.
|
||||
|
||||
To test the entire script workflow, run `test.py`.
|
||||
|
||||
## Misc
|
||||
|
||||
`protocol-pseudocode.py` is guaranteed to not run. It was written as a brainstorm/sketch of a hypothetical xcat protocol using @ebfull's fork of Zcash/Bitcoin that supports createhtlc as an rpc command. Including here in case it's useful in any way.
|
||||
|
||||
I used the module [future](http://python-future.org/futurize.html) to make existing python2 code for the rpc interface compatible with python3.
|
||||
|
|
168
bXcat.py
168
bXcat.py
|
@ -12,7 +12,7 @@ from bitcoin.core import b2x, lx, b2lx, x, COIN, COutPoint, CMutableTxOut, CMuta
|
|||
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 bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
|
||||
|
||||
from utils import *
|
||||
|
||||
|
@ -27,6 +27,13 @@ FEE = 0.001*COIN
|
|||
|
||||
zcashd = zcash.rpc.Proxy()
|
||||
|
||||
def parse_secret(txid):
|
||||
decoded = bitcoind.getrawtransaction(lx(txid), 1)
|
||||
print("Decoded", decoded)
|
||||
# decoded = bitcoind.decoderawtransaction(raw)
|
||||
asm = decoded['vin'][0]['scriptSig']['asm'].split(" ")
|
||||
print(asm[2])
|
||||
|
||||
def get_keys(funder_address, redeemer_address):
|
||||
fundpubkey = CBitcoinAddress(funder_address)
|
||||
redeempubkey = CBitcoinAddress(redeemer_address)
|
||||
|
@ -42,18 +49,21 @@ def hashtimelockcontract(funder, redeemer, secret, locktime):
|
|||
redeemerAddr = CBitcoinAddress(redeemer)
|
||||
h = sha256(secret)
|
||||
blocknum = bitcoind.getblockcount()
|
||||
print("Current blocknum", blocknum)
|
||||
redeemblocknum = blocknum + locktime
|
||||
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
||||
print("REDEEMBLOCKNUM BITCOIN", redeemblocknum)
|
||||
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()
|
||||
print("Redeem script for p2sh contract on Bitcoin blockchain:", b2x(redeemScript))
|
||||
txin_scriptPubKey = 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}
|
||||
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(redeemScript), 'redeemer': redeemer, 'funder': funder}
|
||||
|
||||
def fund_htlc(p2sh, amount):
|
||||
send_amount = amount*COIN
|
||||
send_amount = float(amount) * COIN
|
||||
fund_txid = bitcoind.sendtoaddress(p2sh, send_amount)
|
||||
txid = b2x(lx(b2x(fund_txid)))
|
||||
return txid
|
||||
|
@ -87,75 +97,135 @@ def search_p2sh(block, 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)
|
||||
# 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
|
||||
print("Found fundtx:", fundtx)
|
||||
p2sh = P2SHBitcoinAddress(p2sh)
|
||||
if fundtx['address'] == p2sh:
|
||||
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||
|
||||
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']
|
||||
# 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 < redeemblocknum:
|
||||
redeemPubKey = find_redeemAddr(contract)
|
||||
print('redeemPubKey', redeemPubKey)
|
||||
else:
|
||||
print("nLocktime exceeded, refunding")
|
||||
redeemPubKey = find_refundAddr(contract)
|
||||
print('refundPubKey', redeemPubKey)
|
||||
# redeemPubKey = CBitcoinAddress.from_scriptPubKey(redeemPubKey)
|
||||
# exit()
|
||||
|
||||
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())
|
||||
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...
|
||||
# Is stored as hex, must convert to bytes
|
||||
zec_redeemScript = CScript(x(zec_redeemScript))
|
||||
|
||||
tx.nLockTime = redeemblocknum
|
||||
if blockcount >= 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?
|
||||
# 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']
|
||||
print("SECRET", 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
|
||||
# exit()
|
||||
|
||||
print("txin.scriptSig", b2x(txin.scriptSig))
|
||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
||||
|
||||
# print("txin.scriptSig", b2x(txin.scriptSig))
|
||||
# print("txin_scriptPubKey", b2x(txin_scriptPubKey))
|
||||
# print('tx', tx)
|
||||
print('Redeem txhex', b2x(tx.serialize()))
|
||||
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()))
|
||||
print("script verified, sending raw tx")
|
||||
txid = bitcoind.sendrawtransaction(tx)
|
||||
txhex = b2x(lx(b2x(txid)))
|
||||
print("Txid of submitted redeem tx: ", txhex)
|
||||
return txhex
|
||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
||||
return b2x(lx(b2x(txid)))
|
||||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
# takes hex and returns array of decoded script op codes
|
||||
def parse_script(script_hex):
|
||||
redeemScript = zcashd.decodescript(script_hex)
|
||||
scriptarray = redeemScript['asm'].split(' ')
|
||||
return scriptarray
|
||||
|
||||
def find_redeemblocknum(contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
redeemblocknum = scriptarray[8]
|
||||
return int(redeemblocknum)
|
||||
|
||||
def find_redeemAddr(contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
redeemer = scriptarray[6]
|
||||
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
|
||||
return redeemAddr
|
||||
|
||||
def find_refundAddr(contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
funder = scriptarray[13]
|
||||
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
|
||||
return refundAddr
|
||||
|
||||
# def find_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()
|
||||
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 new_bitcoin_addr():
|
||||
addr = bitcoind.getnewaddress()
|
||||
print('new btc addr', addr.to_scriptPubKey)
|
||||
return addr.to_scriptPubKey()
|
||||
|
||||
def generate(num):
|
||||
blocks = bitcoind.generate(num)
|
||||
return blocks
|
||||
|
|
15
checktx.py
15
checktx.py
|
@ -1,15 +0,0 @@
|
|||
#!/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 +0,0 @@
|
|||
{"t2QrLFUqmp1v1xQSE3hmgwcYuinRb3BRWMm": {"funder": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "redeemer": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "redeemblocknum": 182, "zec_redeemScript": "63a820343e398e4e99a68e7de6ec57f00b6a14a8e6d0a7dd714efbab4dcbd385f4f3038876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702b600b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "p2sh": "t2QrLFUqmp1v1xQSE3hmgwcYuinRb3BRWMm"}, "2MuWU5BpLpqJvvzkCPq8gFHA4VFFGyvjaJf": {"funder": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "redeemer": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "redeemblocknum": 146, "zec_redeemScript": "63a820343e398e4e99a68e7de6ec57f00b6a14a8e6d0a7dd714efbab4dcbd385f4f3038876a9147788b4511a25fba1092e67b307a6dcdb6da125d967029200b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "p2sh": "2MuWU5BpLpqJvvzkCPq8gFHA4VFFGyvjaJf"}}
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
|
||||
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
|
|
@ -1,70 +0,0 @@
|
|||
from rpc.ZDaemon import *
|
||||
import argparse, textwrap
|
||||
import hashlib
|
||||
|
||||
# How to prevent this program from having access to local bitcoind or zcashd? Have to just allow it?
|
||||
# A bit like a GUI wallet that you download, then use as a local app? Running its own instance of zcashd, bitcoind? (bitcoin SPV client possible?)
|
||||
#
|
||||
# UI: Seller puts in their address, buyer puts in theirs. (Job of DEX exchange can be to match orders...)
|
||||
# Interface extracts pubkeys
|
||||
# Interface determines locktime, and gives seller their "secret redeem code" for their funds (hash preimage)
|
||||
# Interface creates the HTLC with preimage
|
||||
# Interface imports address for the redeem script, for buyer, and for seller. (needs access to local zcashd/bitcoind)
|
||||
# Buyer sends to p2sh address, funding
|
||||
# Interface checks that it's in wallet, then creates the redeem transaction (rawtx) for buyer and seller
|
||||
# After set time passes, interface tries to complete the transaction
|
||||
# If falls through, uses redeem transactions.
|
||||
|
||||
zd = ZDaemon(network=TESTNET)
|
||||
|
||||
def get_pubkey_from_taddr():
|
||||
taddr = raw_input("Enter the taddr you would like to send from: ")
|
||||
resp = zd.validateaddress(taddr)
|
||||
if resp['pubkey']:
|
||||
pubkey = resp['pubkey']
|
||||
print "The pubkey for the address you entered is: ", pubkey
|
||||
return pubkey
|
||||
|
||||
def create_htlc(pubkey, sellerpubkey):
|
||||
# UI is going to be opinionated about timelocks, and provide secret for you.
|
||||
secret = raw_input("Enter a secret to lock your funds: ")
|
||||
# convert to bytes
|
||||
secret_bytes = str.encode(secret)
|
||||
digest = hashlib.sha256(preimage).digest()
|
||||
time = 10
|
||||
# need to add this rpc call, assume is running zcashd on zkcp branch
|
||||
htlc = zd.createhtlc(pubkey, sellerpubkey, secret_bytes, time)
|
||||
return htlc
|
||||
|
||||
def import_address(htlc):
|
||||
fund_addr = zd.importaddress(htlc['redeemScript'])
|
||||
txs = zd.listunspent()
|
||||
for tx in txs:
|
||||
if tx['address'] == htlc['address']
|
||||
return tx['address']
|
||||
|
||||
|
||||
def fund_p2sh(p2sh):
|
||||
fund_tx = zd.sendtoaddress(p2sh)
|
||||
return fund_tx
|
||||
|
||||
def redeem_p2sh(fund_tx):
|
||||
for tx in txs:
|
||||
if tx['address'] == htlc['address']
|
||||
return tx['address']
|
||||
|
||||
return tx['txid'], tx['vout']
|
||||
# write this function too
|
||||
rawtx = zd.createrawtransaction(txid, vout, selleraddress, amount)
|
||||
# Buyer has to sign raw tx
|
||||
|
||||
|
||||
# out of band: sellerpubkey, selleraddress
|
||||
pubkey = get_pubkey_from_taddr()
|
||||
# Wait for pubkey from buyer. Assume some messaging layer, or out of band communication, with seller.
|
||||
htlc = create_htlc(pubkey, sellerpubkey)
|
||||
# import address for both buyer and seller
|
||||
addr = import_address(htlc)
|
||||
# Buyer funds tx
|
||||
fund_tx = fund_p2sh(addr)
|
||||
# Now seller must redeem
|
|
@ -0,0 +1 @@
|
|||
Hm0Mla5n
|
|
@ -0,0 +1,103 @@
|
|||
import zXcat
|
||||
import bXcat
|
||||
from xcat import *
|
||||
|
||||
htlcTrade = Trade()
|
||||
print("Starting test of xcat...")
|
||||
|
||||
def get_initiator_addresses():
|
||||
return {'bitcoin': 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp', 'zcash': 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'}
|
||||
|
||||
def get_fulfiller_addresses():
|
||||
return {'bitcoin': 'mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z', 'zcash': 'tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY'}
|
||||
|
||||
def initiate(trade):
|
||||
# Get amounts
|
||||
amounts = {"sell": {"currency": "bitcoin", "amount": "0.5"}, "buy": {"currency": "zcash", "amount": "1.12"}}
|
||||
sell = amounts['sell']
|
||||
buy = amounts['buy']
|
||||
sell_currency = sell['currency']
|
||||
buy_currency = buy['currency']
|
||||
# Get addresses
|
||||
init_addrs = get_initiator_addresses()
|
||||
sell['initiator'] = init_addrs[sell_currency]
|
||||
buy['initiator'] = init_addrs[buy_currency]
|
||||
fulfill_addrs = get_fulfiller_addresses()
|
||||
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
||||
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
||||
# initializing contract classes with addresses, currencies, and amounts
|
||||
trade.sellContract = Contract(sell)
|
||||
trade.buyContract = Contract(buy)
|
||||
print(trade.sellContract.__dict__)
|
||||
print(trade.buyContract.__dict__)
|
||||
|
||||
secret = generate_password()
|
||||
print("Generating secret to lock funds:", secret)
|
||||
save_secret(secret)
|
||||
# TODO: Implement locktimes and mock block passage of time
|
||||
sell_locktime = 2
|
||||
buy_locktime = 4 # Must be more than first tx
|
||||
|
||||
create_sell_p2sh(trade, secret, sell_locktime)
|
||||
txid = fund_sell_contract(trade)
|
||||
print("Sent")
|
||||
create_buy_p2sh(trade, secret, buy_locktime)
|
||||
|
||||
def fulfill(trade):
|
||||
buy = trade.buyContract
|
||||
sell = trade.sellContract
|
||||
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
||||
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
||||
|
||||
if buy_p2sh_balance == 0:
|
||||
print("Buy amt:", buy.amount)
|
||||
txid = fund_buy_contract(trade)
|
||||
print("Fund tx txid:", txid)
|
||||
else:
|
||||
raise ValueError("Sell p2sh not funded, buyer cannot redeem")
|
||||
|
||||
def redeem_one(trade):
|
||||
buy = trade.buyContract
|
||||
if trade.sellContract.get_status() == 'redeemed':
|
||||
raise RuntimeError("Sell contract status was already redeemed before seller could redeem buyer's tx")
|
||||
else:
|
||||
secret = get_secret()
|
||||
print("GETTING SECRET IN TEST:", secret)
|
||||
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
|
||||
print("\nTX Type", tx_type)
|
||||
setattr(trade.buyContract, tx_type, txid)
|
||||
save(trade)
|
||||
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
||||
|
||||
def redeem_two(trade):
|
||||
if trade.sellContract.get_status() == 'redeemed':
|
||||
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
|
||||
elif trade.buyContract.get_status() == 'refunded':
|
||||
print("buyContract was refunded to buyer")
|
||||
else:
|
||||
# Buy contract is where seller disclosed secret in redeeming
|
||||
if trade.buyContract.currency == 'bitcoin':
|
||||
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
|
||||
else:
|
||||
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
|
||||
print("Found secret in seller's redeem tx", secret)
|
||||
redeem_tx = redeem_p2sh(trade.sellContract, secret)
|
||||
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
|
||||
save(trade)
|
||||
|
||||
def generate_blocks(num):
|
||||
bXcat.generate(num)
|
||||
zXcat.generate(num)
|
||||
|
||||
initiate(htlcTrade)
|
||||
fulfill(htlcTrade)
|
||||
|
||||
generate_blocks(6)
|
||||
|
||||
redeem_one(htlcTrade)
|
||||
redeem_two(htlcTrade)
|
||||
|
||||
# addr = CBitcoinAddress('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ')
|
||||
# print(addr)
|
||||
# # print(b2x('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'))
|
||||
# print(b2x(addr))
|
|
@ -0,0 +1,27 @@
|
|||
class Trade(object):
|
||||
def __init__(self, sellContract=None, buyContract=None):
|
||||
'''Create a new trade with a sell contract and buy contract across two chains'''
|
||||
self.sellContract = sellContract
|
||||
self.buyContract = buyContract
|
||||
|
||||
class Contract(object):
|
||||
def __init__(self, data):
|
||||
# Keep track of funding and redeem tx?
|
||||
allowed = ('fulfiller', 'initiator', 'currency', 'p2sh', 'amount', 'fund_tx', 'redeem_tx', 'secret', 'redeemScript', 'redeemblocknum')
|
||||
for key in data:
|
||||
if key in allowed:
|
||||
setattr(self, key, data[key])
|
||||
|
||||
def get_status(self):
|
||||
# keep as function or set as property?
|
||||
if hasattr(self, 'redeem_tx'):
|
||||
return 'redeemed'
|
||||
elif hasattr(self, 'refund_tx'):
|
||||
return 'refunded'
|
||||
elif hasattr(self, 'fund_tx'):
|
||||
# Do additional validation here to check amts on blockchain
|
||||
return 'funded'
|
||||
else:
|
||||
return 'empty'
|
||||
|
||||
# other classes; transactions? users?
|
|
@ -0,0 +1,79 @@
|
|||
from utils import *
|
||||
|
||||
def get_trade_amounts():
|
||||
print("in user input")
|
||||
amounts = {}
|
||||
sell_currency = input("Which currency would you like to trade out of (bitcoin or zcash)? ")
|
||||
if sell_currency == '':
|
||||
sell_currency = 'bitcoin'
|
||||
if sell_currency == 'bitcoin':
|
||||
buy_currency = 'zcash'
|
||||
else:
|
||||
buy_currency = 'bitcoin'
|
||||
print(sell_currency)
|
||||
sell_amt = input("How much {0} do you want to sell? ".format(sell_currency))
|
||||
sell_amt = 3.5
|
||||
print(sell_amt)
|
||||
buy_amt = input("How much {0} do you want to receive in exchange? ".format(buy_currency))
|
||||
buy_amt = 1.2
|
||||
print(buy_amt)
|
||||
sell = {'currency': sell_currency, 'amount': sell_amt}
|
||||
buy = {'currency': buy_currency, 'amount': buy_amt}
|
||||
amounts['sell'] = sell
|
||||
amounts['buy'] = buy
|
||||
return amounts
|
||||
|
||||
def create_password():
|
||||
secret = input("Initiating trade: Create a password to place the funds in escrow: ")
|
||||
# TODO: hash and store secret only locally.
|
||||
if secret == '':
|
||||
secret = generate_password()
|
||||
print('Remember your password:', secret)
|
||||
# Saving secret locally for now
|
||||
save_secret(secret)
|
||||
return secret
|
||||
|
||||
def retrieve_password():
|
||||
secret = input("Enter the secret you used to lock the funds in order to redeem:")
|
||||
if secret == '':
|
||||
secret = get_secret()
|
||||
print(secret)
|
||||
return secret
|
||||
|
||||
def authorize_fund_sell(htlcTrade):
|
||||
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(htlcTrade.sellContract.amount, htlcTrade.sellContract.currency, htlcTrade.sellContract.p2sh))
|
||||
response = input("Type 'enter' to allow this program to send funds on your behalf.")
|
||||
|
||||
def get_initiator_addresses():
|
||||
btc_addr = input("Enter your bitcoin address: ")
|
||||
# btc_addr = bXcat.new_bitcoin_addr()
|
||||
btc_addr = 'myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp'
|
||||
print(btc_addr)
|
||||
zec_addr = input("Enter your zcash address: ")
|
||||
# zec_addr = zXcat.new_zcash_addr()
|
||||
zec_addr = 'tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'
|
||||
print(zec_addr)
|
||||
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
|
||||
return addresses
|
||||
|
||||
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 = 'mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z'
|
||||
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 = 'tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY'
|
||||
print(zec_addr)
|
||||
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
|
||||
return addresses
|
||||
|
||||
def authorize_buyer_fulfill(sell_p2sh_balance, sell_currency, buy_p2sh_balance, buy_currency):
|
||||
input("The seller's p2sh is funded with {0} {1}, type 'enter' if this is the amount you want to buy in {1}.".format(sell_p2sh_balance, 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(buy_p2sh_balance, buy_currency))
|
||||
|
||||
def authorize_seller_redeem(buy):
|
||||
input("Buyer funded the contract where you offered to buy {0}, type 'enter' to redeem {1} {0} from {2}.".format(buy.currency, buy.amount, buy.p2sh))
|
||||
|
||||
def authorize_buyer_redeem(trade):
|
||||
input("Seller funded the contract where you paid them in {0} to buy {1}, type 'enter' to redeem {2} {1} from {3}.".format(trade.buyContract.currency, trade.sellContract.currency, trade.sellContract.amount, trade.sellContract.p2sh))
|
34
utils.py
34
utils.py
|
@ -1,11 +1,22 @@
|
|||
import hashlib
|
||||
import json
|
||||
import random
|
||||
import binascii
|
||||
|
||||
def hex2str(hexstring):
|
||||
return binascii.unhexlify(hexstring).decode('utf-8')
|
||||
|
||||
def sha256(secret):
|
||||
preimage = secret.encode('utf8')
|
||||
h = hashlib.sha256(preimage).digest()
|
||||
return h
|
||||
|
||||
def generate_password():
|
||||
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
passlen = 8
|
||||
p = "".join(random.sample(s,passlen))
|
||||
return p
|
||||
|
||||
# TODO: Port these over to leveldb or some other database
|
||||
def save_trade(trade):
|
||||
with open('xcat.json', 'w') as outfile:
|
||||
|
@ -23,11 +34,20 @@ 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
|
||||
# caching the secret locally for now...
|
||||
def get_secret():
|
||||
with open('secret.json') as data_file:
|
||||
for line in data_file:
|
||||
return line.strip('\n')
|
||||
|
||||
def save_contract(contracts):
|
||||
with open('contract.json', 'w') as outfile:
|
||||
json.dump(contracts, outfile)
|
||||
def save_secret(secret):
|
||||
with open('secret.json', 'w') as outfile:
|
||||
outfile.write(secret)
|
||||
|
||||
def save(trade):
|
||||
print("Saving trade")
|
||||
trade = {
|
||||
'sell': trade.sellContract.__dict__,
|
||||
'buy': trade.buyContract.__dict__
|
||||
}
|
||||
save_trade(trade)
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
7af1b1d333431217958032adc11b84278939d400a7368d33bab4e8455b4203f0
|
||||
7997c2eafeda0815b840bfbd60e5ad70d8879aac252bd594c732a04d6a7b85e9
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# /home/jay/Zcash/python3-xcat/protocol/checktx.py $@
|
||||
echo "$@" >> "/home/jay/Zcash/python3-xcat/protocol/watchdata"
|
|
@ -1 +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"}}
|
||||
{"sell": {"currency": "bitcoin", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "amount": "0.5", "redeemScript": "63a82033fb8f68c7e079a2a35cfcd8827279f8a55fa1be04c99debd8ed3e54954e08228876a9147788b4511a25fba1092e67b307a6dcdb6da125d96702bd01b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "fund_tx": "6cca31678d7fe6461277cea7e0614844e62ccc3ae4e247ca77d62bdd741fabef", "p2sh": "2N3rA4r6VSx65FeBTcgWWcR9HaW69DYDbrp", "redeemblocknum": 445}, "buy": {"currency": "zcash", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "amount": "1.12", "refund_tx": "5e34f0c634fb6207e9c85ed52528629404d25db29b68931b4e0278377ee776d6", "redeemScript": "63a82033fb8f68c7e079a2a35cfcd8827279f8a55fa1be04c99debd8ed3e54954e08228876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702e301b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "fund_tx": "8c9f9581a8cab00836b54237b30a6d68125067e3ffe6ae04413da2bdc9146daa", "p2sh": "t2UkYvcnigZt7FFp86UvTZ78qdGeLXw7tet", "redeemblocknum": 483}}
|
340
xcat.py
340
xcat.py
|
@ -6,6 +6,8 @@ from time import sleep
|
|||
import json
|
||||
import os, sys
|
||||
from pprint import pprint
|
||||
from trades import Contract, Trade
|
||||
import userInput
|
||||
|
||||
def check_p2sh(currency, address):
|
||||
if currency == 'bitcoin':
|
||||
|
@ -15,24 +17,6 @@ def check_p2sh(currency, address):
|
|||
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)
|
||||
|
@ -47,177 +31,166 @@ def fund_htlc(currency, p2sh, amount):
|
|||
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
|
||||
def fund_buy_contract(trade):
|
||||
buy = trade.buyContract
|
||||
txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
|
||||
setattr(trade.buyContract, 'fund_tx', txid)
|
||||
save(trade)
|
||||
return txid
|
||||
|
||||
# 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)
|
||||
def fund_sell_contract(trade):
|
||||
sell = trade.sellContract
|
||||
txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
|
||||
setattr(trade.sellContract, 'fund_tx', txid)
|
||||
save(trade)
|
||||
return txid
|
||||
|
||||
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")
|
||||
def create_sell_p2sh(trade, secret, locktime):
|
||||
# CREATE SELL CONTRACT
|
||||
sell = trade.sellContract
|
||||
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
|
||||
print("sell contract", contract)
|
||||
setattr(trade.sellContract, 'p2sh', contract['p2sh'])
|
||||
setattr(trade.sellContract, 'redeemScript', contract['redeemScript'])
|
||||
setattr(trade.sellContract, 'redeemblocknum', contract['redeemblocknum'])
|
||||
save(trade)
|
||||
|
||||
sell_amt = trade['sell']['amount']
|
||||
txid = fund_htlc(currency, sell_p2sh, sell_amt)
|
||||
def create_buy_p2sh(trade, secret, locktime):
|
||||
## CREATE BUY CONTRACT
|
||||
buy = trade.buyContract
|
||||
print("Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy.currency))
|
||||
print("HTLC DETAILS", buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
|
||||
buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
|
||||
print("Buy contract", buy_contract)
|
||||
|
||||
trade['sell']['p2sh'] = sell_p2sh
|
||||
trade['sell']['fund_tx'] = txid
|
||||
trade['sell']['status'] = 'funded'
|
||||
# TODO: Save secret locally for seller
|
||||
trade['sell']['secret'] = secret
|
||||
setattr(trade.buyContract, 'p2sh', buy_contract['p2sh'])
|
||||
setattr(trade.buyContract, 'redeemScript', buy_contract['redeemScript'])
|
||||
setattr(trade.buyContract, 'redeemblocknum', buy_contract['redeemblocknum'])
|
||||
print("Now contact the buyer and tell them to send funds to this p2sh: ", trade.buyContract.p2sh)
|
||||
|
||||
save_trade(trade)
|
||||
save(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
|
||||
def redeem_p2sh(contract, secret):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = bXcat.redeem(p2sh, action)
|
||||
res = bXcat.auto_redeem(contract, secret)
|
||||
else:
|
||||
res = zXcat.redeem(p2sh, action)
|
||||
res = zXcat.auto_redeem(contract, secret)
|
||||
return res
|
||||
|
||||
def seller_redeem():
|
||||
# add locktime as variable?
|
||||
def print_trade(role):
|
||||
print("\nTrade status for {0}:".format(role))
|
||||
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)
|
||||
pprint(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']))
|
||||
#### Main functions determining user flow from command line
|
||||
def buyer_redeem(trade):
|
||||
userInput.authorize_buyer_redeem(trade)
|
||||
if trade.sellContract.get_status() == 'redeemed':
|
||||
print("You already redeemed the funds and acquired {0} {1}".format(trade.sellContract.amount, trade.sellContract.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)
|
||||
p2sh = trade.sellContract.p2sh
|
||||
currency = trade.sellContract.currency
|
||||
# Buy contract is where seller disclosed secret in redeeming
|
||||
if trade.buyContract.currency == 'bitcoin':
|
||||
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
|
||||
else:
|
||||
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
|
||||
print("Found secret in seller's redeem tx", secret)
|
||||
redeem_tx = redeem_p2sh(trade.sellContract, secret)
|
||||
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
|
||||
save(trade)
|
||||
exit()
|
||||
|
||||
def print_trade(role):
|
||||
print("Trade status:")
|
||||
trade = get_trade()
|
||||
if role == 'seller':
|
||||
pprint(trade)
|
||||
def seller_redeem(trade):
|
||||
buy = trade.buyContract
|
||||
userInput.authorize_seller_redeem(buy)
|
||||
|
||||
if trade.sellContract.get_status() == 'redeemed':
|
||||
print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency))
|
||||
exit()
|
||||
else:
|
||||
del trade['sell']['secret']
|
||||
pprint(trade)
|
||||
# Seller redeems buyer's funded tx (contract in p2sh)
|
||||
secret = userInput.retrieve_password()
|
||||
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
|
||||
setattr(trade.buyContract, tx_type, txid)
|
||||
save(trade)
|
||||
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
||||
print_trade('seller')
|
||||
|
||||
def buyer_fulfill(trade):
|
||||
buy = trade.buyContract
|
||||
sell = trade.sellContract
|
||||
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
||||
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
||||
|
||||
if buy_p2sh_balance == 0:
|
||||
userInput.authorize_buyer_fulfill(sell_p2sh_balance, sell.currency, buy_p2sh_balance, buy.currency)
|
||||
print("Buy amt:", buy.amount)
|
||||
txid = fund_buy_contract(trade)
|
||||
print("Fund tx txid:", txid)
|
||||
else:
|
||||
print("It looks like you've already funded the contract to buy {1}, the amount in escrow in the p2sh is {0}.".format(buy_p2sh_balance, buy.currency))
|
||||
print("Please wait for the seller to remove your funds from escrow to complete the trade.")
|
||||
print_trade('buyer')
|
||||
|
||||
def seller_initiate(trade):
|
||||
# Get amounts
|
||||
amounts = userInput.get_trade_amounts()
|
||||
sell = amounts['sell']
|
||||
buy = amounts['buy']
|
||||
sell_currency = sell['currency']
|
||||
buy_currency = buy['currency']
|
||||
# Get addresses
|
||||
init_addrs = userInput.get_initiator_addresses()
|
||||
sell['initiator'] = init_addrs[sell_currency]
|
||||
buy['initiator'] = init_addrs[buy_currency]
|
||||
fulfill_addrs = userInput.get_fulfiller_addresses()
|
||||
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
||||
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
||||
# initializing contract classes with addresses, currencies, and amounts
|
||||
trade.sellContract = Contract(sell)
|
||||
trade.buyContract = Contract(buy)
|
||||
print(trade.sellContract.__dict__)
|
||||
print(trade.buyContract.__dict__)
|
||||
|
||||
secret = userInput.create_password()
|
||||
# TODO: Implement locktimes and mock block passage of time
|
||||
sell_locktime = 5
|
||||
buy_locktime = 10 # Must be more than first tx
|
||||
|
||||
create_sell_p2sh(trade, secret, sell_locktime)
|
||||
|
||||
userInput.authorize_fund_sell(trade)
|
||||
|
||||
txid = fund_sell_contract(trade)
|
||||
print("Sent")
|
||||
|
||||
create_buy_p2sh(trade, secret, buy_locktime)
|
||||
print_trade('seller')
|
||||
|
||||
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
|
||||
print("=" * 50)
|
||||
|
||||
trade = get_trade()
|
||||
|
||||
if trade == None:
|
||||
htlcTrade = Trade()
|
||||
print("New empty trade")
|
||||
else:
|
||||
buyContract = Contract(trade['buy'])
|
||||
sellContract = Contract(trade['sell'])
|
||||
htlcTrade = Trade(buyContract=buyContract, sellContract=sellContract)
|
||||
|
||||
try:
|
||||
if sys.argv[1] == 'new':
|
||||
erase_trade()
|
||||
role = 'seller'
|
||||
trade = get_trade()
|
||||
htlcTrade = Trade()
|
||||
print("Creating new XCAT transaction...")
|
||||
else:
|
||||
role = sys.argv[1]
|
||||
print("Your role in demo:", role)
|
||||
|
@ -233,46 +206,31 @@ if __name__ == '__main__':
|
|||
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':
|
||||
if htlcTrade.buyContract is not None and htlcTrade.sellContract is not None:
|
||||
if htlcTrade.sellContract.get_status() == 'redeemed' and htlcTrade.buyContract.get_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')
|
||||
if htlcTrade.sellContract == None:
|
||||
seller_initiate(htlcTrade)
|
||||
elif htlcTrade.buyContract.get_status() == 'funded':
|
||||
seller_redeem(htlcTrade)
|
||||
elif htlcTrade.buyContract.get_status() == 'empty':
|
||||
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(htlcTrade.buyContract.currency))
|
||||
else:
|
||||
# Need better way of preventing buyer from having secret
|
||||
if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
|
||||
# if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
|
||||
if htlcTrade.sellContract.get_status() == 'funded' and htlcTrade.buyContract.get_status() != 'redeemed':
|
||||
print("One active trade available, fulfilling buyer contract...")
|
||||
trade = get_trade()
|
||||
buyer_fulfill()
|
||||
buyer_fulfill(htlcTrade)
|
||||
# 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':
|
||||
elif htlcTrade.buyContract.get_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()
|
||||
buyer_redeem(htlcTrade)
|
||||
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
|
||||
|
|
210
zXcat.py
210
zXcat.py
|
@ -14,7 +14,7 @@ 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 zcash.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
|
||||
|
||||
from utils import *
|
||||
|
||||
|
@ -39,7 +39,9 @@ def hashtimelockcontract(funder, redeemer, secret, locktime):
|
|||
redeemerAddr = CBitcoinAddress(redeemer)
|
||||
h = sha256(secret)
|
||||
blocknum = zcashd.getblockcount()
|
||||
print("Current blocknum", blocknum)
|
||||
redeemblocknum = blocknum + locktime
|
||||
print("REDEEMBLOCKNUM ZCASH", redeemblocknum)
|
||||
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])
|
||||
|
@ -49,10 +51,10 @@ def hashtimelockcontract(funder, redeemer, secret, locktime):
|
|||
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}
|
||||
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(zec_redeemScript), 'redeemer': redeemer, 'funder': funder}
|
||||
|
||||
def fund_htlc(p2sh, amount):
|
||||
send_amount = amount*COIN
|
||||
send_amount = float(amount)*COIN
|
||||
fund_txid = zcashd.sendtoaddress(p2sh, send_amount)
|
||||
txid = b2x(lx(b2x(fund_txid)))
|
||||
return txid
|
||||
|
@ -70,68 +72,168 @@ 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']
|
||||
def find_transaction_to_address(p2sh):
|
||||
zcashd.importaddress(p2sh, "", False)
|
||||
txs = zcashd.listunspent()
|
||||
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
|
||||
|
||||
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))
|
||||
# def get_tx_details(txid):
|
||||
# # This method is problematic I haven't gotten the type conversions right
|
||||
# print(bytearray.fromhex(txid))
|
||||
# print(b2x(bytearray.fromhex(txid)))
|
||||
# fund_txinfo = zcashd.gettransaction(bytearray.fromhex(txid))
|
||||
# print(fund_txinfo)
|
||||
#
|
||||
# return fund_txinfo['details'][0]
|
||||
|
||||
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])
|
||||
def find_secret(p2sh):
|
||||
return parse_secret('4c25b5db9f3df48e48306891d8437c69308afa122f92416df1a3ba0d3604882f')
|
||||
zcashd.importaddress(p2sh, "", False)
|
||||
# is this working?
|
||||
txs = zcashd.listtransactions()
|
||||
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'])
|
||||
|
||||
# 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)
|
||||
def parse_secret(txid):
|
||||
raw = zcashd.gettransaction(lx(txid), True)['hex']
|
||||
# print("Raw", raw)
|
||||
decoded = zcashd.decoderawtransaction(raw)
|
||||
scriptSig = decoded['vin'][0]['scriptSig']
|
||||
print("Decoded", scriptSig)
|
||||
asm = scriptSig['asm'].split(" ")
|
||||
pubkey = asm[1]
|
||||
secret = hex2str(asm[2])
|
||||
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
|
||||
print('redeemPubkey', redeemPubkey)
|
||||
print(secret)
|
||||
return secret
|
||||
|
||||
# print('zec_redeemScript', zec_redeemScript)
|
||||
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
|
||||
# print("Redeem tx hex:", b2x(tx.serialize()))
|
||||
# redeems automatically after buyer has funded tx, by scanning for transaction to the p2sh
|
||||
# i.e., doesn't require buyer telling us fund txid
|
||||
|
||||
# Can only call to_p2sh_scriptPubKey on CScript obj
|
||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
||||
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
|
||||
print("Found fundtx:", fundtx)
|
||||
p2sh = P2SHBitcoinAddress(p2sh)
|
||||
if fundtx['address'] == p2sh:
|
||||
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
|
||||
|
||||
# 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
|
||||
# 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)
|
||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
||||
print("TXID SUCCESSFULLY REDEEMED")
|
||||
return 'redeem_tx', b2x(lx(b2x(txid)))
|
||||
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)
|
||||
print("Txid of refund tx:", b2x(lx(b2x(txid))))
|
||||
print("TXID SUCCESSFULLY REFUNDED")
|
||||
return 'refund_tx', b2x(lx(b2x(txid)))
|
||||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def parse_script(script_hex):
|
||||
redeemScript = zcashd.decodescript(script_hex)
|
||||
scriptarray = redeemScript['asm'].split(' ')
|
||||
return scriptarray
|
||||
|
||||
def find_redeemblocknum(contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
redeemblocknum = scriptarray[8]
|
||||
return int(redeemblocknum)
|
||||
|
||||
def find_redeemAddr(contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
redeemer = scriptarray[6]
|
||||
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
|
||||
return redeemAddr
|
||||
|
||||
def find_refundAddr(contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
funder = scriptarray[13]
|
||||
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
|
||||
return refundAddr
|
||||
|
||||
def find_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)
|
||||
asm = scriptSig['asm'].split(" ")
|
||||
pubkey = asm[1]
|
||||
initiator = CBitcoinAddress(contract.initiator)
|
||||
fulfiller = CBitcoinAddress(contract.fulfiller)
|
||||
print("Initiator", b2x(initiator))
|
||||
print("Fulfiler", b2x(fulfiller))
|
||||
print('pubkey', pubkey)
|
||||
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()
|
||||
print('new ZEC addr', addr.to_p2sh_scriptPubKey)
|
||||
return addr.to_scriptPubKey()
|
||||
|
||||
def generate(num):
|
||||
blocks = zcashd.generate(num)
|
||||
return blocks
|
||||
|
|
|
@ -16,6 +16,7 @@ from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH
|
|||
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
||||
from zcash.wallet import CBitcoinAddress, CBitcoinSecret
|
||||
import hashlib
|
||||
import binascii
|
||||
|
||||
# SelectParams('testnet')
|
||||
SelectParams('regtest')
|
||||
|
|
Loading…
Reference in New Issue