Merge branch 'master' of https://github.com/zcash-hackworks/zbxcat
This commit is contained in:
commit
772f1b857b
|
@ -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,21 +165,30 @@ 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))
|
||||
return self.redeem(contract, fundtx, secret)
|
||||
else:
|
||||
print("nLocktime exceeded, refunding")
|
||||
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, redeemPubKey.to_scriptPubKey())
|
||||
txout = CMutableTxOut(fundtx['amount'] - FEE, self.redeemPubKey.to_scriptPubKey())
|
||||
# Create the unsigned raw transaction.
|
||||
tx = CMutableTransaction([txin], [txout])
|
||||
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
||||
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||
# TODO: protect privkey better, separate signing from rawtx creation
|
||||
privkey = self.bitcoind.dumpprivkey(redeemPubKey)
|
||||
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, zec_redeemScript])
|
||||
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript])
|
||||
|
||||
# print("txin.scriptSig", b2x(txin.scriptSig))
|
||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
||||
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...")
|
||||
|
@ -183,15 +196,32 @@ class bitcoinProxy():
|
|||
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'])
|
||||
|
||||
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}
|
||||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def parse_script(self, script_hex):
|
||||
redeemScript = self.bitcoind.call('decodescript', script_hex)
|
||||
|
@ -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):
|
||||
|
|
32
xcat/cli.py
32
xcat/cli.py
|
@ -27,11 +27,12 @@ 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
|
||||
cleanup(tradeid)
|
||||
elif status == 'sellerFunded':
|
||||
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(trade.buy.currency))
|
||||
|
@ -101,6 +102,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")
|
||||
|
||||
# Import a trade in hex, and save to db
|
||||
|
@ -173,9 +175,9 @@ def checktrade(tradeid):
|
|||
def newtrade(tradeid, **kwargs):
|
||||
print("Creating new XCAT trade...")
|
||||
erase_trade()
|
||||
tradeid, trade= initialize_trade(tradeid, conf=kwargs['conf'])
|
||||
print("Trade", trade)
|
||||
trade = seller_init(tradeid, trade)
|
||||
tradeid, trade= initialize_trade(tradeid, conf=kwargs['conf'], network=kwargs['network'])
|
||||
print("New trade created: {0}".format(trade))
|
||||
trade = 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)
|
||||
return trade
|
||||
|
@ -199,12 +201,24 @@ def main():
|
|||
'''))
|
||||
parser.add_argument("command", action="store", help="list commands")
|
||||
parser.add_argument("arguments", action="store", nargs="*", help="add arguments")
|
||||
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode. Defaults to false")
|
||||
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("-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")
|
||||
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:
|
||||
|
@ -234,9 +248,9 @@ def main():
|
|||
if len(args.arguments) < 1: throw("Usage: newtrade [tradeid]")
|
||||
tradeid = args.arguments[0]
|
||||
if args.conf == 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")
|
||||
|
@ -248,8 +262,10 @@ def main():
|
|||
tradeid = args.arguments[0]
|
||||
checkBuyStatus(tradeid)
|
||||
elif command == "step3":
|
||||
generate(31)
|
||||
tradeid = args.arguments[0]
|
||||
checkSellStatus(tradeid)
|
||||
elif command == "step4":
|
||||
# generate(1)
|
||||
tradeid = args.arguments[0]
|
||||
checkBuyStatus(tradeid)
|
||||
|
|
|
@ -8,24 +8,35 @@ import xcat.db as db
|
|||
from xcat.xcatconf import *
|
||||
from xcat.bitcoinRPC import bitcoinProxy
|
||||
from xcat.zcashRPC import zcashProxy
|
||||
import logging
|
||||
|
||||
bitcoinRPC = bitcoinProxy()
|
||||
zcashRPC = zcashProxy()
|
||||
|
||||
def generate(num):
|
||||
bitcoinRPC.generate(num)
|
||||
zcashRPC.generate(num)
|
||||
|
||||
def is_myaddr(address):
|
||||
# Handle different network prefixes
|
||||
if address[:1] == 'm':
|
||||
status = bitcoinRPC.validateaddress(address)
|
||||
else:
|
||||
status = zcashRPC.validateaddress(address)
|
||||
logging.debug("Address status: ", status)
|
||||
if status['isvalid'] is False:
|
||||
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(currency, p2sh, fundtx):
|
||||
if currency == 'bitcoin':
|
||||
secret = bitcoinRPC.find_secret(p2sh, fundtx)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
secret = zcashRPC.find_secret(p2sh, fundtx)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
return secret
|
||||
|
||||
def import_addrs(trade):
|
||||
|
@ -36,17 +47,21 @@ def check_p2sh(currency, address):
|
|||
if currency == 'bitcoin':
|
||||
print("Checking funds in Bitcoin p2sh")
|
||||
return bitcoinRPC.check_funds(address)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
print("Checking funds in Zcash p2sh")
|
||||
return zcashRPC.check_funds(address)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
|
||||
def check_fund_status(currency, address):
|
||||
if currency == 'bitcoin':
|
||||
print("Checking funds in Bitcoin p2sh")
|
||||
return bitcoinRPC.get_fund_status(address)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
print("Checking funds in Zcash p2sh")
|
||||
return zcashRPC.get_fund_status(address)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
|
||||
# TODO: function to calculate appropriate locktimes between chains
|
||||
# def verify_p2sh(trade):
|
||||
|
@ -61,17 +76,50 @@ def check_fund_status(currency, address):
|
|||
def create_htlc(currency, funder, redeemer, commitment, locktime):
|
||||
if currency == 'bitcoin':
|
||||
sell_p2sh = bitcoinRPC.hashtimelockcontract(funder, redeemer, commitment, locktime)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
sell_p2sh = zcashRPC.hashtimelockcontract(funder, redeemer, commitment, locktime)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
return sell_p2sh
|
||||
|
||||
def fund_htlc(currency, p2sh, amount):
|
||||
if currency == 'bitcoin':
|
||||
txid = bitcoinRPC.fund_htlc(p2sh, amount)
|
||||
else:
|
||||
elif currency == 'zcash':
|
||||
txid = zcashRPC.fund_htlc(p2sh, amount)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
return txid
|
||||
|
||||
def redeem_p2sh(contract, secret):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = bitcoinRPC.redeem_contract(contract, secret)
|
||||
elif currency == 'zcash':
|
||||
res = zcashRPC.redeem_contract(contract, secret)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
return res
|
||||
|
||||
def refund_contract(contract):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = bitcoinRPC.refund(contract)
|
||||
elif currency == 'zcash':
|
||||
res = zcashRPC.refund(contract)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
return res
|
||||
|
||||
def parse_secret(currency, txid):
|
||||
if currency == 'bitcoin':
|
||||
secret = bitcoinRPC.parse_secret(txid)
|
||||
elif currency == 'zcash':
|
||||
secret = zcashRPC.parse_secret(txid)
|
||||
else:
|
||||
raise ValueError("Currency not recognized: ", currency)
|
||||
return secret
|
||||
|
||||
def fund_contract(contract):
|
||||
txid = fund_htlc(contract.currency, contract.p2sh, contract.amount)
|
||||
return txid
|
||||
|
@ -109,21 +157,6 @@ def create_buy_p2sh(trade, commitment, locktime):
|
|||
|
||||
save(trade)
|
||||
|
||||
def redeem_p2sh(contract, secret):
|
||||
currency = contract.currency
|
||||
if currency == 'bitcoin':
|
||||
res = bitcoinRPC.redeem_contract(contract, secret)
|
||||
else:
|
||||
res = zcashRPC.redeem_contract(contract, secret)
|
||||
return res
|
||||
|
||||
def parse_secret(chain, txid):
|
||||
if chain == 'bitcoin':
|
||||
secret = bitcoinRPC.parse_secret(txid)
|
||||
else:
|
||||
secret = zcashRPC.parse_secret(txid)
|
||||
return secret
|
||||
|
||||
#### Main functions determining user flow from command line
|
||||
def buyer_redeem(trade):
|
||||
userInput.authorize_buyer_redeem(trade)
|
||||
|
@ -148,7 +181,6 @@ def buyer_redeem(trade):
|
|||
def seller_redeem_p2sh(trade, secret):
|
||||
buy = trade.buy
|
||||
userInput.authorize_seller_redeem(buy)
|
||||
|
||||
if trade.sell.get_status() == 'redeemed':
|
||||
print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency))
|
||||
exit()
|
||||
|
@ -203,11 +235,10 @@ def initialize_trade(tradeid, **kwargs):
|
|||
print(trade.buy.__dict__)
|
||||
return tradeid, trade
|
||||
|
||||
|
||||
def seller_init(tradeid, trade):
|
||||
def seller_init(tradeid, trade, network):
|
||||
secret = generate_password()
|
||||
db.save_secret(tradeid, secret)
|
||||
print("\nGenerated a secret preimage to lock funds. This will only be stored locally: ", secret)
|
||||
print("Generated a secret preimage to lock funds. This will only be stored locally: {0}".format(secret))
|
||||
|
||||
hash_of_secret = sha256(secret)
|
||||
# TODO: Implement locktimes and mock block passage of time
|
||||
|
@ -220,5 +251,5 @@ def seller_init(tradeid, trade):
|
|||
create_buy_p2sh(trade, hash_of_secret, buy_locktime)
|
||||
|
||||
trade.commitment = 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'
|
||||
|
|
|
@ -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]
|
||||
try:
|
||||
secret = x2s(asm[2])
|
||||
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
|
||||
except:
|
||||
secret = None
|
||||
self.redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
|
||||
print("redeemPubkey: ", self.redeemPubkey)
|
||||
return secret
|
||||
|
||||
def redeem_contract(self, contract, secret):
|
||||
|
@ -136,18 +166,23 @@ 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:
|
||||
return self.redeem(contract, fundtx, secret)
|
||||
else:
|
||||
print("nLocktime exceeded, refunding")
|
||||
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.
|
||||
|
@ -167,16 +202,32 @@ class zcashProxy():
|
|||
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")
|
||||
|
||||
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', refundPubKey)
|
||||
txid = self.zcashd.sendtoaddress(refundPubKey, fundtx['amount'] - FEE)
|
||||
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}
|
||||
else:
|
||||
print("No contract for this p2sh found in database", p2sh)
|
||||
|
||||
def parse_script(self, script_hex):
|
||||
redeemScript = self.zcashd.decodescript(script_hex)
|
||||
|
|
Loading…
Reference in New Issue