adapt api for ethereum xcat

This commit is contained in:
Ariel Gabizon 2017-07-13 21:14:28 -04:00
parent 220f55ed7c
commit a567a69581
6 changed files with 431 additions and 6 deletions

64
apiForETHXCAT.py Normal file
View File

@ -0,0 +1,64 @@
import zXcat
import bXcat
from xcat import *
print("Starting test of xcat...")
def Zcash_getaddr()
return zXcatForEth.zcashd.getnewaddresss()
def Zcash_fund(p2sh,amount)
fund_txid = zXcatForEth.zcashd.sendtoaddress(p2sh,amount)
return fund_txid
def Zcash_getredeemscript_andp2sh(seller, buyer, hash_of_secret, lock_increment)
return zXcatForEth.hashtimelockcontract(seller, buyer, hash_of_secret, lock_increment)
# finds seller's redeem tx and gets secret from it
def Zcash_get_secret(p2sh,fund_txid)
return zXcatForEth.find_secret(p2sh,fund_tx)
def Zcash_refund(redeemscript,buyer_ad,p2sh,fund_txid)
return zXcatForEth.
def Zcash_redeem(redeemscript,secret,p2sh, amount):
txid = zXcatForETH.redeem_with_secret(trade.buyContract, secret, trade.sellContract)
return txid
def redeem_buyer():
print("BUYER REDEEMING SELL CONTRACT")
print("=============================")
trade = get_trade()
buyContract = trade.buyContract
sellContract = trade.sellContract
secret = ""
# if sellContract.get_status() == 'redeemed':
# raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
# elif buyContract.get_status() == 'refunded':
# print("buyContract was refunded to buyer")
# else:
# Buy contract is where seller disclosed secret in redeeming
if buyContract.currency == 'bitcoin':
if (bXcat.still_locked(buyContract)):
if(not hasattr(buyContract,'fund_tx')):
print("Seems address has not been funded yet. Aborting.")
quit()
secret = bXcat.find_secret(buyContract.p2sh,buyContract.fund_tx)
if(secret != ""):
print("Found secret in seller's redeem tx on bitcoin chain:", secret)
else:
if zXcat.still_locked(buyContract):
secret = zXcat.find_secret(buyContract.p2sh,buyContract.fund_tx)
if(secret != ""):
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)
def generate_blocks(num):
bXcat.generate(num)
zXcat.generate(num)

View File

@ -283,7 +283,7 @@ def find_secret(p2sh,vinid):
bitcoind.importaddress(p2sh, "", True)
# is this working?
txs = bitcoind.listtransactions()
txs = bitcoind.listreceivedbyaddress()
# print("==========================================LISTTT============", txs)
# print()
for tx in txs:

View File

@ -1 +1 @@
N9BkxaG0
4tqcS72n

View File

@ -6,10 +6,10 @@ htlcTrade = initiate()
fund_buyer()
# zXcat.generate(8)
zXcat.generate(6)
# zXcat.generate(6)
redeem_seller()
zXcat.generate(2)
bXcat.generate(20)
zXcat.generate(1)
bXcat.generate(1)
redeem_buyer()
@ -17,3 +17,4 @@ redeem_buyer()
# print(addr)
# # print(b2x('tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ'))
# print(b2x(addr))
e

View File

