This commit is contained in:
Ariel Gabizon 2017-07-26 20:35:55 +02:00
parent 4e100c46c3
commit 15ff860746
6 changed files with 110 additions and 92 deletions

30
api.py
View File

@ -103,22 +103,32 @@ def buyer_fund():
def seller_redeem_before_signature():
def seller_redeem():
trade = get_seller_trade()
print(trade)
print("SELLER REDEEMING BUY CONTRACT")
print("=============================")
buy = trade.buyContract
print(buy)
# 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() # Just the seller getting his local copy of the secret
print("SELLER SECRET IN TEST:", secret)
txid = redeem_p2sh(trade.buyContract, secret, trade.sellContract)
setattr(trade.buyContract, 'redeem_tx', txid)
save(trade)
(buy,sell) = init_redeem_p2sh(trade.buyContract, trade.sellContract)
# in case we're still in the time lock on buy side, try to redeem with secret
if(buy.redeemtype != ""):
privkey = get_redeemer_priv_key(buy)
buy = get_raw_redeem(buy)
if(sell.redeemtype != ""):
privkey = get_redeemer_priv_key(sell)
contract = check trade(trade.buyContract, secret, trade.sellContract)
setattr(trade.buyContract, 'redeem_tx', txid)
save_seller(trade)
def seller_sign_edeem():
def seller_redeem_after_signature():
trade
def buyer_redeem():
print("BUYER REDEEMING SELL CONTRACT")
@ -148,7 +158,7 @@ def buyer_redeem():
print("Found secret in seller's redeem tx on zcash chain:", secret)
redeem_tx = redeem_p2sh(sellContract, secret, buyContract)
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
save(trade)
save_buyer(trade)
def generate_blocks(num):

View File

@ -26,6 +26,11 @@ bitcoind = bitcoin.rpc.Proxy()
FEE = 0.001*COIN
zcashd = zcash.rpc.Proxy()
def send_raw_tx(rawtx):
txid = bitcoind.sendrawtransaction(rawtx)
return txid
def import_address(address):
bitcoind.importaddress(address, "", False)
@ -92,24 +97,42 @@ def get_tx_details(txid):
fund_txinfo = bitcoind.gettransaction(lx(txid))
return fund_txinfo['details'][0]
# redeems funded tx automatically, by scanning for transaction to the p2sh
# i.e., doesn't require buyer telling us fund txid
# returns false if fund tx doesn't exist or is too small
def check_redeem_with_secret(contract):
def get_redeemer_priv_key(contract):
if (contract.redeemtype == 'secret'):
redeemPubKey = find_redeemAddr(contract)
elif (contract.redeemtype = 'timelock'):
redeemPubKey = find_refundAddr(contract)
else:
raise ValueError("Invalid redeemtype:", contract.redeemtype)
return bitcoind.dumpprivkey(redeemPubKey)
def check_and_return_fundtx(contract):
# How to find redeemscript and redeemblocknum from blockchain?
print("Redeeming contract using secret", contract.__dict__)
p2sh = contract.p2sh
minamount = float(contract.amount)
# checking there are funds in the address
amount = check_funds(p2sh)
if(amount < minamount):
print("address ", p2sh, " not sufficiently funded")
return false
# may have problems in case funder funded address in more than one tx
# the funder may have accidentily funded the p2sh with sufficient amount in several transactions. The current code
# will abort in this case. This is a conservative approach to prevent the following attack, for example: the funder splits
# the amount into many tiny outputs, hoping the redeemer will not have time to redeem them all by the timelock.
fundtx = find_transaction_to_address(p2sh)
if(fundtx=""):
raise ValueError("fund tx to ", p2sh, " not found")
amount = fundtx['amount'] / COIN
if(amount < minamount):
print("funder funded ", p2sh, " in more than one tx will need to run redeem again to get whole amount")
contract.fund_tx = fund_tx
return contract
# assuming we have the correct fund tx in the contract prepares the signed redeem raw tx
def get_raw_redeem(contract, privkey)
p2sh = contract.p2sh
p2sh = P2SHBitcoinAddress(p2sh)
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
@ -126,29 +149,23 @@ def check_redeem_with_secret(contract):
print("No contract for this p2sh found in database", p2sh)
def finish_redeem_with_secret():
sighash = SignatureHash(redeemscript, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey
print("herebeforedump")
privkey = bitcoind.dumpprivkey(redeemPubKey)
print("hereafterdump")
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
sighash = SignatureHash(redeemscript, tx, 0, SIGHASH_ALL)
secret = get_secret() # assumes secret is present in secret.json
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
if(contract.redeemtype = "secret"):
print("SECRET", secret)
preimage = secret.encode('utf-8')
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemscript])
# exit()
# print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = 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)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
return b2x(lx(b2x(txid)))
elif(contract.redeemtype = "timelock"):
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemscript])
else:
raise ValueError("invalid redeemtype:", contract.redeemtype)
txin_scriptPubKey = redeemscript.to_p2sh_scriptPubKey()
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("script verified, writing raw redeem tx in contract")
contract.rawredeemtx = tx
return contract
@ -265,6 +282,7 @@ def find_transaction_to_address(p2sh):
if tx['address'] == CBitcoinAddress(p2sh):
print("Found tx to p2sh", p2sh, "tx is", tx)
return tx
return ""
def new_bitcoin_addr():
addr = bitcoind.getnewaddress()

View File

@ -7,7 +7,7 @@ class Trade(object):
class Contract(object):
def __init__(self, data):
# Keep track of funding and redeem tx?
allowed = ('funder', 'redeemer', 'currency', 'p2sh', 'amount', 'fund_tx', 'redeem_tx', 'secret', 'redeemscript', 'redeemblocknum','hash_of_secret')
allowed = ('funder', 'redeemer', 'currency', 'p2sh', 'amount', 'fund_tx', 'redeem_tx', 'secret', 'redeemscript', 'redeemblocknum','hash_of_secret','redeemtype')
for key in data:
if key in allowed:
setattr(self, key, data[key])

View File

@ -17,12 +17,12 @@ def generate_password():
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:
json.dump(trade, outfile)
'''
def save_seller_trade(trade):
with open('sellertrade.json', 'w') as outfile:
json.dump(jsonformat(trade), outfile)
@ -43,7 +43,7 @@ def get_init():
trade = trades.Trade(sellContract,buyContract)
return trade
'''
def get_trade():
with open('xcat.json') as data_file:
# try:
@ -52,7 +52,7 @@ def get_trade():
buyContract = trades.Contract(xcatdb['buy'])
trade = trades.Trade(sellContract,buyContract)
return trade
return trade'''
def get_seller_trade():
with open('init.json') as data_file:

50
xcat.py
View File

@ -97,7 +97,7 @@ def create_buy_2sh(trade, secret, locktime):
# we try to redeem contract with secret
# we try to redeem revertcontract with time lock
# returns True if at least one redeem succeeded
def prepare_redeem_p2sh(contract, secret, revertcontract):
def redeem_p2sh(contract, secret, revertcontract):
currency = contract.currency
res = False
@ -129,6 +129,7 @@ def prepare_redeem_p2sh(contract, secret, revertcontract):
except Exception:
print("Failed - the other party might have redeemed the fund tx on the btc chain with the secret by now")
if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))
print("HHERE")
if (revert_currency == 'zcash'):
if(zXcat.still_locked(revertcontract)):
print('too early for redeeminng with time lock on zcash chain')
@ -142,53 +143,6 @@ def prepare_redeem_p2sh(contract, secret, revertcontract):
return res
def finish_redeem_p2sh(contract, secret, revertcontract):
currency = contract.currency
res = False
revert_currency = revertcontract.currency
# should the code still try to redeem when time has passed? currently it doesn't
if (currency == 'bitcoin' and bXcat.still_locked(contract)):
print("trying to redeem btc with secret:")
try:
res = bXcat.redeem_with_secret(contract, secret)
except Exception:
print("Failed - you might not have the correct secret - perhaps because the seller has not redeemed the buy contract correctly")
if(res): print("You have redeemed {0} {1}!".format(contract.amount, contract.currency))
if (currency == 'zcash' and zXcat.still_locked(contract)):
print("trying to redeeming zec with secret:")
try:
res = zXcat.redeem_with_secret(contract, secret)
except Exception:
print("Failed - you might not have the correct secret - perhaps because the seller has not redeemed the buy contract correctly")
if(res): print("You have redeemed {0} {1}!".format(contract.amount, contract.currency))
if (revert_currency == 'bitcoin'):
if(bXcat.still_locked(revertcontract)):
print('too early for redeeminng with time lock on btc chain')
else:
print("trying to redeeming btc with timelock:")
try:
res = bXcat.redeem_after_timelock(revertcontract)
except Exception:
print("Failed - the other party might have redeemed the fund tx on the btc chain with the secret by now")
if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))
if (revert_currency == 'zcash'):
if(zXcat.still_locked(revertcontract)):
print('too early for redeeminng with time lock on zcash chain')
else:
print("trying to redeeming zec with timelock:")
try:
res = zXcat.redeem_after_timelock(revertcontract)
except Exception:
print("Failed - the other party might have redeemed the fund tx on the zcash chain with the secret by now")
if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))
return res
def print_trade(role):
print("\nTrade status for {0}:".format(role))
trade = get_trade()

View File

@ -23,6 +23,10 @@ SelectParams('regtest')
zcashd = zcash.rpc.Proxy()
FEE = 0.0001*COIN
def send_raw_tx(rawtx):
txid = zcashd.sendrawtransaction(rawtx)
return txid
def import_address(address):
zcashd.importaddress(address, "", False)
@ -361,3 +365,35 @@ def redeem_after_timelock(contract):
txid = zcashd.sendrawtransaction(tx)
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
return b2x(lx(b2x(txid)))
def get_redeemer_priv_key(contract):
if (contract.redeemtype == 'secret'):
redeemPubKey = find_redeemAddr(contract)
elif (contract.redeemtype = 'timelock'):
redeemPubKey = find_refundAddr(contract)
else:
raise ValueError("Invalid redeemtype:", contract.redeemtype)
return zcashd.dumpprivkey(redeemPubKey)
def check_and_return_fundtx(contract):
# How to find redeemscript and redeemblocknum from blockchain?
print("Redeeming contract using secret", contract.__dict__)
p2sh = contract.p2sh
minamount = float(contract.amount)
# the funder may have accidentily funded the p2sh with sufficient amount in several transactions. The current code
# will abort in this case. This is a conservative approach to prevent the following attack, for example: the funder splits
# the amount into many tiny outputs, hoping the redeemer will not have time to redeem them all by the timelock.
fundtx = find_transaction_to_address(p2sh)
if(fundtx=""):
raise ValueError("fund tx to ", p2sh, " not found")
amount = fundtx['amount'] / COIN
if(amount < minamount):
print("funder funded ", p2sh, " in more than one tx will need to run redeem again to get whole amount")
contract.fund_tx = fund_tx
return contract