Refactor to use python-bitcoinlib rpc proxy
This commit is contained in:
parent
3e1bb1fbaf
commit
75c253994a
|
@ -0,0 +1,45 @@
|
||||||
|
# ZBXCAT
|
||||||
|
A work-in-progress for Zcash Bitcoin Cross-Chain Atomic Transactions
|
||||||
|
|
||||||
|
Contains basic scripts we're still testing in regtest mode on both networks. This may all be refactored as we go.
|
||||||
|
|
||||||
|
The ZBXCAT directory contains BitcoinRPC and ZcashRPC, wrappers around the rpc interface that can be imported as Python modules.
|
||||||
|
|
||||||
|
The settings.py file in BitcoinRPC and ZcashRPC parse the config files for username/password and set the network ports.
|
||||||
|
|
||||||
|
Most functions are named exactly the same as the rpc methods, except for a few additional custom functions that do things like return only the version number.
|
||||||
|
|
||||||
|
**EDIT**: The scripts now use the rpc proxy code in python-bitcoinlib, and ZDaemon's functions will be refactored into python-zcashlib (a Zcash fork of python-bitcoinlib)
|
||||||
|
|
||||||
|
## Current status of scripts
|
||||||
|
|
||||||
|
Run `redeem-preimage-p2sh.py` to test. It creates and redeems a p2sh transaction using a preimage. To successfully run it, you need python3, the dependencies installed, and a bitcoin daemon running in regtest mode.
|
||||||
|
|
||||||
|
(Currently only tested on Bitcoin. Need to verify that the Zcash fork of python-bitcoinlib, one of the dependencies, works properly, then figure out the best way to install it.)
|
||||||
|
|
||||||
|
`bitcoin-swap.py` contains all the functions that use a proxy to interact with a Bitcoin daemon.
|
||||||
|
|
||||||
|
Use python3 to test. To create a virtualenv for python3, run this command from the top level of the directory:
|
||||||
|
```
|
||||||
|
virtualenv -p python3 venv
|
||||||
|
source venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Install dependencies for ZBXCAT: `pip install -r requirements.txt`
|
||||||
|
|
||||||
|
## Installing python-zcashlib for testing and editing
|
||||||
|
|
||||||
|
The Zcash fork of python-bitcoinlib that is currently in progress:
|
||||||
|
|
||||||
|
`git clone https://github.com/arcalinea/python-bitcoinlib/tree/zcashlib`
|
||||||
|
|
||||||
|
You can install this module locally through pip, in editable mode, so that changes you make are applied immediately. For install from local filesystem path:
|
||||||
|
|
||||||
|
`pip install --editable (-e) <path-to-zcashlib>`
|
||||||
|
|
||||||
|
|
||||||
|
## Misc
|
||||||
|
|
||||||
|
`protocol-pseudocode.py` is guaranteed to not run. It was written as a brainstorm/sketch of a hypothetical xcat protocol using @ebfull's fork of Zcash/Bitcoin that supports createhtlc as an rpc command. Including here in case it's useful in any way.
|
||||||
|
|
||||||
|
I used the module [future](http://python-future.org/futurize.html) to make existing python2 code for the rpc interface compatible with python3.
|
|
@ -1,103 +0,0 @@
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
|
|
||||||
from .settings import *
|
|
||||||
|
|
||||||
class BDaemon(object):
|
|
||||||
|
|
||||||
id_count = 0
|
|
||||||
|
|
||||||
def __init__(self, network=NETWORK, user=RPCUSER, password=RPCPASSWORD, timeout=TIMEOUT):
|
|
||||||
#TODO: check utf safety
|
|
||||||
url = network_url(network)
|
|
||||||
print('In mode:', network)
|
|
||||||
self.network = url
|
|
||||||
self.user = user.encode('utf8')
|
|
||||||
self.password = password.encode('utf8')
|
|
||||||
self.timeout = timeout
|
|
||||||
|
|
||||||
|
|
||||||
def _call(self, method, *args):
|
|
||||||
jsondata = json.dumps({ 'version': '2',
|
|
||||||
'method': method,
|
|
||||||
'params': args,
|
|
||||||
'id': self.id_count})
|
|
||||||
|
|
||||||
r = requests.post(self.network, auth=(self.user,self.password), data=jsondata, timeout=self.timeout)
|
|
||||||
|
|
||||||
self.id_count += 1
|
|
||||||
|
|
||||||
resp = json.loads(r.text)
|
|
||||||
|
|
||||||
#TODO: deal with errors better.
|
|
||||||
error = resp['error']
|
|
||||||
if error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return resp['result']
|
|
||||||
|
|
||||||
# REGTEST
|
|
||||||
def generate(self, blocknum):
|
|
||||||
return self._call('generate', blocknum)
|
|
||||||
|
|
||||||
def importaddress(self, script):
|
|
||||||
return self._call('importaddress', script)
|
|
||||||
|
|
||||||
#Block Info
|
|
||||||
def getBlockHash(self, blockheight):
|
|
||||||
return self._call('getblockhash', blockheight)
|
|
||||||
|
|
||||||
def getBlockByHash(self, blockhash):
|
|
||||||
return self._call('getblock', blockhash)
|
|
||||||
|
|
||||||
def getBlockByHeight(self, blockheight):
|
|
||||||
return self.getBlockByHash(self.getBlockHash(blockheight))
|
|
||||||
|
|
||||||
# Custom methods to get Network Info
|
|
||||||
def getNetworkHeight(self):
|
|
||||||
return self._call('getblockcount')
|
|
||||||
|
|
||||||
def getNetworkDifficulty(self):
|
|
||||||
return self._call('getdifficulty')
|
|
||||||
|
|
||||||
def getVersion(self):
|
|
||||||
info = self._call('getnetworkinfo')
|
|
||||||
client = info['subversion']
|
|
||||||
version = client.strip('/').split(':')[1]
|
|
||||||
return version
|
|
||||||
|
|
||||||
def getConnectionCount(self):
|
|
||||||
return self._call('getconnectioncount')
|
|
||||||
|
|
||||||
# Wallet Info
|
|
||||||
def getbalance(self):
|
|
||||||
return self._call('getbalance')
|
|
||||||
|
|
||||||
def listunspent(self, minconf=1):
|
|
||||||
return self._call('listunspent', minconf)
|
|
||||||
|
|
||||||
#Raw Txs
|
|
||||||
def gettransaction(self, txid):
|
|
||||||
return self._call('gettransaction', txid)
|
|
||||||
|
|
||||||
def getrawtransaction(self, txid, verbose=0):
|
|
||||||
# default verbose=0 returns serialized, hex-encoded data
|
|
||||||
# verbose=1, returns a JSON obj of tx
|
|
||||||
return self._call('getrawtransaction', txid, verbose)
|
|
||||||
|
|
||||||
def decoderawtransaction(self, txhex):
|
|
||||||
return self._call('decoderawtransaction', txhex)
|
|
||||||
|
|
||||||
def sendrawtransaction(self, txhex):
|
|
||||||
return self._call('sendrawtransaction', txhex)
|
|
||||||
|
|
||||||
def getnewaddress(self):
|
|
||||||
return self._call('getnewaddress')
|
|
||||||
|
|
||||||
def sendtoaddress(self, taddress, amount):
|
|
||||||
return self._call('sendtoaddress', taddress, amount)
|
|
||||||
|
|
||||||
def listunspent(self):
|
|
||||||
return self._call('listunspent')
|
|
|
@ -1,28 +0,0 @@
|
||||||
import os, re
|
|
||||||
|
|
||||||
TIMEOUT = 100
|
|
||||||
#Default fee to use on network for txs.
|
|
||||||
DEFAULT_FEE = 0.01
|
|
||||||
# Default network is testnet
|
|
||||||
NETWORK = 'TESTNET'
|
|
||||||
def network_url(network):
|
|
||||||
if network == 'TESTNET':
|
|
||||||
return "http://localhost:18332"
|
|
||||||
if network == 'REGTEST':
|
|
||||||
return "http://localhost:18332"
|
|
||||||
if network == 'MAINNET':
|
|
||||||
return "http://localhost:8332"
|
|
||||||
|
|
||||||
bitcoinconf = os.path.expanduser('~/.bitcoin/bitcoin.conf')
|
|
||||||
def read_config(filename):
|
|
||||||
f = open(filename)
|
|
||||||
for line in f:
|
|
||||||
if re.match('rpcuser', line):
|
|
||||||
user = line.strip('\n').split('=')[1]
|
|
||||||
if re.match('rpcpassword', line):
|
|
||||||
password = line.strip('\n').split('=')[1]
|
|
||||||
return (user, password)
|
|
||||||
config = read_config(bitcoinconf)
|
|
||||||
# from bitcoin.conf
|
|
||||||
RPCUSER = config[0]
|
|
||||||
RPCPASSWORD = config[1]
|
|
|
@ -1 +0,0 @@
|
||||||
Python wrapper for bitcoind and zcashd, created for ZBXCAT - Zcash Bitcoin Cross-Chain Atomic Transactions.
|
|
|
@ -1,160 +0,0 @@
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
|
|
||||||
from .settings import *
|
|
||||||
|
|
||||||
class ZDaemon(object):
|
|
||||||
|
|
||||||
id_count = 0
|
|
||||||
|
|
||||||
def __init__(self, network=NETWORK, user=RPCUSER, password=RPCPASSWORD, timeout=TIMEOUT):
|
|
||||||
#TODO: check utf safety
|
|
||||||
url = network_url(network)
|
|
||||||
print('In mode:', network)
|
|
||||||
self.network = url
|
|
||||||
self.user = user.encode('utf8')
|
|
||||||
self.password = password.encode('utf8')
|
|
||||||
self.timeout = timeout
|
|
||||||
|
|
||||||
|
|
||||||
def _call(self, method, *args):
|
|
||||||
jsondata = json.dumps({ 'version': '2',
|
|
||||||
'method': method,
|
|
||||||
'params': args,
|
|
||||||
'id': self.id_count})
|
|
||||||
|
|
||||||
print("jsondata", jsondata)
|
|
||||||
|
|
||||||
r = requests.post(self.network, auth=(self.user,self.password), data=jsondata, timeout=self.timeout)
|
|
||||||
|
|
||||||
self.id_count += 1
|
|
||||||
|
|
||||||
resp = json.loads(r.text)
|
|
||||||
|
|
||||||
#TODO: deal with errors better.
|
|
||||||
error = resp['error']
|
|
||||||
if error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return resp['result']
|
|
||||||
|
|
||||||
# REGTEST
|
|
||||||
def generate(self, blocknum):
|
|
||||||
return self._call('generate', blocknum)
|
|
||||||
|
|
||||||
def importaddress(self, script):
|
|
||||||
return self._call('importaddress', script)
|
|
||||||
|
|
||||||
#Block Info
|
|
||||||
def getBlockHash(self, blockheight):
|
|
||||||
return self._call('getblockhash', blockheight)
|
|
||||||
|
|
||||||
def getBlockByHash(self, blockhash):
|
|
||||||
return self._call('getblock', blockhash)
|
|
||||||
|
|
||||||
def getBlockByHeight(self, blockheight):
|
|
||||||
return self.getBlockByHash(self.getBlockHash(blockheight))
|
|
||||||
|
|
||||||
# Custom methods to get Network Info
|
|
||||||
def getNetworkHeight(self):
|
|
||||||
return self._call('getblockcount')
|
|
||||||
|
|
||||||
def getNetworkDifficulty(self):
|
|
||||||
return self._call('getdifficulty')
|
|
||||||
|
|
||||||
def getVersion(self):
|
|
||||||
info = self._call('getnetworkinfo')
|
|
||||||
client = info['subversion']
|
|
||||||
version = client.strip('/').split(':')[1]
|
|
||||||
return version
|
|
||||||
|
|
||||||
def getConnectionCount(self):
|
|
||||||
return self._call('getconnectioncount')
|
|
||||||
|
|
||||||
# Wallet Info (transparent)
|
|
||||||
def getbalance(self):
|
|
||||||
return self._call('getbalance')
|
|
||||||
|
|
||||||
def listunspent(self, minconf=1):
|
|
||||||
return self._call('listunspent', minconf)
|
|
||||||
|
|
||||||
#Raw Txs
|
|
||||||
def gettransaction(self, txid):
|
|
||||||
return self._call('gettransaction', txid)
|
|
||||||
|
|
||||||
def getrawtransaction(self, txid, verbose=0):
|
|
||||||
# default verbose=0 returns serialized, hex-encoded data
|
|
||||||
# verbose=1, returns a JSON obj of tx
|
|
||||||
return self._call('getrawtransaction', txid, verbose)
|
|
||||||
|
|
||||||
def decoderawtransaction(self, txhex):
|
|
||||||
return self._call('decoderawtransaction', txhex)
|
|
||||||
|
|
||||||
def sendrawtransaction(self, txhex):
|
|
||||||
return self._call('sendrawtransaction', txhex)
|
|
||||||
|
|
||||||
# taddr methods
|
|
||||||
def getnewaddress(self):
|
|
||||||
return self._call('getnewaddress')
|
|
||||||
|
|
||||||
def sendtoaddress(self, taddress, amount):
|
|
||||||
return self._call('sendtoaddress', taddress, amount)
|
|
||||||
|
|
||||||
def listunspent(self):
|
|
||||||
return self._call('listunspent')
|
|
||||||
|
|
||||||
# Custom method to find a taddr with spendable utxos for z_sendmany
|
|
||||||
def find_taddr_with_unspent(self):
|
|
||||||
unspent = self._call('listunspent')
|
|
||||||
for utxo in unspent:
|
|
||||||
if utxo['spendable'] == True and utxo['amount'] > 0.1:
|
|
||||||
# Check that it's not a coinbase tx
|
|
||||||
tx = zd.gettransaction(utxo['txid'])
|
|
||||||
if 'generated' not in tx:
|
|
||||||
return tx['address']
|
|
||||||
|
|
||||||
def sweep_coinbase(self, zaddr):
|
|
||||||
cb = []
|
|
||||||
utxos = self.listunspent()
|
|
||||||
for utxo in utxos:
|
|
||||||
tx = self.gettransaction(utxo['txid'])
|
|
||||||
if 'generated' in tx and tx['generated'] == True:
|
|
||||||
cb.append(utxo)
|
|
||||||
for coin in cb:
|
|
||||||
amount = coin['amount'] - 0.0001
|
|
||||||
opid = self.z_sendmany(coin['address'], zaddr, amount)
|
|
||||||
print("OPID of z_sendmany: ", opid)
|
|
||||||
status = self.z_getoperationstatus(opid)
|
|
||||||
print("Status: ", status[0]['status'])
|
|
||||||
|
|
||||||
# zaddr methods
|
|
||||||
def z_gettotalbalance(self):
|
|
||||||
return self._call('z_gettotalbalance')
|
|
||||||
|
|
||||||
def z_getnewaddress(self):
|
|
||||||
return self._call('z_getnewaddress')
|
|
||||||
|
|
||||||
def z_listaddresses(self):
|
|
||||||
return self._call('z_listaddresses')
|
|
||||||
|
|
||||||
def z_listreceivedbyaddress(self, zaddr, minconf=1):
|
|
||||||
return self._call('z_listreceivedbyaddress', zaddr, minconf)
|
|
||||||
|
|
||||||
def z_getoperationstatus(self, opid):
|
|
||||||
return self._call('z_getoperationstatus', ["{0}".format(opid)])
|
|
||||||
|
|
||||||
def z_getoperationresult(self, opid):
|
|
||||||
return self._call('z_getoperationresult', ["{0}".format(opid)])
|
|
||||||
|
|
||||||
# With addition of encrypted memo field
|
|
||||||
def z_sendmany(self, sender, receiver, amount=0.0001, memo=''):
|
|
||||||
amts_array = []
|
|
||||||
if memo == '':
|
|
||||||
amounts = {"address": receiver, "amount": amount}
|
|
||||||
else:
|
|
||||||
memo = memo.encode('hex')
|
|
||||||
amounts = {"address": receiver, "amount": amount, "memo": memo}
|
|
||||||
amts_array.append(amounts)
|
|
||||||
return self._call('z_sendmany', sender, amts_array)
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,38 +0,0 @@
|
||||||
import os, re
|
|
||||||
|
|
||||||
#Timeout needs to be high for any pour operations
|
|
||||||
TIMEOUT = 600
|
|
||||||
#Default fee to use on network for txs.
|
|
||||||
DEFAULT_FEE = 0.01
|
|
||||||
|
|
||||||
# Default is testnet
|
|
||||||
NETWORK = 'TESTNET'
|
|
||||||
def network_url(network):
|
|
||||||
if network == 'TESTNET':
|
|
||||||
return "http://localhost:18232"
|
|
||||||
if network == 'REGTEST':
|
|
||||||
return "http://localhost:18232"
|
|
||||||
if network == 'MAINNET':
|
|
||||||
return "http://localhost:18444"
|
|
||||||
|
|
||||||
zcashconf = os.path.expanduser('~/.zcash/zcash.conf')
|
|
||||||
def read_config(filename):
|
|
||||||
f = open(filename)
|
|
||||||
for line in f:
|
|
||||||
if re.match('rpcuser', line):
|
|
||||||
user = line.strip('\n').split('=')[1]
|
|
||||||
if re.match('rpcpassword', line):
|
|
||||||
password = line.strip('\n').split('=')[1]
|
|
||||||
return (user, password)
|
|
||||||
config = read_config(zcashconf)
|
|
||||||
# from zcash conf
|
|
||||||
RPCUSER = config[0]
|
|
||||||
RPCPASSWORD = config[1]
|
|
||||||
|
|
||||||
|
|
||||||
#TESTS
|
|
||||||
#for tests (sample data here - replace with your own)
|
|
||||||
TEST_TXID = ''
|
|
||||||
TEST_ZADDR = ""
|
|
||||||
TEST_TADDR = ""
|
|
||||||
TEST_ZSECRET = ""
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,2 +0,0 @@
|
||||||
requests
|
|
||||||
python-bitcoinlib
|
|
|
@ -1,38 +0,0 @@
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
import os.path
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from .rpc.ZDaemon import *
|
|
||||||
from settings import *
|
|
||||||
|
|
||||||
def test_daemon():
|
|
||||||
zd = ZDaemon()
|
|
||||||
|
|
||||||
# Network tests
|
|
||||||
print(zd.getBlockHash(100))
|
|
||||||
print(zd.getBlockByHash(zd.getBlockHash(100)))
|
|
||||||
print(zd.getBlockByHeight(100))
|
|
||||||
print(zd.getNetworkHeight())
|
|
||||||
print(zd.getNetworkDifficulty())
|
|
||||||
print(zd.getVersion())
|
|
||||||
print(zd.getConnectionCount())
|
|
||||||
|
|
||||||
# Taddr Wallet tests
|
|
||||||
print(zd.getbalance())
|
|
||||||
print(zd.listunspent())
|
|
||||||
print(zd.getnewaddress())
|
|
||||||
|
|
||||||
# Zaddr wallet tests
|
|
||||||
print(zd.z_getnewaddress())
|
|
||||||
zaddrs = zd.z_listaddresses()
|
|
||||||
print(zaddrs)
|
|
||||||
zaddr_received = zd.z_listreceivedbyaddress(zaddr)
|
|
||||||
print(zaddr_received)
|
|
||||||
|
|
||||||
# TODO: test z_sendmany, and use regtest
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# test_daemon()
|
|
||||||
zd = ZDaemon()
|
|
||||||
print(zd.getVersion())
|
|
35
daemon.py
35
daemon.py
|
@ -1,35 +0,0 @@
|
||||||
from ZBXCAT.BitcoinRPC.BDaemon import *
|
|
||||||
|
|
||||||
bd = BDaemon('REGTEST')
|
|
||||||
# v = bd.getVersion()
|
|
||||||
# print(v)
|
|
||||||
|
|
||||||
def generate(num):
|
|
||||||
gen = bd.generate(num)
|
|
||||||
print("Generated blocks", gen)
|
|
||||||
|
|
||||||
def fund_p2sh(p2sh, amount):
|
|
||||||
fund_tx = bd.sendtoaddress(p2sh, amount)
|
|
||||||
return fund_tx
|
|
||||||
|
|
||||||
def tx_details(txid):
|
|
||||||
tx = bd.gettransaction(txid)
|
|
||||||
details = tx['details'][0]
|
|
||||||
return details
|
|
||||||
|
|
||||||
# These two methods are placeholders
|
|
||||||
def get_recipient_address():
|
|
||||||
address = bd.getnewaddress()
|
|
||||||
return address
|
|
||||||
|
|
||||||
def get_sender_address():
|
|
||||||
address = bd.getnewaddress()
|
|
||||||
return address
|
|
||||||
|
|
||||||
def importaddress(addr):
|
|
||||||
res = bd.importaddress(addr)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def sendrawtx(hex):
|
|
||||||
txid = bd.sendrawtransaction(hex)
|
|
||||||
return txid
|
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Based on spend-p2sh-txout.py from python-bitcoinlib.
|
# Based on spend-p2sh-txout.py from python-bitcoinlib.
|
||||||
# Copyright (C) 2014 The python-bitcoinlib developers
|
|
||||||
# Copyright (C) 2017 The Zcash developers
|
# Copyright (C) 2017 The Zcash developers
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -9,6 +8,8 @@ if sys.version_info.major < 3:
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
import bitcoin
|
||||||
|
import bitcoin.rpc
|
||||||
from bitcoin import SelectParams
|
from bitcoin import SelectParams
|
||||||
from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
||||||
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
|
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
|
||||||
|
@ -17,23 +18,21 @@ from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
||||||
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
|
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from daemon import *
|
|
||||||
|
|
||||||
SelectParams('testnet')
|
# SelectParams('testnet')
|
||||||
|
# To get transactions not in your wallet, must set -txindex=1
|
||||||
|
SelectParams('regtest')
|
||||||
|
proxy = bitcoin.rpc.Proxy()
|
||||||
|
|
||||||
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
|
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
|
||||||
preimage = b'preimage'
|
preimage = b'preimage'
|
||||||
h = hashlib.sha256(preimage).digest()
|
h = hashlib.sha256(preimage).digest()
|
||||||
seckey = CBitcoinSecret('cShLodcaVA7JjDXnRaK5jsmQhPJmBDeXPd4Fzx8dRD9ih6gmXKH6')
|
|
||||||
|
|
||||||
# AUTOMATE - get recipient addr
|
# proxy.getnewaddress() returns CBitcoinAddress
|
||||||
recipient_address = get_recipient_address()
|
recipientpubkey = proxy.getnewaddress()
|
||||||
print(' recipient_address (newly generated)', recipient_address)
|
senderpubkey = proxy.getnewaddress()
|
||||||
sender_address = get_sender_address()
|
# privkey of the recipient, used to sign the redeemTx
|
||||||
print(' sender_address (newly generated)', sender_address)
|
seckey = proxy.dumpprivkey(recipientpubkey)
|
||||||
|
|
||||||
recipientpubkey = CBitcoinAddress('mheZcjatFMjcHX5hVQdAY4Lvxm7q7rXuU2')
|
|
||||||
senderpubkey = CBitcoinAddress('mheZcjatFMjcHX5hVQdAY4Lvxm7q7rXuU2')
|
|
||||||
|
|
||||||
blocknum = 7
|
blocknum = 7
|
||||||
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
|
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
|
||||||
|
@ -43,55 +42,36 @@ txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH
|
||||||
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
||||||
print("redeem script:", b2x(txin_redeemScript))
|
print("redeem script:", b2x(txin_redeemScript))
|
||||||
|
|
||||||
#print("scriptpubey:", b2x(recipientpubkey.to_scriptPubKey()))
|
# Create P2SH scriptPubKey from redeemScript.
|
||||||
|
|
||||||
# Create P2SH scriptPubKey from redeemScript.
|
|
||||||
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
|
||||||
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
|
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
|
||||||
|
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it.
|
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
||||||
# You'll need to send some funds to it to create a txout to spend.
|
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
||||||
print('Pay to:',str(txin_p2sh_address))
|
|
||||||
|
|
||||||
p2sh = str(txin_p2sh_address)
|
p2sh = str(txin_p2sh_address)
|
||||||
|
print('Pay to:', p2sh)
|
||||||
|
|
||||||
#AUTOMATE funding tx, seller sends funds
|
# AUTOMATE Send funds to p2sh
|
||||||
amount = 1.0
|
amount = 1.0*COIN
|
||||||
fund_tx = fund_p2sh(p2sh, amount)
|
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
|
||||||
print("TXID", fund_tx)
|
|
||||||
|
|
||||||
# AUTOMATE import this address?
|
|
||||||
importaddress(p2sh)
|
|
||||||
print('p2sh address imported')
|
|
||||||
|
|
||||||
# MINE THE FUNDED TX
|
|
||||||
generate(1)
|
|
||||||
|
|
||||||
print('Now redeeming.........')
|
print('Now redeeming.........')
|
||||||
|
|
||||||
# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin
|
# AUTOMATE getting vout of funding tx
|
||||||
# transaction hashes are shown little-endian rather than the usual big-endian.
|
txinfo = proxy.gettransaction(fund_tx)
|
||||||
|
details = txinfo['details'][0]
|
||||||
# AUTOMATE
|
|
||||||
txid = lx(fund_tx)
|
|
||||||
details = tx_details(fund_tx)
|
|
||||||
print('get details of fund_tx', details)
|
|
||||||
vout = details['vout']
|
vout = details['vout']
|
||||||
print('vout', vout)
|
|
||||||
|
|
||||||
# Create the txin structure, which includes the outpoint. The scriptSig
|
# Create the txin structure. scriptSig defaults to being empty.
|
||||||
# defaults to being empty.
|
# The input is the p2sh funding transaction txid, vout is its index
|
||||||
txin = CMutableTxIn(COutPoint(txid, vout))
|
txin = CMutableTxIn(COutPoint(fund_tx, vout))
|
||||||
|
|
||||||
# Create the txout. This time we create the scriptPubKey from a Bitcoin
|
# Create the txout. Pays out to recipient, so uses recipient's pubkey
|
||||||
# address.
|
# Withdraw full amount minus fee
|
||||||
# AUTOMATE: amount and address set above
|
default_fee = 0.001*COIN
|
||||||
redeemed = amount*COIN
|
txout = CMutableTxOut(amount - default_fee, recipientpubkey.to_scriptPubKey())
|
||||||
fee = 0.001*COIN
|
|
||||||
txout = CMutableTxOut(redeemed - fee, CBitcoinAddress(recipient_address).to_scriptPubKey())
|
|
||||||
|
|
||||||
# Create the unsigned transaction.
|
# Create the unsigned raw transaction.
|
||||||
tx = CMutableTransaction([txin], [txout])
|
tx = CMutableTransaction([txin], [txout])
|
||||||
|
|
||||||
# Calculate the signature hash for that transaction. Note how the script we use
|
# Calculate the signature hash for that transaction. Note how the script we use
|
||||||
|
@ -103,19 +83,13 @@ sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
|
|
||||||
# Set the scriptSig of our transaction input appropriately.
|
# Set the scriptSig of our transaction input appropriately.
|
||||||
txin.scriptSig = CScript([ sig,seckey.pub, preimage, OP_TRUE, txin_redeemScript])
|
txin.scriptSig = CScript([ sig, seckey.pub, preimage, OP_TRUE, txin_redeemScript])
|
||||||
|
|
||||||
print("scriptSig:", b2x(txin.scriptSig))
|
print("Redeem tx hex:", b2x(tx.serialize()))
|
||||||
|
|
||||||
# Verify the signature worked.
|
# Verify the signature worked.
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
|
|
||||||
# Tx to hex
|
print("Now sending redeem transaction.......")
|
||||||
hextx = b2x(tx.serialize())
|
txid = proxy.sendrawtransaction(tx)
|
||||||
print(hextx)
|
print("Txid of submitted redeem tx: ", b2x(txid))
|
||||||
|
|
||||||
# AUTOMATE: Send raw redeem tx
|
|
||||||
txid = sendrawtx(hextx)
|
|
||||||
print("txid of submitted redeem tx", txid)
|
|
||||||
|
|
||||||
generate(1)
|
|
||||||
|
|
Loading…
Reference in New Issue