@ -1 +1 @@
{"sell": {"currency": "zcash", "initiator": "tmYU7UVFGVK2cmNqQ9dLoNFZMWSvZWCeKtD", "redeemblocknum": 2109, "amount": "0.01", "fulfiller": "tmVx4ckwZAZFPwxMGgrpZ6N7wp3VRgDuXfy", "fund_tx": "ebaa6f3c99ed282701eb8ed926ca822cd9b52458f72e9170988920ae1cffd7ff", "redeem_tx": false, "p2sh": "t2VK8Pgu4GEfnBrh8bLTND1ns3bfQUw7wP9", "redeemScript": "63a8202bf467e67c060bc84c7cc1a47791d8678d8eac11a9e80af993b767661131e7f38876a914ddfa63110b8569bc80c0e5cbe3ea9df142ddea6e67023d08b17576a914f99980ad0a78f8f742d88d97726cc9c91df172f86888ac"}, "buy": {"currency": "bitcoin", "initiator": "mrjxeVKPWCEQYEU9du3CnGT9NKpVNUzSKz", "redeemblocknum": 7936, "amount": "1.12", "fulfiller": "miN9ygMUHEipaobPFuoRwmsNN3ovwzPMDS", "fund_tx": "c7dd7447cbc8717d4574011bcf75c727425ac0585f2aac7f3a87365bbbfedc09", "redeem_tx": "31f691e284f9ea59eb76d691a9442f8dda4897c69772fa4b986f3e4eb1d63b90", "p2sh": "2MtDXWhoi4PwA66pCbusQpGTdGeN6UYTQEu", "redeemScript": "63a8202bf467e67c060bc84c7cc1a47791d8678d8eac11a9e80af993b767661131e7f38876a9147b1f15d1da93ba0414fe7ac36297a2bb7749a3ef6702001fb17576a9141f3e62e6daa403faea8bc8a30259f0888a1a7cb06888ac"}}
{"sell": {"fulfiller": "tmP3jJoyhBzAxeqpTYW9HrAt1XvJFg9gttc", "amount": "0.01", "redeemScript": "63a82044ddae444a583f19d0ebdd539cc7abb7e1aa4e54fac042486a5c1b935c4a0bf88876a9149243e230812261371e6743055ee8c6ea4181f80567024608b17576a9148e4144cee39078239a6dcd026ac65043a76254486888ac", "initiator": "tmNgXUNBTfH9TD4TwgEKK8K6Aytjph3QGtZ", "fund_tx": "9bb403ad881781818ea193f2c23eeafd02190b9aac8944b22650ffc6faff2736", "redeemblocknum": 2118, "currency": "zcash", "p2sh": "t2NPhNuYvwXVpiFA3QiZjgaeSDZwQVVKmUT"}, "buy": {"fulfiller": "mj12n62KmrWLCsjrpG1PN8iQnLy4J518s7", "amount": "1.12", "redeemScript": "63a82044ddae444a583f19d0ebdd539cc7abb7e1aa4e54fac042486a5c1b935c4a0bf88876a9141991d55a642946f5a8def2d69116619f659b89626702151fb17576a9142637c44bba94afbe236eb7587cc67e0682cd3d186888ac", "initiator": "mhr9tXrXmCXipK1ogWkhbESAmaYN1DfCQG", "fund_tx": "9c3bc5dc1aca5e722aa43f632fd7b130d9c5f972cad0f75236a210e80b1a466f", "redeemblocknum": 7957, "redeem_tx": "18ae4ff1a30111154ea40f265a47e5f18fc8b2cb785c9de80f71ad4b577d2f46", "currency": "bitcoin", "p2sh": "2N8Ru77EQ1qZtzBieZ17Q8wdEZA9SjiTJmj"}}

360
zXcatForEth.py Normal file
View File

