Merge pull request #21 from zcash/refactor-rpc

Refactor rpc
This commit is contained in:
arcalinea 2017-08-25 15:58:27 -07:00 committed by GitHub
commit 8a070461cd
7 changed files with 429 additions and 411 deletions

View File

@ -15,210 +15,218 @@ from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
from xcat.utils import * from xcat.utils import *
from xcat.zcashRPC import parse_script
SelectParams('testnet')
# SelectParams('regtest')
# TODO: Accurately read user and pw info
bitcoind = bitcoin.rpc.Proxy(timeout=900)
FEE = 0.001*COIN FEE = 0.001*COIN
def validateaddress(addr): class bitcoinProxy():
return bitcoind.validateaddress(addr) def __init__(self, network='regtest', timeout=900):
self.network = network
self.timeout = timeout
def find_secret(p2sh, fundtx_input): SelectParams(self.network)
txs = bitcoind.call('listtransactions', "*", 20, 0, True) self.bitcoind = bitcoin.rpc.Proxy(timeout=self.timeout)
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): def validateaddress(self, addr):
raw = zcashd.gettransaction(txid, True)['hex'] return self.bitcoind.validateaddress(addr)
decoded = zcashd.call('decoderawtransaction', raw)
scriptSig = decoded['vin'][0]['scriptSig']
asm = scriptSig['asm'].split(" ")
pubkey = asm[1]
secret = x2s(asm[2])
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
return secret
def get_keys(funder_address, redeemer_address): def find_secret(self, p2sh, fundtx_input):
fundpubkey = CBitcoinAddress(funder_address) txs = self.bitcoind.call('listtransactions', "*", 20, 0, True)
redeempubkey = CBitcoinAddress(redeemer_address) for tx in txs:
# fundpubkey = bitcoind.getnewaddress() raw = self.bitcoind.gettransaction(lx(tx['txid']))['hex']
# redeempubkey = bitcoind.getnewaddress() decoded = self.bitcoind.decoderawtransaction(raw)
return fundpubkey, redeempubkey 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 privkey(address): def parse_secret(self, txid):
bitcoind.dumpprivkey(address) raw = zcashd.gettransaction(txid, True)['hex']
decoded = zcashd.call('decoderawtransaction', raw)
scriptSig = decoded['vin'][0]['scriptSig']
asm = scriptSig['asm'].split(" ")
pubkey = asm[1]
secret = x2s(asm[2])
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
return secret
def hashtimelockcontract(funder, redeemer, commitment, locktime): def get_keys(self, funder_address, redeemer_address):
funderAddr = CBitcoinAddress(funder) fundpubkey = CBitcoinAddress(funder_address)
redeemerAddr = CBitcoinAddress(redeemer) redeempubkey = CBitcoinAddress(redeemer_address)
if type(commitment) == str: # fundpubkey = self.bitcoind.getnewaddress()
commitment = x(commitment) # redeempubkey = self.bitcoind.getnewaddress()
# h = sha256(secret) return fundpubkey, redeempubkey
blocknum = bitcoind.getblockcount()
print("Current blocknum on Bitcoin: ", blocknum)
redeemblocknum = blocknum + locktime
print("Redeemblocknum on Bitcoin: ", redeemblocknum)
redeemScript = CScript([OP_IF, OP_SHA256, commitment, 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 Bitcoin blockchain: {0}".format(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)
# Import address at same time you create
bitcoind.importaddress(p2sh, "", False)
print("p2sh computed", p2sh)
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
def fund_htlc(p2sh, amount): def privkey(self, address):
send_amount = float(amount) * COIN self.bitcoind.dumpprivkey(address)
# Import address at same time that you fund it
bitcoind.importaddress(p2sh, "", False)
fund_txid = bitcoind.sendtoaddress(p2sh, send_amount)
txid = b2x(lx(b2x(fund_txid)))
return txid
# Following two functions are about the same def hashtimelockcontract(self, funder, redeemer, commitment, locktime):
def check_funds(p2sh): funderAddr = CBitcoinAddress(funder)
bitcoind.importaddress(p2sh, "", False) redeemerAddr = CBitcoinAddress(redeemer)
# Get amount in address if type(commitment) == str:
amount = bitcoind.getreceivedbyaddress(p2sh, 0) commitment = x(commitment)
amount = amount/COIN # h = sha256(secret)
return amount blocknum = self.bitcoind.getblockcount()
print("Current blocknum on Bitcoin: ", blocknum)
redeemblocknum = blocknum + locktime
print("Redeemblocknum on Bitcoin: ", redeemblocknum)
redeemScript = CScript([OP_IF, OP_SHA256, commitment, 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 Bitcoin blockchain: {0}".format(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)
# Import address at same time you create
self.bitcoind.importaddress(p2sh, "", False)
print("p2sh computed", p2sh)
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
def get_fund_status(p2sh): def fund_htlc(self, p2sh, amount):
bitcoind.importaddress(p2sh, "", False) send_amount = float(amount) * COIN
amount = bitcoind.getreceivedbyaddress(p2sh, 0) # Import address at same time that you fund it
amount = amount/COIN self.bitcoind.importaddress(p2sh, "", False)
print("Amount in bitcoin p2sh: ", amount, p2sh) fund_txid = self.bitcoind.sendtoaddress(p2sh, send_amount)
if amount > 0: txid = b2x(lx(b2x(fund_txid)))
return 'funded' return txid
else:
return 'empty'
## TODO: FIX search for p2sh in block # Following two functions are about the same
def search_p2sh(block, p2sh): def check_funds(self, p2sh):
print("Fetching block...") self.bitcoind.importaddress(p2sh, "", False)
blockdata = bitcoind.getblock(lx(block)) # Get amount in address
print("done fetching block") amount = self.bitcoind.getreceivedbyaddress(p2sh, 0)
txs = blockdata.vtx amount = amount/COIN
print("txs", txs) return amount
for tx in txs:
txhex = b2x(tx.serialize())
# Using my fork of python-zcashlib to get result of decoderawtransaction
txhex = txhex + '00'
rawtx = zcashd.decoderawtransaction(txhex)
# print('rawtx', rawtx)
print(rawtx)
for vout in rawtx['vout']:
if 'addresses' in vout['scriptPubKey']:
for addr in vout['scriptPubKey']['addresses']:
print("Sent to address:", addr)
if addr == p2sh:
print("Address to p2sh found in transaction!", addr)
print("Returning from search_p2sh")
def get_tx_details(txid): def get_fund_status(self, p2sh):
# must convert txid string to bytes x(txid) self.bitcoind.importaddress(p2sh, "", False)
fund_txinfo = bitcoind.gettransaction(lx(txid)) amount = self.bitcoind.getreceivedbyaddress(p2sh, 0)
return fund_txinfo['details'][0] amount = amount/COIN
print("Amount in bitcoin p2sh: ", amount, p2sh)
def redeem_contract(contract, secret): if amount > 0:
print("Parsing script for redeem_contract...") return 'funded'
scriptarray = parse_script(contract.redeemScript)
redeemblocknum = scriptarray[8]
redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
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 fund_tx: ", fundtx)
p2sh = P2SHBitcoinAddress(p2sh)
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
blockcount = bitcoind.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
if blockcount < int(redeemblocknum):
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: protect privkey better, separate signing from rawtx creation
privkey = bitcoind.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
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('Raw redeem transaction hex: ', b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Script verified, sending raw transaction...")
txid = bitcoind.sendrawtransaction(tx)
fund_tx = str(fundtx['outpoint'])
redeem_tx = b2x(lx(b2x(txid)))
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else: else:
print("nLocktime exceeded, refunding") return 'empty'
print('refundPubKey', refundPubKey)
txid = bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
fund_tx = str(fundtx['outpoint'])
refund_tx = b2x(lx(b2x(txid)))
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
else:
print("No contract for this p2sh found in database", p2sh)
def find_redeemblocknum(contract): ## TODO: FIX search for p2sh in block
scriptarray = parse_script(contract.redeemScript) def search_p2sh(self, block, p2sh):
redeemblocknum = scriptarray[8] print("Fetching block...")
return int(redeemblocknum) blockdata = self.bitcoind.getblock(lx(block))
print("done fetching block")
txs = blockdata.vtx
print("txs", txs)
for tx in txs:
txhex = b2x(tx.serialize())
# Using my fork of python-zcashlib to get result of decoderawtransaction
txhex = txhex + '00'
rawtx = zcashd.decoderawtransaction(txhex)
# print('rawtx', rawtx)
print(rawtx)
for vout in rawtx['vout']:
if 'addresses' in vout['scriptPubKey']:
for addr in vout['scriptPubKey']['addresses']:
print("Sent to address:", addr)
if addr == p2sh:
print("Address to p2sh found in transaction!", addr)
print("Returning from search_p2sh")
def find_redeemAddr(contract): def get_tx_details(self, txid):
scriptarray = parse_script(contract.redeemScript) # must convert txid string to bytes x(txid)
redeemer = scriptarray[6] fund_txinfo = self.bitcoind.gettransaction(lx(txid))
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer)) return fund_txinfo['details'][0]
return redeemAddr
def find_refundAddr(contract): def redeem_contract(self, contract, secret):
scriptarray = parse_script(contract.redeemScript) print("Parsing script for redeem_contract...")
funder = scriptarray[13] scriptarray = self.parse_script(contract.redeemScript)
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder)) redeemblocknum = scriptarray[8]
return refundAddr redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
p2sh = contract.p2sh
#checking there are funds in the address
amount = self.check_funds(p2sh)
if(amount == 0):
print("address ", p2sh, " not funded")
quit()
fundtx = self.find_transaction_to_address(p2sh)
amount = fundtx['amount'] / COIN
# print("Found fund_tx: ", fundtx)
p2sh = P2SHBitcoinAddress(p2sh)
if fundtx['address'] == p2sh:
print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
def find_transaction_to_address(p2sh): blockcount = self.bitcoind.getblockcount()
bitcoind.importaddress(p2sh, "", False) print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
txs = bitcoind.listunspent() if blockcount < int(redeemblocknum):
for tx in txs: print('redeemPubKey', redeemPubKey)
if tx['address'] == CBitcoinAddress(p2sh): zec_redeemScript = CScript(x(contract.redeemScript))
print("Found tx to p2sh", p2sh) txin = CMutableTxIn(fundtx['outpoint'])
return tx 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: protect privkey better, separate signing from rawtx creation
privkey = self.bitcoind.dumpprivkey(redeemPubKey)
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
preimage = secret.encode('utf-8')
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
def new_bitcoin_addr(): # print("txin.scriptSig", b2x(txin.scriptSig))
addr = bitcoind.getnewaddress() txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
return str(addr) print('Raw redeem transaction hex: ', b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Script verified, sending raw transaction...")
txid = self.bitcoind.sendrawtransaction(tx)
fund_tx = str(fundtx['outpoint'])
redeem_tx = b2x(lx(b2x(txid)))
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else:
print("nLocktime exceeded, refunding")
print('refundPubKey', refundPubKey)
txid = self.bitcoind.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
fund_tx = str(fundtx['outpoint'])
refund_tx = b2x(lx(b2x(txid)))
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
else:
print("No contract for this p2sh found in database", p2sh)
def generate(num): def parse_script(self, script_hex):
blocks = bitcoind.generate(num) redeemScript = self.bitcoind.call('decodescript', script_hex)
return blocks scriptarray = redeemScript['asm'].split(' ')
return scriptarray
def find_redeemblocknum(self, contract):
scriptarray = parse_script(contract.redeemScript)
redeemblocknum = scriptarray[8]
return int(redeemblocknum)
def find_redeemAddr(self, contract):
scriptarray = parse_script(contract.redeemScript)
redeemer = scriptarray[6]
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
return redeemAddr
def find_refundAddr(self, contract):
scriptarray = parse_script(contract.redeemScript)
funder = scriptarray[13]
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
return refundAddr
def find_transaction_to_address(self, p2sh):
self.bitcoind.importaddress(p2sh, "", False)
txs = self.bitcoind.listunspent()
for tx in txs:
if tx['address'] == CBitcoinAddress(p2sh):
print("Found tx to p2sh", p2sh)
return tx
def new_bitcoin_addr(self):
addr = self.bitcoind.getnewaddress()
return str(addr)
def generate(self, num):
blocks = self.bitcoind.generate(num)
return blocks

View File

@ -24,7 +24,12 @@ def checkSellStatus(tradeid):
secret = db.get_secret(tradeid) secret = db.get_secret(tradeid)
print("Retrieved secret to redeem funds for {0}: {1}".format(tradeid, secret)) print("Retrieved secret to redeem funds for {0}: {1}".format(tradeid, secret))
txs = seller_redeem_p2sh(trade, secret) txs = seller_redeem_p2sh(trade, secret)
trade.buy.redeem_tx = txs['redeem_tx'] if 'redeem_tx' in txs:
trade.buy.redeem_tx = txs['redeem_tx']
print("Redeem tx: ", txs['redeem_tx'])
elif 'refund_tx' in txs:
trade.buy.redeem_tx = txs['refund_tx']
print("Refund tx: ", txs['refund_tx'])
save_state(trade, tradeid) save_state(trade, tradeid)
# Remove from db? Or just from temporary file storage # Remove from db? Or just from temporary file storage
cleanup(tradeid) cleanup(tradeid)
@ -87,8 +92,12 @@ def checkBuyStatus(tradeid):
if secret != None: if secret != None:
print("Found secret on blockchain in seller's redeem tx: ", secret) print("Found secret on blockchain in seller's redeem tx: ", secret)
txs = redeem_p2sh(trade.sell, secret) txs = redeem_p2sh(trade.sell, secret)
trade.sell.redeem_tx = txs['redeem_tx'] if 'redeem_tx' in txs:
print("Redeem txid: ", trade.sell.redeem_tx) trade.sell.redeem_tx = txs['redeem_tx']
print("Redeem txid: ", trade.sell.redeem_tx)
elif 'refund_tx' in txs:
trade.sell.redeem_tx = txs['refund_tx']
print("Refund tx: ", txs['refund_tx'])
save_state(trade, tradeid) save_state(trade, tradeid)
print("XCAT trade complete!") print("XCAT trade complete!")
else: else:
@ -134,6 +143,15 @@ def findtrade(tradeid):
print(trade.toJSON()) print(trade.toJSON())
return trade return trade
def find_role(contract):
# When regtest created both addrs on same machine, role is both.
if is_myaddr(contract.initiator) and is_myaddr(contract.fulfiller):
return 'test'
elif is_myaddr(contract.initiator):
return 'initiator'
else:
return 'fulfiller'
def checktrade(tradeid): def checktrade(tradeid):
print("In checktrade") print("In checktrade")
trade = db.get(tradeid) trade = db.get(tradeid)
@ -183,7 +201,7 @@ def main():
parser.add_argument("arguments", action="store", nargs="*", help="add arguments") parser.add_argument("arguments", action="store", nargs="*", help="add arguments")
parser.add_argument("-w", "--wormhole", action="store_true", help="Transfer trade data through magic-wormhole") parser.add_argument("-w", "--wormhole", action="store_true", help="Transfer trade data through magic-wormhole")
parser.add_argument("-c", "--conf", action="store", help="Use default trade data in conf file.") parser.add_argument("-c", "--conf", action="store", help="Use default trade data in conf file.")
parser.add_argument("-n", "--network", action="store", help="Set network to regtest or mainnet. Defaults to testnet while in beta.") parser.add_argument("-n", "--network", action="store", help="Set network to regtest or mainnet. Defaults to testnet while in alpha.")
# parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process") # parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process")
args = parser.parse_args() args = parser.parse_args()

View File

@ -6,8 +6,6 @@ import json
import ast import ast
from xcat.trades import * from xcat.trades import *
import xcat.bitcoinRPC as bitcoinRPC
db = plyvel.DB('/tmp/xcatDB', create_if_missing=True) db = plyvel.DB('/tmp/xcatDB', create_if_missing=True)
preimageDB = plyvel.DB('/tmp/preimageDB', create_if_missing=True) preimageDB = plyvel.DB('/tmp/preimageDB', create_if_missing=True)

View File

@ -1,13 +1,25 @@
import json import json
import os, sys import os, sys
from pprint import pprint from pprint import pprint
import xcat.zcashRPC as zcashRPC
import xcat.bitcoinRPC as bitcoinRPC
from xcat.utils import * from xcat.utils import *
from xcat.trades import Contract, Trade from xcat.trades import Contract, Trade
import xcat.userInput as userInput import xcat.userInput as userInput
import xcat.db as db import xcat.db as db
from xcat.xcatconf import * from xcat.xcatconf import *
from xcat.bitcoinRPC import bitcoinProxy
from xcat.zcashRPC import zcashProxy
bitcoinRPC = bitcoinProxy()
zcashRPC = zcashProxy()
def is_myaddr(address):
if address[:1] == 'm':
status = bitcoinRPC.validateaddress(address)
else:
status = zcashRPC.validateaddress(address)
status = status['ismine']
# print("Address {0} is mine: {1}".format(address, status))
return status
def find_secret_from_fundtx(currency, p2sh, fundtx): def find_secret_from_fundtx(currency, p2sh, fundtx):
if currency == 'bitcoin': if currency == 'bitcoin':

View File

@ -1,5 +1,7 @@
from xcat.utils import * from xcat.utils import *
from xcat.db import * from xcat.db import *
from xcat.bitcoinRPC import bitcoinProxy
from xcat.zcashRPC import zcashProxy
def enter_trade_id(): def enter_trade_id():
tradeid = input("Enter a unique identifier for this trade: ") tradeid = input("Enter a unique identifier for this trade: ")
@ -34,6 +36,8 @@ def authorize_fund_sell(htlcTrade):
response = input("Type 'enter' to allow this program to send funds on your behalf.") response = input("Type 'enter' to allow this program to send funds on your behalf.")
def get_initiator_addresses(): def get_initiator_addresses():
bitcoinRPC = bitcoinProxy()
zcashRPC = zcashProxy()
btc_addr = input("Enter your bitcoin address or press enter to generate one: ") btc_addr = input("Enter your bitcoin address or press enter to generate one: ")
btc_addr = bitcoinRPC.new_bitcoin_addr() btc_addr = bitcoinRPC.new_bitcoin_addr()
print(btc_addr) print(btc_addr)

View File

@ -1,7 +1,5 @@
import hashlib, json, random, binascii import hashlib, json, random, binascii
import xcat.trades as trades import xcat.trades as trades
import xcat.bitcoinRPC as bitcoinRPC
import xcat.zcashRPC as zcashRPC
import os import os
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
@ -41,27 +39,6 @@ def jsonformat(trade):
'buy': trade.buyContract.__dict__ 'buy': trade.buyContract.__dict__
} }
############################################
#### Role detection utils ####
############################################
def find_role(contract):
# Obviously when regtest created both addrs on same machine, role is both.
if is_myaddr(contract.initiator) and is_myaddr(contract.fulfiller):
return 'test'
elif is_myaddr(contract.initiator):
return 'initiator'
else:
return 'fulfiller'
def is_myaddr(address):
if address[:1] == 'm':
status = bitcoinRPC.validateaddress(address)
else:
status = zcashRPC.validateaddress(address)
status = status['ismine']
# print("Address {0} is mine: {1}".format(address, status))
return status
############################################ ############################################
########### Preimage utils ################# ########### Preimage utils #################
############################################ ############################################

