Merge branch 'master' of https://github.com/zcash-hackworks/zbxcat into better-tests
This commit is contained in:
commit
7ccc8084b2
|
@ -10,16 +10,20 @@ import bitcoin.rpc
|
|||
from bitcoin import SelectParams
|
||||
from bitcoin.core import b2x, lx, b2lx, x, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160, CTransaction
|
||||
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.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, OP_FALSE
|
||||
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
||||
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
|
||||
|
||||
from xcat.utils import *
|
||||
import logging
|
||||
|
||||
FEE = 0.001*COIN
|
||||
|
||||
class bitcoinProxy():
|
||||
def __init__(self, network='regtest', timeout=900):
|
||||
if network is not 'testnet' and network is not 'mainnet':
|
||||
network='regtest'
|
||||
logging.debug("NETWORK in proxy: {0}".format(network))
|
||||
self.network = network
|
||||
self.timeout = timeout
|
||||
|
||||
|
@ -143,7 +147,7 @@ class bitcoinProxy():
|
|||
print("Parsing script for redeem_contract...")
|
||||
scriptarray = self.parse_script(contract.redeemScript)
|
||||
redeemblocknum = scriptarray[8]
|
||||
redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
|
||||
self.redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
|
||||
refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
|
||||
p2sh = contract.p2sh
|
||||
#checking there are funds in the address
|
||||
|
@ -161,38 +165,64 @@ class bitcoinProxy():
|
|||
blockcount = self.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 = 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])
|
||||
|
||||
# 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 = self.bitcoind.sendrawtransaction(tx)
|
||||
fund_tx = str(fundtx['outpoint'])
|
||||
redeem_tx = b2x(lx(b2x(txid)))
|
||||
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
|
||||
return self.redeem(contract, fundtx, secret)
|
||||
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}
|
||||
return self.refund(contract)
|
||||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def redeem(self, contract, fundtx, secret):
|
||||
print('redeemPubKey', self.redeemPubKey)
|
||||
# TODO: Compare with script on blockchain?
|
||||
redeemScript = CScript(x(contract.redeemScript))
|
||||
txin = CMutableTxIn(fundtx['outpoint'])
|
||||
txout = CMutableTxOut(fundtx['amount'] - FEE, self.redeemPubKey.to_scriptPubKey())
|
||||
# Create the unsigned raw transaction.
|
||||
tx = CMutableTransaction([txin], [txout])
|
||||
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||
# TODO: protect privkey better, separate signing from rawtx creation
|
||||
privkey = self.bitcoind.dumpprivkey(self.redeemPubKey)
|
||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||
preimage = secret.encode('utf-8')
|
||||
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript])
|
||||
|
||||
# print("txin.scriptSig", b2x(txin.scriptSig))
|
||||
txin_scriptPubKey = 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 = self.bitcoind.sendrawtransaction(tx)
|
||||
fund_tx = str(fundtx['outpoint'])
|
||||
redeem_tx = b2x(lx(b2x(txid)))
|
||||
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
|
||||
|
||||
def refund(self, contract):
|
||||
fundtx = self.find_transaction_to_address(contract.p2sh)
|
||||
print("Fund tx found in refund: ", fundtx)
|
||||
refundPubKey = self.find_refundAddr(contract)
|
||||
print('refundPubKey: {0}'.format(refundPubKey))
|
||||
|
||||
redeemScript = CScript(x(contract.redeemScript))
|
||||
txin = CMutableTxIn(fundtx['outpoint'])
|
||||
txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey())
|
||||
# Create the unsigned raw transaction.
|
||||
tx = CMutableTransaction([txin], [txout])
|
||||
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||
privkey = self.bitcoind.dumpprivkey(refundPubKey)
|
||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||
# Sign without secret
|
||||
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
||||
# txin.nSequence = 2185
|
||||
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||
print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize())))
|
||||
res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||
print("Script verified, sending raw transaction... (NOT)", res)
|
||||
txid = self.bitcoind.sendrawtransaction(tx)
|
||||
refund_tx = b2x(lx(b2x(txid)))
|
||||
fund_tx = str(fundtx['outpoint'])
|
||||
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
|
||||
|
||||
def parse_script(self, script_hex):
|
||||
redeemScript = self.bitcoind.call('decodescript', script_hex)
|
||||
scriptarray = redeemScript['asm'].split(' ')
|
||||
|
@ -210,7 +240,7 @@ class bitcoinProxy():
|
|||
return redeemAddr
|
||||
|
||||
def find_refundAddr(self, contract):
|
||||
scriptarray = parse_script(contract.redeemScript)
|
||||
scriptarray = self.parse_script(contract.redeemScript)
|
||||
funder = scriptarray[13]
|
||||
refundAddr = P2PKHBitcoinAddress.from_bytes(x(funder))
|
||||
return refundAddr
|
||||
|
@ -220,7 +250,7 @@ class bitcoinProxy():
|
|||
txs = self.bitcoind.listunspent()
|
||||
for tx in txs:
|
||||
if tx['address'] == CBitcoinAddress(p2sh):
|
||||
print("Found tx to p2sh", p2sh)
|
||||
logging.debug("Found tx to p2sh: {0}".format(p2sh))
|
||||
return tx
|
||||
|
||||
def new_bitcoin_addr(self):
|
||||
|
|
33
xcat/cli.py
33
xcat/cli.py
|
@ -32,9 +32,11 @@ def checkSellStatus(tradeid):
|
|||
if 'redeem_tx' in txs:
|
||||
trade.buy.redeem_tx = txs['redeem_tx']
|
||||
print("Redeem tx: ", txs['redeem_tx'])
|
||||
elif 'refund_tx' in txs:
|
||||
if 'refund_tx' in txs:
|
||||
trade.buy.redeem_tx = txs['refund_tx']
|
||||
print("Refund tx: ", txs['refund_tx'])
|
||||
print("Buyer refund tx: ", txs['refund_tx'])
|
||||
txs = refund_contract(trade.sell) # Refund to seller
|
||||
print("Your refund txid: ", txs['refund_tx'])
|
||||
save_state(trade, tradeid)
|
||||
# Remove from db? Or just from temporary file storage
|
||||
utils.cleanup(tradeid)
|
||||
|
@ -123,6 +125,7 @@ def checkBuyStatus(tradeid):
|
|||
save_state(trade, tradeid)
|
||||
print("XCAT trade complete!")
|
||||
else:
|
||||
# Search if tx has been refunded from p2sh
|
||||
print("Secret not found in redeemtx")
|
||||
|
||||
|
||||
|
@ -211,9 +214,12 @@ def newtrade(tradeid, **kwargs):
|
|||
protocol = Protocol()
|
||||
print("Creating new XCAT trade...")
|
||||
utils.erase_trade()
|
||||
tradeid, trade = protocol.initialize_trade(tradeid, conf=kwargs['conf'])
|
||||
print("Trade", trade)
|
||||
trade = protocol.seller_init(tradeid, trade)
|
||||
tradeid, trade = protocol.initialize_trade(
|
||||
tradeid,
|
||||
conf=kwargs['conf'],
|
||||
network=kwargs['network'])
|
||||
print("New trade created: {0}".format(trade))
|
||||
trade = protocol.seller_init(tradeid, trade, network=kwargs['network'])
|
||||
print("\nUse 'xcat exporttrade [tradeid]' to export the trade and sent "
|
||||
"to the buyer.\n")
|
||||
save_state(trade, tradeid)
|
||||
|
@ -260,6 +266,17 @@ def main():
|
|||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.debug:
|
||||
numeric_level = getattr(logging, 'DEBUG', None)
|
||||
logging.basicConfig(format='%(levelname)s: %(message)s', level=numeric_level)
|
||||
else:
|
||||
logging.basicConfig(format='%(levelname)s: %(message)s', level='INFO')
|
||||
|
||||
if args.network:
|
||||
NETWORK = args.network
|
||||
else:
|
||||
NETWORK = 'testnet'
|
||||
|
||||
command = args.command
|
||||
if command == 'importtrade':
|
||||
if args.wormhole:
|
||||
|
@ -294,9 +311,9 @@ def main():
|
|||
utils.throw("Usage: newtrade [tradeid]")
|
||||
tradeid = args.arguments[0]
|
||||
if args.conf is None:
|
||||
newtrade(tradeid, network=args.network, conf='cli')
|
||||
newtrade(tradeid, network=NETWORK, conf='cli')
|
||||
else:
|
||||
newtrade(tradeid, network=args.network, conf=args.conf)
|
||||
newtrade(tradeid, network=NETWORK, conf=args.conf)
|
||||
elif command == "daemon":
|
||||
# TODO: not implemented
|
||||
print("Run as daemon process")
|
||||
|
@ -308,8 +325,10 @@ def main():
|
|||
tradeid = args.arguments[0]
|
||||
checkBuyStatus(tradeid)
|
||||
elif command == "step3":
|
||||
protocol.generate(31)
|
||||
tradeid = args.arguments[0]
|
||||
checkSellStatus(tradeid)
|
||||
elif command == "step4":
|
||||
# generate(1)
|
||||
tradeid = args.arguments[0]
|
||||
checkBuyStatus(tradeid)
|
||||
|
|
|
@ -5,6 +5,7 @@ from xcat.xcatconf import ADDRS
|
|||
from xcat.trades import Contract, Trade
|
||||
from xcat.bitcoinRPC import bitcoinProxy
|
||||
from xcat.zcashRPC import zcashProxy
|
||||
import logging
|
||||
|
||||
|
||||
class Protocol():
|
||||
|
@ -13,20 +14,33 @@ class Protocol():
|
|||
self.bitcoinRPC = bitcoinProxy()
|
||||
self.zcashRPC = zcashProxy()
|
||||
|
||||
def generate(self, num):
|
||||
self.bitcoinRPC.generate(num)
|
||||
self.zcashRPC.generate(num)
|
||||
|
||||
def is_myaddr(self, address):
|
||||
# Handle differnt network prefixes
|
||||
if address[:1] == 'm':
|
||||
status = self.bitcoinRPC.validateaddress(address)
|
||||
else:
|
||||
status = self.zcashRPC.validateaddress(address)
|
||||
status = status['ismine']
|
||||
|
||||
logging.debug("Address status: ", status)
|
||||
|
||||
if not status['isvalid']:
|
||||
raise ValueError("Invalid address: %s" % address)
|
||||
elif 'ismine' in status:
|
||||
status = status['ismine']
|
||||
# print("Address {0} is mine: {1}".format(address, status))
|
||||
return status
|
||||
|
||||
def find_secret_from_fundtx(self, currency, p2sh, fundtx):
|
||||
if currency == 'bitcoin':
|
||||
secret = self.bitcoinRPC.find_secret(p2sh, fundtx)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
secret = self.zcashRPC.find_secret(p2sh, fundtx)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s' % currency)
|
||||
return secret
|
||||
|
||||
def import_addrs(self, trade):
|
||||
|
@ -37,17 +51,21 @@ class Protocol():
|
|||
if currency == 'bitcoin':
|
||||
print("Checking funds in Bitcoin p2sh")
|
||||
return self.bitcoinRPC.check_funds(address)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
print("Checking funds in Zcash p2sh")
|
||||
return self.zcashRPC.check_funds(address)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s' % currency)
|
||||
|
||||
def check_fund_status(self, currency, address):
|
||||
if currency == 'bitcoin':
|
||||
print("Checking funds in Bitcoin p2sh")
|
||||
return self.bitcoinRPC.get_fund_status(address)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
print("Checking funds in Zcash p2sh")
|
||||
return self.zcashRPC.get_fund_status(address)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s' % currency)
|
||||
|
||||
# TODO: function to calculate appropriate locktimes between chains
|
||||
# def verify_p2sh(trade):
|
||||
|
@ -66,16 +84,20 @@ class Protocol():
|
|||
if currency == 'bitcoin':
|
||||
sell_p2sh = self.bitcoinRPC.hashtimelockcontract(
|
||||
funder, redeemer, commitment, locktime)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
sell_p2sh = self.zcashRPC.hashtimelockcontract(
|
||||
funder, redeemer, commitment, locktime)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s' % currency)
|
||||
return sell_p2sh
|
||||
|
||||
def fund_htlc(self, currency, p2sh, amount):
|
||||
if currency == 'bitcoin':
|
||||
txid = self.bitcoinRPC.fund_htlc(p2sh, amount)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
txid = self.zcashRPC.fund_htlc(p2sh, amount)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s' % currency)
|
||||
return txid
|
||||
|
||||
def fund_contract(self, contract):
|
||||
|
@ -124,15 +146,29 @@ class Protocol():
|
|||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = self.bitcoinRPC.redeem_contract(contract, secret)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
res = self.zcashRPC.redeem_contract(contract, secret)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s' % currency)
|
||||
return res
|
||||
|
||||
def parse_secret(self, chain, txid):
|
||||
if chain == 'bitcoin':
|
||||
secret = self.bitcoinRPC.parse_secret(txid)
|
||||
def refund_contract(self, contract):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = self.bitcoinRPC.refund(contract)
|
||||
elif currency == 'zcash':
|
||||
res = self.zcashRPC.refund(contract)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s', currency)
|
||||
return res
|
||||
|
||||
def parse_secret(self, currency, txid):
|
||||
if currency == 'bitcoin':
|
||||
secret = self.bitcoinRPC.parse_secret(txid)
|
||||
elif currency == 'zcash':
|
||||
secret = self.zcashRPC.parse_secret(txid)
|
||||
else:
|
||||
raise ValueError('Currency not recognized: %s', currency)
|
||||
return secret
|
||||
|
||||
# Main functions determining user flow from command line
|
||||
|
@ -185,7 +221,7 @@ class Protocol():
|
|||
buy_p2sh_balance,
|
||||
buy.currency)
|
||||
print("Buy amt:", buy.amount)
|
||||
txid = fund_buy_contract(trade)
|
||||
txid = self.fund_buy_contract(trade)
|
||||
print("Fund tx txid:", txid)
|
||||
else:
|
||||
print("It looks like you've already funded the contract to buy "
|
||||
|
@ -193,7 +229,7 @@ class Protocol():
|
|||
"{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')
|
||||
self.print_trade('buyer')
|
||||
|
||||
def initialize_trade(self, tradeid, **kwargs):
|
||||
trade = Trade()
|
||||
|
@ -228,7 +264,7 @@ class Protocol():
|
|||
secret = utils.generate_password()
|
||||
db.save_secret(tradeid, secret)
|
||||
print("\nGenerated a secret preimage to lock funds. This will only "
|
||||
"be stored locally: ", secret)
|
||||
"be stored locally: {0}".format(secret))
|
||||
|
||||
hash_of_secret = utils.sha256(secret)
|
||||
# TODO: Implement locktimes and mock block passage of time
|
||||
|
@ -241,5 +277,5 @@ class Protocol():
|
|||
self.create_buy_p2sh(trade, hash_of_secret, buy_locktime)
|
||||
|
||||
trade.commitment = utils.b2x(hash_of_secret)
|
||||
print("TRADE after seller init", trade.toJSON())
|
||||
print("TRADE after seller init {0}".format(trade.toJSON()))
|
||||
return trade
|
||||
|
|
|
@ -2,6 +2,7 @@ from xcat.utils import *
|
|||
from xcat.db import *
|
||||
from xcat.bitcoinRPC import bitcoinProxy
|
||||
from xcat.zcashRPC import zcashProxy
|
||||
from xcat.xcatconf import *
|
||||
|
||||
def enter_trade_id():
|
||||
tradeid = input("Enter a unique identifier for this trade: ")
|
||||
|
|
|
@ -17,9 +17,11 @@ ADDRS = {
|
|||
"zcash": "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc"
|
||||
},
|
||||
"fulfiller": {
|
||||
"bitcoin": "mm2smEJjRN4xoijEfpb5XvYd8e3EYWezom",
|
||||
"zcash": "tmPwPdceaJAHQn7UiRCVnJ5tXBXHVqWMkis"
|
||||
"bitcoin": "mn2boR7rYq9DaAWWrVN5MazHKFyf7UhdyU",
|
||||
"zcash": "tmErB22A1G74aq32aAh5AoqgQSJsAAAdT2p"
|
||||
},
|
||||
"amounts": {'buy': {'currency': 'zcash', 'amount': 0.02}, 'sell': {'currency': 'bitcoin', 'amount': 0.01}}
|
||||
}
|
||||
}
|
||||
|
||||
NETWORK = 'testnet'
|
||||
|
|
119
xcat/zcashRPC.py
119
xcat/zcashRPC.py
|
@ -12,6 +12,7 @@ from zcash.core import b2x, lx, x, b2lx, COIN, COutPoint, CMutableTxOut, CMutabl
|
|||
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 logging
|
||||
|
||||
from xcat.utils import x2s
|
||||
|
||||
|
@ -104,6 +105,7 @@ class zcashProxy():
|
|||
for tx in txs:
|
||||
raw = self.zcashd.gettransaction(lx(tx['txid']))['hex']
|
||||
decoded = self.zcashd.decoderawtransaction(raw)
|
||||
# print("TXINFO", decoded['vin'][0])
|
||||
if('txid' in decoded['vin'][0]):
|
||||
sendid = decoded['vin'][0]['txid']
|
||||
if (sendid == fundtx_input ):
|
||||
|
@ -112,14 +114,42 @@ class zcashProxy():
|
|||
print("Redeem transaction with secret not found")
|
||||
return
|
||||
|
||||
# def find_secret(self, p2sh, fundtx_input):
|
||||
# print("In find secret zcashrpc")
|
||||
# txs = self.zcashd.call('listtransactions', "*", 200, 0, True)
|
||||
# for tx in txs:
|
||||
# if tx['address'] == p2sh: # Only check txs involving imported p2sh
|
||||
# raw = self.zcashd.gettransaction(lx(tx['txid']))['hex']
|
||||
# decoded = self.zcashd.decoderawtransaction(raw)
|
||||
# print('decoded', decoded)
|
||||
# if('txid' in decoded['vin'][0]):
|
||||
# sendid = decoded['vin'][0]['txid']
|
||||
# print("sendid", sendid)
|
||||
# if (sendid == fundtx_input ):
|
||||
# print("Found funding zcash tx: ", sendid)
|
||||
# res = self.parse_secret(lx(tx['txid']))
|
||||
# secret = res[0]
|
||||
# redeemPubkey = res[1]
|
||||
# if secret is None:
|
||||
# print("Secret not found")
|
||||
# res = self.validateaddress(redeemPubkey)
|
||||
# if res['ismine']:
|
||||
# print("Funding tx already refunded. Sent to your address {0}".format(redeemPubkey))
|
||||
# logging.debug("Redeem transaction with secret not found")
|
||||
# return
|
||||
|
||||
def parse_secret(self, txid):
|
||||
raw = self.zcashd.gettransaction(txid, True)['hex']
|
||||
decoded = self.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))
|
||||
try:
|
||||
secret = x2s(asm[2])
|
||||
except:
|
||||
secret = None
|
||||
self.redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
|
||||
print("redeemPubkey: ", self.redeemPubkey)
|
||||
return secret
|
||||
|
||||
def redeem_contract(self, contract, secret):
|
||||
|
@ -136,48 +166,69 @@ class zcashProxy():
|
|||
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 = 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)
|
||||
|
||||
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 = 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}
|
||||
return self.redeem(contract, fundtx, secret)
|
||||
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}
|
||||
return self.refund(contract)
|
||||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def redeem(self, contract, fundtx, secret):
|
||||
# TODO: parse the script once, up front.
|
||||
redeemPubKey = self.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 = 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}
|
||||
|
||||
def refund(self, contract):
|
||||
fundtx = self.find_transaction_to_address(contract.p2sh)
|
||||
print("Fund tx found in refund: ", fundtx)
|
||||
refundPubKey = self.find_refundAddr(contract)
|
||||
print('refundPubKey: {0}'.format(refundPubKey))
|
||||
|
||||
redeemScript = CScript(x(contract.redeemScript))
|
||||
txin = CMutableTxIn(fundtx['outpoint'])
|
||||
txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey())
|
||||
# Create the unsigned raw transaction.
|
||||
tx = CMutableTransaction([txin], [txout])
|
||||
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||
privkey = self.zcashd.dumpprivkey(refundPubKey)
|
||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||
# Sign without secret
|
||||
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
||||
# txin.nSequence = 2185
|
||||
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||
print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize())))
|
||||
res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||
print("Script verified, sending raw transaction... (NOT)", res)
|
||||
txid = self.zcashd.sendrawtransaction(tx)
|
||||
refund_tx = b2x(lx(b2x(txid)))
|
||||
fund_tx = str(fundtx['outpoint'])
|
||||
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
|
||||
|
||||
def parse_script(self, script_hex):
|
||||
redeemScript = self.zcashd.decodescript(script_hex)
|
||||
scriptarray = redeemScript['asm'].split(' ')
|
||||
|
|
Loading…
Reference in New Issue