@ -0,0 +1,360 @@
#!/usr/bin/env python3
# Based on spend-p2sh-txout.py from python-bitcoinlib.
# Copyright (C) 2017 The Zcash developers
# small modifications from zXcat.py to be convenient for ETH xcat
import sys
if sys.version_info.major < 3:
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
sys.exit(1)
import zcash
import zcash.rpc
from zcash import SelectParams
from zcash.core import b2x, lx, x, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from zcash.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
import bitcoin
from utils import *
# SelectParams('testnet')
SelectParams('regtest')
zcashd = zcash.rpc.Proxy()
FEE = 0.001*COIN
def get_keys(funder_address, redeemer_address):
fundpubkey = CBitcoinAddress(funder_address)
redeempubkey = CBitcoinAddress(redeemer_address)
# fundpubkey = zcashd.getnewaddress()
# redeempubkey = zcashd.getnewaddress()
return fundpubkey, redeempubkey
def privkey(address):
zcashd.dumpprivkey(address)
def hashtimelockcontract(funder, redeemer, hash_of_secret, lock_increment):
funderAddr = CBitcoinAddress(funder)
redeemerAddr = CBitcoinAddress(redeemer)
blocknum = zcashd.getblockcount()
print("Current blocknum", blocknum)
redeemblocknum = blocknum + lock_increment
print("REDEEMBLOCKNUM ZCASH", redeemblocknum)
zec_redeemScript = CScript([OP_IF, OP_SHA256, hash_of_secret, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
print("Redeem script for p2sh contract on Zcash blockchain:", b2x(zec_redeemScript))
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
p2sh = str(txin_p2sh_address)
# Returning all this to be saved locally in p2sh.json
return { 'redeemScript': b2x(zec_redeemScript), 'p2sh': p2sh}
def fund_htlc(p2sh, amount):
send_amount = float(amount)*COIN
fund_txid = zcashd.sendtoaddress(p2sh, send_amount)
txid = b2x(lx(b2x(fund_txid)))
return txid
def check_funds(p2sh):
zcashd.importaddress(p2sh, "", False) #Ariel: changed this to true
print("Imported address", p2sh)
# Get amount in address
amount = zcashd.getreceivedbyaddress(p2sh, 0)
print("Amount in address", amount)
amount = amount/COIN
return amount
def get_tx_details(txid):
fund_txinfo = zcashd.gettransaction(txid)
return fund_txinfo['details'][0]
def find_transaction_to_address(p2sh):
zcashd.importaddress(p2sh, "", False)
txs = zcashd.listunspent()
for tx in txs:
if tx['address'] == CBitcoinAddress(p2sh):
print("Found tx to p2sh", p2sh)
return tx
# 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]
def find_secret(p2sh,vinid):
zcashd.importaddress(p2sh, "", True)
# is this working?
txs = zcashd.listtransactions()
# print("==========================================LISTTT============", txs)
# print()
# print('LENNNNNNN:', len(txs))
# print('LENNNNNNN2:', len(txs))
for tx in txs:
# print("tx addr:", tx['address'], "tx id:", tx['txid'])
# print(type(tx['address']))
# print(type(p2sh))
# print('type::',type(tx['txid']))
raw = zcashd.gettransaction(lx(tx['txid']))['hex']
decoded = zcashd.decoderawtransaction(raw)
# print("fdsfdfds", decoded['vin'][0])
if('txid' in decoded['vin'][0]):
sendid = decoded['vin'][0]['txid']
# print("sendid:", sendid)
if (sendid == vinid ):
# print(type(tx['txid']))
# print(str.encode(tx['txid']))
return parse_secret(lx(tx['txid']))
print("Redeem transaction with secret not found")
return ""
def parse_secret(txid):
raw = zcashd.gettransaction(txid)['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
# 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(p2sh, 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))
# Parsing redeemblocknum from the redeemscript of the p2sh
redeemblocknum = find_redeemblocknum(contract)
blockcount = zcashd.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash chain:", 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()
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 >= redeemblocknum:
print("\nLocktime exceeded")
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])
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)
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)
def parse_script(script_hex):
redeemScript = zcashd.decodescript(script_hex)
scriptarray = redeemScript['asm'].split(' ')
return scriptarray
def find_redeemblocknum(redeemscript):
scriptarray = parse_script(redeemScript)
redeemblocknum = scriptarray[8]
return int(redeemblocknum)
def find_redeemAddr(redeemscript):
scriptarray = parse_script(redeemScript)
redeemer = scriptarray[6]
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
return redeemAddr
def find_refundAddr(redeemscript):
scriptarray = parse_script(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)
return addr
def generate(num):
blocks = zcashd.generate(num)
return blocks
# 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
# minamout - the minimal amount your're expecting
# assumes your Zcash client has the private key of the legit redeemer
def redeem_with_secret(redeemscript,secret,p2sh,minamount):
# How to find redeemScript and redeemblocknum from blockchain?
# print("Redeeming contract using secret", contract.__dict__)
#checking there are funds in the address
amount = check_funds(p2sh)
if(amount < minamount):
print("address ", p2sh, " not sufficiently funded")
return false
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))
redeemPubKey = find_redeemAddr(redeemscript)
print('redeemPubKey', redeemPubKey)
redeemScriptObject = CScript(x(redeemScript))
txin = CMutableTxIn(fundtx['outpoint'])
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
# Create the unsigned raw transaction.
tx = CMutableTransaction([txin], [txout])
sighash = SignatureHash(redeemScriptObject, 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, redeemScriptObject])
# exit()
# print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = redeemScriptObject.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))))
return b2x(lx(b2x(txid)))
else:
print("No contract for this p2sh found in database", p2sh)
# given a contract return true or false according to whether the relevant fund tx's timelock is still valid
def still_locked(contract):
p2sh = contract.p2sh
# Parsing redeemblocknum from the redeemscript of the p2sh
redeemblocknum = find_redeemblocknum(contract)
blockcount = zcashd.getblockcount()
return (int(blockcount) < int(redeemblocknum))
def redeem_after_timelock(redeemscript,redeempubkey,p2sh,fund_txid):
amount = fundtx['amount'] / COIN
if (fundtx['address'].__str__() != p2sh):
print("no fund transaction found to the contract p2sh address ",p2sh)
quit()
# print("Found fundtx:", fundtx)
# Parsing redeemblocknum from the redeemscript of the p2sh
redeemblocknum = find_redeemblocknum(redeemscript)
blockcount = zcashd.getblockcount()
print ("Current block:", blockcount, "Can redeem from block:", redeemblocknum)
if(still_locked(contract)):
print("too early for redeeming with timelock try again at block", redeemblocknum, "or later")
return
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
redeemPubKey = find_refundAddr(redeemscript)
print('refundPubKey', redeemPubKey)
redeemScriptObject = CScript(x(contract.redeemScript))
txin = CMutableTxIn(fundtx['outpoint'])
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
# Create the unsigned raw transaction.
txin.nSequence = 0
tx = CMutableTransaction([txin], [txout])
tx.nLockTime = redeemblocknum
sighash = SignatureHash(redeemScriptObject, tx, 0, SIGHASH_ALL)
# TODO: figure out how to better protect privkey
privkey = zcashd.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScriptObject])
# exit()
# print("txin.scriptSig", b2x(txin.scriptSig))
txin_scriptPubKey = redeemScriptObject.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))))
return b2x(lx(b2x(txid)))