View File

@ -16,215 +16,216 @@ 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.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
from zcash.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress from zcash.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
from xcat.utils import * from xcat.utils import x2s
SelectParams('testnet')
# SelectParams('regtest')
zcashd = zcash.rpc.Proxy(timeout=900)
FEE = 0.001*COIN FEE = 0.001*COIN
def x2s(hexstring): class zcashProxy():
"""Convert hex to a utf-8 string""" def __init__(self, network='regtest', timeout=900):
return binascii.unhexlify(hexstring).decode('utf-8') self.network = network
self.timeout = timeout
def validateaddress(addr): SelectParams(self.network)
return zcashd.validateaddress(addr) self.zcashd = zcash.rpc.Proxy(timeout=self.timeout)
def get_keys(funder_address, redeemer_address): def validateaddress(self, addr):
fundpubkey = CBitcoinAddress(funder_address) return self.zcashd.validateaddress(addr)
redeempubkey = CBitcoinAddress(redeemer_address)
return fundpubkey, redeempubkey
def privkey(address): def get_keys(self, funder_address, redeemer_address):
zcashd.dumpprivkey(address) fundpubkey = CBitcoinAddress(funder_address)
redeempubkey = CBitcoinAddress(redeemer_address)
return fundpubkey, redeempubkey
def hashtimelockcontract(funder, redeemer, commitment, locktime): def privkey(self, address):
funderAddr = CBitcoinAddress(funder) self.zcashd.dumpprivkey(address)
redeemerAddr = CBitcoinAddress(redeemer)
if type(commitment) == str:
commitment = x(commitment)
# h = sha256(secret)
blocknum = zcashd.getblockcount()
print("Current blocknum on Zcash: ", blocknum)
redeemblocknum = blocknum + locktime
print("Redeemblocknum on Zcash: ", redeemblocknum)
# can rm op_dup and op_hash160 if you replace addrs with pubkeys (as raw hex/bin data?), and can rm last op_equalverify (for direct pubkey comparison)
zec_redeemScript = CScript([OP_IF, OP_SHA256, commitment, 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)
print("p2sh computed: ", p2sh)
# Import address as soon as you create it
zcashd.importaddress(p2sh, "", False)
# Returning all this to be saved locally in p2sh.json
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(zec_redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
def fund_htlc(p2sh, amount): def hashtimelockcontract(self, funder, redeemer, commitment, locktime):
send_amount = float(amount)*COIN funderAddr = CBitcoinAddress(funder)
# Import addr at same time as you fund redeemerAddr = CBitcoinAddress(redeemer)
zcashd.importaddress(p2sh, "", False) if type(commitment) == str:
fund_txid = zcashd.sendtoaddress(p2sh, send_amount) commitment = x(commitment)
txid = b2x(lx(b2x(fund_txid))) # h = sha256(secret)
return txid blocknum = self.zcashd.getblockcount()
print("Current blocknum on Zcash: ", blocknum)
redeemblocknum = blocknum + locktime
print("Redeemblocknum on Zcash: ", redeemblocknum)
# can rm op_dup and op_hash160 if you replace addrs with pubkeys (as raw hex/bin data?), and can rm last op_equalverify (for direct pubkey comparison)
zec_redeemScript = CScript([OP_IF, OP_SHA256, commitment, 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)
print("p2sh computed: ", p2sh)
# Import address as soon as you create it
self.zcashd.importaddress(p2sh, "", False)
# Returning all this to be saved locally in p2sh.json
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(zec_redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
# Following two functions are about the same def fund_htlc(self, p2sh, amount):
def check_funds(p2sh): send_amount = float(amount)*COIN
zcashd.importaddress(p2sh, "", False) # Import addr at same time as you fund
# Get amount in address self.zcashd.importaddress(p2sh, "", False)
amount = zcashd.getreceivedbyaddress(p2sh, 0) fund_txid = self.zcashd.sendtoaddress(p2sh, send_amount)
amount = amount/COIN txid = b2x(lx(b2x(fund_txid)))
return amount return txid
def get_fund_status(p2sh): # Following two functions are about the same
zcashd.importaddress(p2sh, "", False) def check_funds(self, p2sh):
amount = zcashd.getreceivedbyaddress(p2sh, 0) self.zcashd.importaddress(p2sh, "", False)
amount = amount/COIN # Get amount in address
print("Amount in zcash p2sh: ", amount, p2sh) amount = self.zcashd.getreceivedbyaddress(p2sh, 0)
if amount > 0: amount = amount/COIN
return 'funded' return amount
else:
return 'empty'
def get_tx_details(txid): def get_fund_status(self, p2sh):
fund_txinfo = zcashd.gettransaction(txid) self.zcashd.importaddress(p2sh, "", False)
return fund_txinfo['details'][0] amount = self.zcashd.getreceivedbyaddress(p2sh, 0)
amount = amount/COIN
def find_transaction_to_address(p2sh): print("Amount in zcash p2sh: ", amount, p2sh)
zcashd.importaddress(p2sh, "", False) if amount > 0:
txs = zcashd.listunspent(0, 100) return 'funded'
for tx in txs:
if tx['address'] == CBitcoinAddress(p2sh):
print("Found tx to p2sh", p2sh)
return tx
def find_secret(p2sh, fundtx_input):
txs = zcashd.call('listtransactions', "*", 20, 0, True)
for tx in txs:
raw = zcashd.gettransaction(lx(tx['txid']))['hex']
decoded = zcashd.decoderawtransaction(raw)
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(txid, True)['hex']
decoded = zcashd.decoderawtransaction(raw)
scriptSig = decoded['vin'][0]['scriptSig']
asm = scriptSig['asm'].split(" ")
pubkey = asm[1]
secret = x2s(asm[2])
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
return secret
def redeem_contract(contract, secret):
# How to find redeemScript and redeemblocknum from blockchain?
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 fund_tx: ", fundtx)
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 < contract.redeemblocknum:
# TODO: parse the script once, up front.
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])
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Script verified, sending raw redeem transaction...")
txid = zcashd.sendrawtransaction(tx)
redeem_tx = b2x(lx(b2x(txid)))
fund_tx = str(fundtx['outpoint'])
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else: else:
print("nLocktime exceeded, refunding") return 'empty'
refundPubKey = find_refundAddr(contract)
print('refundPubKey', refundPubKey)
txid = zcashd.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
refund_tx = b2x(lx(b2x(txid)))
fund_tx = str(fundtx['outpoint'])
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
else:
print("No contract for this p2sh found in database", p2sh)
def parse_script(script_hex): def get_tx_details(self, txid):
redeemScript = zcashd.decodescript(script_hex) fund_txinfo = self.zcashd.gettransaction(txid)
scriptarray = redeemScript['asm'].split(' ') return fund_txinfo['details'][0]
return scriptarray
def find_redeemblocknum(contract): def find_transaction_to_address(self, p2sh):
print("In find_redeemblocknum") self.zcashd.importaddress(p2sh, "", False)
scriptarray = parse_script(contract.redeemScript) txs = self.zcashd.listunspent(0, 100)
print("Returning scriptarray", scriptarray) for tx in txs:
redeemblocknum = scriptarray[8] if tx['address'] == CBitcoinAddress(p2sh):
return int(redeemblocknum) print("Found tx to p2sh", p2sh)
return tx
def find_redeemAddr(contract): def find_secret(self, p2sh, fundtx_input):
scriptarray = parse_script(contract.redeemScript) txs = self.zcashd.call('listtransactions', "*", 20, 0, True)
redeemer = scriptarray[6] for tx in txs:
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer)) raw = self.zcashd.gettransaction(lx(tx['txid']))['hex']
return redeemAddr decoded = self.zcashd.decoderawtransaction(raw)
if('txid' in decoded['vin'][0]):
sendid = decoded['vin'][0]['txid']
if (sendid == fundtx_input ):
print("Found funding tx: ", sendid)
return self.parse_secret(lx(tx['txid']))
print("Redeem transaction with secret not found")
return
def find_refundAddr(contract): def parse_secret(self, txid):
scriptarray = parse_script(contract.redeemScript) raw = self.zcashd.gettransaction(txid, True)['hex']
funder = scriptarray[13] decoded = self.zcashd.decoderawtransaction(raw)
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder)) scriptSig = decoded['vin'][0]['scriptSig']
return refundAddr asm = scriptSig['asm'].split(" ")
pubkey = asm[1]
secret = x2s(asm[2])
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
return secret
def find_recipient(contract): def redeem_contract(self, contract, secret):
# make this dependent on actual fund tx to p2sh, not contract # How to find redeemScript and redeemblocknum from blockchain?
txid = contract.fund_tx p2sh = contract.p2sh
raw = zcashd.gettransaction(lx(txid), True)['hex'] #checking there are funds in the address
decoded = zcashd.decoderawtransaction(raw) amount = self.check_funds(p2sh)
scriptSig = decoded['vin'][0]['scriptSig'] if(amount == 0):
print("Decoded", scriptSig) print("Address ", p2sh, " not funded")
asm = scriptSig['asm'].split(" ") quit()
pubkey = asm[1] fundtx = self.find_transaction_to_address(p2sh)
initiator = CBitcoinAddress(contract.initiator) amount = fundtx['amount'] / COIN
fulfiller = CBitcoinAddress(contract.fulfiller) # print("Found fund_tx: ", fundtx)
print("Initiator", b2x(initiator)) p2sh = P2SHBitcoinAddress(p2sh)
print("Fulfiller", b2x(fulfiller)) if fundtx['address'] == p2sh:
print('pubkey', pubkey) print("Found {0} in p2sh {1}, redeeming...".format(amount, p2sh))
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
print('redeemPubkey', redeemPubkey)
def new_zcash_addr(): # Where can you find redeemblocknum in the transaction?
addr = zcashd.getnewaddress() # redeemblocknum = find_redeemblocknum(contract)
return str(addr) blockcount = self.zcashd.getblockcount()
print("\nCurrent blocknum at time of redeem on Zcash:", blockcount)
if blockcount < contract.redeemblocknum:
# TODO: parse the script once, up front.
redeemPubKey = self.find_redeemAddr(contract)
def generate(num): print('redeemPubKey', redeemPubKey)
blocks = zcashd.generate(num) zec_redeemScript = CScript(x(contract.redeemScript))
return blocks
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 = self.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])
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
print("Script verified, sending raw redeem transaction...")
txid = self.zcashd.sendrawtransaction(tx)
redeem_tx = b2x(lx(b2x(txid)))
fund_tx = str(fundtx['outpoint'])
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
else:
print("nLocktime exceeded, refunding")
refundPubKey = self.find_refundAddr(contract)
print('refundPubKey', refundPubKey)
txid = self.zcashd.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
refund_tx = b2x(lx(b2x(txid)))
fund_tx = str(fundtx['outpoint'])
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
else:
print("No contract for this p2sh found in database", p2sh)
def parse_script(self, script_hex):
redeemScript = self.zcashd.decodescript(script_hex)
scriptarray = redeemScript['asm'].split(' ')
return scriptarray
def find_redeemblocknum(self, contract):
print("In find_redeemblocknum")
scriptarray = self.parse_script(contract.redeemScript)
print("Returning scriptarray", scriptarray)
redeemblocknum = scriptarray[8]
return int(redeemblocknum)
def find_redeemAddr(self, contract):
scriptarray = self.parse_script(contract.redeemScript)
redeemer = scriptarray[6]
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
return redeemAddr
def find_refundAddr(self, contract):
scriptarray = self.parse_script(contract.redeemScript)
funder = scriptarray[13]
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
return refundAddr
def find_recipient(self, contract):
# make this dependent on actual fund tx to p2sh, not contract
txid = contract.fund_tx
raw = self.zcashd.gettransaction(lx(txid), True)['hex']
decoded = self.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("Fulfiller", b2x(fulfiller))
print('pubkey', pubkey)
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
print('redeemPubkey', redeemPubkey)
def new_zcash_addr(self):
addr = self.zcashd.getnewaddress()
return str(addr)
def generate(self, num):
blocks = self.zcashd.generate(num)
return blocks