commit
8a070461cd
|
@ -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
|
||||||
|
|
26
xcat/cli.py
26
xcat/cli.py
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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':
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 #################
|
||||||
############################################
|
############################################
|
||||||
|
|
387
xcat/zcashRPC.py
387
xcat/zcashRPC.py
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue