bitcoin regtest
This commit is contained in:
parent
2141620120
commit
4d4c6b598a
@ -39,7 +39,7 @@ Yes, external developers can modify COLDCARD and make their own versions!
|
||||
If the red/green light is red, this means some part of flash was
|
||||
changed without the secure checksum inside SE1 being first updated.
|
||||
The upgrade process does this correctly in Mk4, and there is no
|
||||
point time the checksum is wrong, so there should be no way to see this
|
||||
point in time the checksum is wrong, so there should be no way to see this
|
||||
screen:
|
||||
|
||||

|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
|
||||
# Check-out and build
|
||||
|
||||
- clone repo
|
||||
- do submodule magic in `external`
|
||||
- make top-level virtual env: `virtualenv -p python3 ENV`
|
||||
- activate it
|
||||
- then:
|
||||
|
||||
cd external/ckcc-protocol
|
||||
pip install -r requirements.txt
|
||||
pip install --editable .
|
||||
cd ../..
|
||||
pip install -r requirements.txt
|
||||
pip install -r unix/requirements.txt
|
||||
|
||||
- should give you a command-line program "ckcc" in your path
|
||||
- should be able to do:
|
||||
|
||||
cd unix
|
||||
make && ./simulator.py
|
||||
|
||||
|
||||
@ -251,12 +251,36 @@ class BitcoinTestnet(BitcoinMain):
|
||||
b44_cointype = 1
|
||||
|
||||
|
||||
class BitcoinRegtest(BitcoinMain):
|
||||
ctype = 'XRT'
|
||||
name = 'Bitcoin Regtest'
|
||||
menu_name = 'Regtest: BTC'
|
||||
|
||||
slip132 = {
|
||||
AF_CLASSIC: Slip132Version(0x043587cf, 0x04358394, 't'),
|
||||
AF_P2WPKH_P2SH: Slip132Version(0x044a5262, 0x044a4e28, 'u'),
|
||||
AF_P2WPKH: Slip132Version(0x045f1cf6, 0x045f18bc, 'v'),
|
||||
AF_P2WSH_P2SH: Slip132Version(0x024289ef, 0x024285b5, 'U'),
|
||||
AF_P2WSH: Slip132Version(0x02575483, 0x02575048, 'V'),
|
||||
}
|
||||
|
||||
bech32_hrp = 'bcrt'
|
||||
|
||||
b58_addr = bytes([111])
|
||||
b58_script = bytes([196])
|
||||
b58_privkey = bytes([239])
|
||||
|
||||
b44_cointype = 1
|
||||
|
||||
|
||||
def get_chain(short_name):
|
||||
# lookup object from name: 'BTC' or 'XTN'
|
||||
if short_name == 'BTC':
|
||||
return BitcoinMain
|
||||
elif short_name == 'XTN':
|
||||
return BitcoinTestnet
|
||||
elif short_name == 'XRT':
|
||||
return BitcoinRegtest
|
||||
else:
|
||||
raise KeyError(short_name)
|
||||
|
||||
@ -271,7 +295,7 @@ def current_chain():
|
||||
return get_chain(chain)
|
||||
|
||||
# Overbuilt: will only be testnet and mainchain.
|
||||
AllChains = [BitcoinMain, BitcoinTestnet]
|
||||
AllChains = [BitcoinMain, BitcoinTestnet, BitcoinRegtest]
|
||||
|
||||
def slip32_deserialize(xp):
|
||||
# .. and classify chain and addr-type, as implied by prefix
|
||||
|
||||
@ -249,8 +249,8 @@ DangerZoneMenu = [
|
||||
MenuItem("Set High-Water", f=set_highwater),
|
||||
MenuItem('Wipe HSM Policy', f=wipe_hsm_policy, predicate=hsm_policy_available),
|
||||
MenuItem('Clear OV cache', f=wipe_ovc),
|
||||
ToggleMenuItem('Testnet Mode', 'chain', ['Bitcoin', 'Testnet3'],
|
||||
value_map=['BTC', 'XTN'],
|
||||
ToggleMenuItem('Testnet Mode', 'chain', ['Bitcoin', 'Testnet3', 'Regtest'],
|
||||
value_map=['BTC', 'XTN', 'XRT'],
|
||||
story="Testnet must only be used by developers because \
|
||||
correctly- crafted transactions signed on Testnet could be broadcast on Mainnet."),
|
||||
MenuItem('Settings Space', f=show_settings_space),
|
||||
|
||||
@ -712,7 +712,8 @@ class MultisigWallet:
|
||||
assert node.privkey() == None # 'no privkeys plz'
|
||||
except ValueError:
|
||||
pass
|
||||
assert chain.ctype == expect_chain # 'wrong chain'
|
||||
# HACK but there is no difference extended_keys - just bech32 hrp
|
||||
assert chain.ctype == expect_chain or expect_chain == "XRT" and chain.ctype == "XTN" # 'wrong chain'
|
||||
|
||||
depth = node.depth()
|
||||
|
||||
|
||||
278
testing/api.py
278
testing/api.py
@ -6,37 +6,128 @@
|
||||
#
|
||||
# testnet=1
|
||||
# server=1
|
||||
# rpcservertimeout=2000 # for test_sign.py::test_io_size
|
||||
#
|
||||
import pytest, os
|
||||
from bitcoinrpc.authproxy import AuthServiceProxy
|
||||
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import atexit
|
||||
import socket
|
||||
import shutil
|
||||
import pytest
|
||||
import tempfile
|
||||
import subprocess
|
||||
from authproxy import AuthServiceProxy, JSONRPCException
|
||||
from base64 import b64encode, b64decode
|
||||
from constants import simulator_fixed_words
|
||||
|
||||
URL = '127.0.0.1:18332/wallet/'
|
||||
|
||||
def get_cookie():
|
||||
# read local bitcoind cookie .. highly mac-only
|
||||
AUTHFILE = '~/Library/Application Support/Bitcoin/testnet3/.cookie'
|
||||
|
||||
try:
|
||||
cookie = open(os.path.expanduser(AUTHFILE), 'rt').read().strip()
|
||||
except FileNotFoundError:
|
||||
raise pytest.skip('no local bitcoind')
|
||||
# stolen from HWI test suite and slightly modified
|
||||
class Bitcoind:
|
||||
def __init__(self, bitcoind_path, signer="/home/more/PycharmProjects/HWI/venv/lib/python3.8/site-packages/hwi.py"):
|
||||
self.bitcoind_path = bitcoind_path
|
||||
self.signer = signer
|
||||
self.datadir = tempfile.mkdtemp()
|
||||
self.rpc = None
|
||||
self.bitcoind_proc = None
|
||||
self.userpass = None
|
||||
self.supply_wallet = None
|
||||
|
||||
return cookie
|
||||
def start(self):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def get_free_port():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.bind(("", 0))
|
||||
s.listen(1)
|
||||
port = s.getsockname()[1]
|
||||
s.close()
|
||||
return port
|
||||
|
||||
self.p2p_port = get_free_port()
|
||||
self.rpc_port = get_free_port()
|
||||
|
||||
self.bitcoind_proc = subprocess.Popen(
|
||||
[
|
||||
self.bitcoind_path,
|
||||
"-regtest",
|
||||
f"-signer={self.signer}",
|
||||
f"-datadir={self.datadir}",
|
||||
"-noprinttoconsole",
|
||||
"-fallbackfee=0.0002",
|
||||
"-keypool=1",
|
||||
f"-port={self.p2p_port}",
|
||||
f"-rpcport={self.rpc_port}"
|
||||
]
|
||||
)
|
||||
|
||||
atexit.register(self.cleanup)
|
||||
|
||||
# Wait for cookie file to be created
|
||||
cookie_path = os.path.join(self.datadir, "regtest", ".cookie")
|
||||
for i in range(20):
|
||||
if not os.path.exists(cookie_path):
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
RuntimeError("'.cookie' not found. Is bitcoind running?")
|
||||
# Read .cookie file to get user and pass
|
||||
with open(cookie_path) as f:
|
||||
self.userpass = f.readline().lstrip().rstrip()
|
||||
self.rpc_url = f"http://{self.userpass}@127.0.0.1:{self.rpc_port}"
|
||||
self.rpc = AuthServiceProxy(self.rpc_url)
|
||||
|
||||
# Wait for bitcoind to be ready
|
||||
ready = False
|
||||
while not ready:
|
||||
try:
|
||||
self.rpc.getblockchaininfo()
|
||||
ready = True
|
||||
except JSONRPCException:
|
||||
time.sleep(0.5)
|
||||
pass
|
||||
|
||||
assert self.rpc.getblockchaininfo()['chain'] == 'regtest'
|
||||
assert self.rpc.getnetworkinfo()['version'] >= 220000, "we require >= 22.0 of Core"
|
||||
# not descriptors so that we can do dumpwallet
|
||||
self.supply_wallet = self.create_wallet(wallet_name="supply", descriptors=False)
|
||||
# Make sure there are blocks and coins available
|
||||
self.supply_wallet.generatetoaddress(101, self.supply_wallet.getnewaddress())
|
||||
|
||||
def get_wallet_rpc(self, wallet):
|
||||
url = self.rpc_url + f"/wallet/{wallet}"
|
||||
return AuthServiceProxy(url)
|
||||
|
||||
def create_wallet(self, wallet_name: str, disable_private_keys: bool = False, blank: bool = False,
|
||||
passphrase: str = None, avoid_reuse: bool = False, descriptors: bool = True,
|
||||
load_on_startup: bool = False, external_signer: bool = False) -> AuthServiceProxy:
|
||||
"""Create wallet and return AuthServiceProxy object to that wallet"""
|
||||
self.rpc.createwallet(wallet_name=wallet_name, disable_private_keys=disable_private_keys,
|
||||
blank=blank, passphrase=passphrase, avoid_reuse=avoid_reuse,
|
||||
descriptors=descriptors, load_on_startup=load_on_startup,
|
||||
external_signer=external_signer)
|
||||
return self.get_wallet_rpc(wallet_name)
|
||||
|
||||
def cleanup(self):
|
||||
if self.bitcoind_proc is not None and self.bitcoind_proc.poll() is None:
|
||||
self.bitcoind_proc.kill()
|
||||
shutil.rmtree(self.datadir)
|
||||
|
||||
@staticmethod
|
||||
def create(*args, **kwargs):
|
||||
c = Bitcoind(*args, **kwargs)
|
||||
c.start()
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def bitcoind():
|
||||
# JSON-RPC connection to a bitcoind instance
|
||||
# this assumes that you have bitcoind in path somewhere
|
||||
bitcoin_d = Bitcoind.create("bitcoind")
|
||||
return bitcoin_d
|
||||
|
||||
# see <https://github.com/jgarzik/python-bitcoinrpc>
|
||||
cookie = get_cookie()
|
||||
|
||||
conn = AuthServiceProxy('http://' + cookie + '@' + URL)
|
||||
|
||||
assert conn.getblockchaininfo()['chain'] == 'test'
|
||||
assert conn.getnetworkinfo()['version'] >= 220000, "we require >= 22.0 of Core"
|
||||
|
||||
return conn
|
||||
|
||||
@pytest.fixture
|
||||
def match_key(bitcoind, set_master_key, reset_seed_words):
|
||||
@ -49,7 +140,7 @@ def match_key(bitcoind, set_master_key, reset_seed_words):
|
||||
print("match_key: doit()")
|
||||
from tempfile import mktemp
|
||||
fn = mktemp()
|
||||
bitcoind.dumpwallet(fn)
|
||||
bitcoind.supply_wallet.dumpwallet(fn)
|
||||
prv = None
|
||||
|
||||
for ln in open(fn, 'rt').readlines():
|
||||
@ -68,37 +159,40 @@ def match_key(bitcoind, set_master_key, reset_seed_words):
|
||||
# NOTE: set_master_key does teardown/reset
|
||||
return doit
|
||||
|
||||
@pytest.fixture()
|
||||
|
||||
@pytest.fixture
|
||||
def bitcoind_finalizer(bitcoind):
|
||||
# Use bitcoind to finalize a PSBT and get out txn
|
||||
|
||||
def doit(psbt, extract=True):
|
||||
|
||||
rv = bitcoind.finalizepsbt(b64encode(psbt).decode('ascii'), extract)
|
||||
|
||||
rv = bitcoind.rpc.finalizepsbt(b64encode(psbt).decode('ascii'), extract)
|
||||
return b64decode(rv.get('psbt', '')), rv.get('hex'), rv['complete']
|
||||
|
||||
return doit
|
||||
|
||||
@pytest.fixture()
|
||||
|
||||
@pytest.fixture
|
||||
def bitcoind_analyze(bitcoind):
|
||||
# Use bitcoind to finalize a PSBT and get out txn
|
||||
|
||||
def doit(psbt):
|
||||
return bitcoind.analyzepsbt(b64encode(psbt).decode('ascii'))
|
||||
return bitcoind.rpc.analyzepsbt(b64encode(psbt).decode('ascii'))
|
||||
|
||||
return doit
|
||||
|
||||
@pytest.fixture()
|
||||
|
||||
@pytest.fixture
|
||||
def bitcoind_decode(bitcoind):
|
||||
# Use bitcoind to finalize a PSBT and get out txn
|
||||
|
||||
def doit(psbt):
|
||||
return bitcoind.decodepsbt(b64encode(psbt).decode('ascii'))
|
||||
return bitcoind.rpc.decodepsbt(b64encode(psbt).decode('ascii'))
|
||||
|
||||
return doit
|
||||
|
||||
@pytest.fixture()
|
||||
|
||||
@pytest.fixture
|
||||
def explora():
|
||||
def doit(*parts):
|
||||
import urllib.request
|
||||
@ -110,66 +204,82 @@ def explora():
|
||||
return doit
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@pytest.fixture
|
||||
def bitcoind_wallet(bitcoind):
|
||||
# Use bitcoind to create a temporary wallet file, and then do cleanup after
|
||||
# - wallet will not have any keys, and is watch-only
|
||||
import os, shutil
|
||||
# Use bitcoind to create a temporary wallet file
|
||||
w_name = 'ckcc-test-wallet-%s' % uuid.uuid4()
|
||||
conn = bitcoind.create_wallet(wallet_name=w_name, disable_private_keys=True, blank=True,
|
||||
passphrase=None, avoid_reuse=False, descriptors=False)
|
||||
return conn
|
||||
|
||||
fname = '/tmp/ckcc-test-wallet-%d' % os.getpid()
|
||||
|
||||
disable_private_keys = True
|
||||
blank = True
|
||||
w = bitcoind.createwallet(fname, disable_private_keys, blank)
|
||||
|
||||
assert w['name'] == fname
|
||||
|
||||
# give them an object they can do API calls w/ rpcwallet filled-in
|
||||
cookie = get_cookie()
|
||||
url = 'http://' + cookie + '@' + URL + '/wallet/' + fname.replace('/', '%2f')
|
||||
#print(url)
|
||||
conn = AuthServiceProxy(url)
|
||||
assert conn.getblockchaininfo()['chain'] == 'test'
|
||||
|
||||
yield conn
|
||||
|
||||
# cleanup
|
||||
bitcoind.unloadwallet(fname)
|
||||
assert fname.startswith('/tmp/ckcc-test-wallet')
|
||||
shutil.rmtree(fname)
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@pytest.fixture
|
||||
def bitcoind_d_wallet(bitcoind):
|
||||
# Use bitcoind to create a temporary DESCRIPTOR-based wallet file, and then do cleanup after
|
||||
# - wallet will not have any keys until a descriptor is added, and is not just watch-only
|
||||
import os, shutil
|
||||
|
||||
fname = '/tmp/ckcc-test-desc-wallet-%d' % os.getpid()
|
||||
|
||||
disable_private_keys = True
|
||||
blank = True
|
||||
password = None
|
||||
avoid_reuse = False
|
||||
descriptors = True
|
||||
w = bitcoind.createwallet(fname, disable_private_keys, blank,
|
||||
password, avoid_reuse, descriptors)
|
||||
|
||||
assert w['name'] == fname
|
||||
|
||||
# give them an object they can do API calls w/ rpcwallet filled-in
|
||||
cookie = get_cookie()
|
||||
url = 'http://' + cookie + '@' + URL + '/wallet/' + fname.replace('/', '%2f')
|
||||
#print(url)
|
||||
conn = AuthServiceProxy(url)
|
||||
assert conn.getblockchaininfo()['chain'] == 'test'
|
||||
|
||||
yield conn
|
||||
|
||||
# cleanup
|
||||
bitcoind.unloadwallet(fname)
|
||||
assert fname.startswith('/tmp/ckcc-test-desc-wallet')
|
||||
shutil.rmtree(fname)
|
||||
# Use bitcoind to create a temporary DESCRIPTOR-based wallet file
|
||||
w_name = 'ckcc-test-desc-wallet-%s' % uuid.uuid4()
|
||||
conn = bitcoind.create_wallet(wallet_name=w_name, disable_private_keys=True, blank=True,
|
||||
passphrase=None, avoid_reuse=False, descriptors=True)
|
||||
return conn
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bitcoind_d_wallet_w_sk(bitcoind):
|
||||
# Use bitcoind to create a temporary DESCRIPTOR-based wallet file
|
||||
w_name = 'ckcc-test-desc-wallet-w-sk-%s' % uuid.uuid4()
|
||||
conn = bitcoind.create_wallet(wallet_name=w_name, disable_private_keys=False, blank=False,
|
||||
passphrase=None, avoid_reuse=False, descriptors=True)
|
||||
return conn
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bitcoind_d_sim(bitcoind):
|
||||
# Use bitcoind to create a clone of simulator wallet
|
||||
w_name = 'ckcc-test-desc-wallet-sim-%s' % uuid.uuid4()
|
||||
conn = bitcoind.create_wallet(wallet_name=w_name, disable_private_keys=False, blank=True,
|
||||
passphrase=None, avoid_reuse=False, descriptors=True)
|
||||
# below is simulator descriptor wallet
|
||||
descriptors = [
|
||||
{
|
||||
"timestamp": "now",
|
||||
"label": "Coldcard 0f056943",
|
||||
"active": True,
|
||||
"desc": "wpkh([0f056943/84h/1h/0h]tprv8fRh8AYC5iQitbbtzwVaUUyXVZh3Y7HxVYSbqzf45eao9SMfEc3MexJx4y6pU1WjjxcEiYArEjhRTSy5mqfXzBtSncTYhKfxQWywcfeqxFE/0/*)#mzg0pna0",
|
||||
"internal": False
|
||||
},
|
||||
{
|
||||
"timestamp": "now",
|
||||
"active": True,
|
||||
"desc": "wpkh([0f056943/84h/1h/0h]tprv8fRh8AYC5iQitbbtzwVaUUyXVZh3Y7HxVYSbqzf45eao9SMfEc3MexJx4y6pU1WjjxcEiYArEjhRTSy5mqfXzBtSncTYhKfxQWywcfeqxFE/1/*)#2kdwuxdh",
|
||||
"internal": True
|
||||
},
|
||||
{
|
||||
"timestamp": "now",
|
||||
"label": "Coldcard 0f056943",
|
||||
"active": True,
|
||||
"desc": "pkh([0f056943/44h/1h/0h]tprv8g2F84LJV3jWVuWyDZB4EwHGwe8esEG8H6Gxn4CCdNgQTrtH7CMywCmwzuMGZjz13sQ9rcCZucCm6i2zigkYGSPUvCzDQxGW8RCy7FpPdrg/0/*)#kjnlnm3v",
|
||||
"internal": False
|
||||
},
|
||||
{
|
||||
"timestamp": "now",
|
||||
"active": True,
|
||||
"desc": "pkh([0f056943/44h/1h/0h]tprv8g2F84LJV3jWVuWyDZB4EwHGwe8esEG8H6Gxn4CCdNgQTrtH7CMywCmwzuMGZjz13sQ9rcCZucCm6i2zigkYGSPUvCzDQxGW8RCy7FpPdrg/1/*)#8xk7wwp5",
|
||||
"internal": True
|
||||
},
|
||||
{
|
||||
"timestamp": "now",
|
||||
"label": "Coldcard 0f056943",
|
||||
"active": True,
|
||||
"desc": "sh(wpkh([0f056943/49h/1h/0h]tprv8fXojhVHnKUsegFf4CXvmhXRGWq8GBzDvxHYQNRDrJJWCyqTrcYi7vdbSn65CHETVPdw4sxc75v23Ev7o8fCePazRf917CMt1C3mjnKV4Jq/0/*))#0qf5gv2y",
|
||||
"internal": False
|
||||
},
|
||||
{
|
||||
"timestamp": "now",
|
||||
"active": True,
|
||||
"desc": "sh(wpkh([0f056943/49h/1h/0h]tprv8fXojhVHnKUsegFf4CXvmhXRGWq8GBzDvxHYQNRDrJJWCyqTrcYi7vdbSn65CHETVPdw4sxc75v23Ev7o8fCePazRf917CMt1C3mjnKV4Jq/1/*))#6p8zsnlm",
|
||||
"internal": True
|
||||
},
|
||||
]
|
||||
conn.importdescriptors(descriptors)
|
||||
return conn
|
||||
|
||||
# EOF
|
||||
|
||||
201
testing/authproxy.py
Normal file
201
testing/authproxy.py
Normal file
@ -0,0 +1,201 @@
|
||||
# Copyright (c) 2011 Jeff Garzik
|
||||
#
|
||||
# Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
|
||||
#
|
||||
# Copyright (c) 2007 Jan-Klaas Kollhof
|
||||
#
|
||||
# This file is part of jsonrpc.
|
||||
#
|
||||
# jsonrpc is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This software is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this software; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""HTTP proxy for opening RPC connection to bitcoind.
|
||||
|
||||
AuthServiceProxy has the following improvements over python-jsonrpc's
|
||||
ServiceProxy class:
|
||||
|
||||
- HTTP connections persist for the life of the AuthServiceProxy object
|
||||
(if server supports HTTP/1.1)
|
||||
- sends protocol 'version', per JSON-RPC 1.1
|
||||
- sends proper, incrementing 'id'
|
||||
- sends Basic HTTP authentication headers
|
||||
- parses all JSON numbers that look like floats as Decimal
|
||||
- uses standard Python json lib
|
||||
"""
|
||||
|
||||
import base64
|
||||
import decimal
|
||||
from http import HTTPStatus
|
||||
import http.client
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
import urllib.parse
|
||||
|
||||
HTTP_TIMEOUT = 30
|
||||
USER_AGENT = "AuthServiceProxy/0.1"
|
||||
|
||||
log = logging.getLogger("BitcoinRPC")
|
||||
|
||||
class JSONRPCException(Exception):
|
||||
def __init__(self, rpc_error, http_status=None):
|
||||
try:
|
||||
errmsg = '%(message)s (%(code)i)' % rpc_error
|
||||
except (KeyError, TypeError):
|
||||
errmsg = ''
|
||||
super().__init__(errmsg)
|
||||
self.error = rpc_error
|
||||
self.http_status = http_status
|
||||
|
||||
|
||||
def EncodeDecimal(o):
|
||||
if isinstance(o, decimal.Decimal):
|
||||
return str(o)
|
||||
raise TypeError(repr(o) + " is not JSON serializable")
|
||||
|
||||
class AuthServiceProxy():
|
||||
__id_count = 0
|
||||
|
||||
# ensure_ascii: escape unicode as \uXXXX, passed to json.dumps
|
||||
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True):
|
||||
self.__service_url = service_url
|
||||
self._service_name = service_name
|
||||
self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
|
||||
self.__url = urllib.parse.urlparse(service_url)
|
||||
user = None if self.__url.username is None else self.__url.username.encode('utf8')
|
||||
passwd = None if self.__url.password is None else self.__url.password.encode('utf8')
|
||||
authpair = user + b':' + passwd
|
||||
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
|
||||
self.timeout = timeout
|
||||
self._set_conn(connection)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
# Python internal stuff
|
||||
raise AttributeError
|
||||
if self._service_name is not None:
|
||||
name = "%s.%s" % (self._service_name, name)
|
||||
return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
|
||||
|
||||
def _request(self, method, path, postdata):
|
||||
'''
|
||||
Do a HTTP request, with retry if we get disconnected (e.g. due to a timeout).
|
||||
This is a workaround for https://bugs.python.org/issue3566 which is fixed in Python 3.5.
|
||||
'''
|
||||
headers = {'Host': self.__url.hostname,
|
||||
'User-Agent': USER_AGENT,
|
||||
'Authorization': self.__auth_header,
|
||||
'Content-type': 'application/json'}
|
||||
if os.name == 'nt':
|
||||
# Windows somehow does not like to re-use connections
|
||||
# TODO: Find out why the connection would disconnect occasionally and make it reusable on Windows
|
||||
self._set_conn()
|
||||
try:
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
except http.client.BadStatusLine as e:
|
||||
if e.line == "''": # if connection was closed, try again
|
||||
self.__conn.close()
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
else:
|
||||
raise
|
||||
except (BrokenPipeError, ConnectionResetError):
|
||||
# Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset
|
||||
# ConnectionResetError happens on FreeBSD with Python 3.4
|
||||
self.__conn.close()
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
|
||||
def get_request(self, *args, **argsn):
|
||||
AuthServiceProxy.__id_count += 1
|
||||
|
||||
log.debug("-{}-> {} {}".format(
|
||||
AuthServiceProxy.__id_count,
|
||||
self._service_name,
|
||||
json.dumps(args or argsn, default=EncodeDecimal, ensure_ascii=self.ensure_ascii),
|
||||
))
|
||||
if args and argsn:
|
||||
raise ValueError('Cannot handle both named and positional arguments')
|
||||
return {'version': '1.1',
|
||||
'method': self._service_name,
|
||||
'params': args or argsn,
|
||||
'id': AuthServiceProxy.__id_count}
|
||||
|
||||
def __call__(self, *args, **argsn):
|
||||
postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
|
||||
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
if response['error'] is not None:
|
||||
raise JSONRPCException(response['error'], status)
|
||||
elif 'result' not in response:
|
||||
raise JSONRPCException({
|
||||
'code': -343, 'message': 'missing JSON-RPC result'}, status)
|
||||
elif status != HTTPStatus.OK:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
|
||||
else:
|
||||
return response['result']
|
||||
|
||||
def batch(self, rpc_call_list):
|
||||
postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
|
||||
log.debug("--> " + postdata)
|
||||
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
if status != HTTPStatus.OK:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
|
||||
return response
|
||||
|
||||
def _get_response(self):
|
||||
req_start_time = time.time()
|
||||
try:
|
||||
http_response = self.__conn.getresponse()
|
||||
except socket.timeout:
|
||||
raise JSONRPCException({
|
||||
'code': -344,
|
||||
'message': '%r RPC took longer than %f seconds. Consider '
|
||||
'using larger timeout for calls that take '
|
||||
'longer to return.' % (self._service_name,
|
||||
self.__conn.timeout)})
|
||||
if http_response is None:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'missing HTTP response from server'})
|
||||
|
||||
content_type = http_response.getheader('Content-Type')
|
||||
if content_type != 'application/json':
|
||||
raise JSONRPCException(
|
||||
{'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)},
|
||||
http_response.status)
|
||||
|
||||
responsedata = http_response.read().decode('utf8')
|
||||
response = json.loads(responsedata, parse_float=decimal.Decimal)
|
||||
elapsed = time.time() - req_start_time
|
||||
if "error" in response and response["error"] is None:
|
||||
log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
|
||||
else:
|
||||
log.debug("<-- [%.6f] %s" % (elapsed, responsedata))
|
||||
return response, http_response.status
|
||||
|
||||
def __truediv__(self, relative_uri):
|
||||
return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
|
||||
|
||||
def _set_conn(self, connection=None):
|
||||
port = 80 if self.__url.port is None else self.__url.port
|
||||
if connection:
|
||||
self.__conn = connection
|
||||
self.timeout = connection.timeout
|
||||
elif self.__url.scheme == 'https':
|
||||
self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=self.timeout)
|
||||
else:
|
||||
self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=self.timeout)
|
||||
@ -1,11 +1,10 @@
|
||||
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
import pytest, glob, time, sys, random, re, ndef
|
||||
from pprint import pprint
|
||||
from ckcc.protocol import CCProtocolPacker, CCProtoError
|
||||
import pytest, time, sys, random, re, ndef
|
||||
from ckcc.protocol import CCProtocolPacker
|
||||
from helpers import B2A, U2SAT, prandom
|
||||
from api import bitcoind, match_key, bitcoind_finalizer, bitcoind_analyze, bitcoind_decode, explora
|
||||
from api import bitcoind_wallet, bitcoind_d_wallet
|
||||
from api import bitcoind_wallet, bitcoind_d_wallet, bitcoind_d_wallet_w_sk, bitcoind_d_sim
|
||||
from binascii import b2a_hex, a2b_hex
|
||||
from constants import *
|
||||
|
||||
@ -257,7 +256,7 @@ def addr_vs_path(master_xpub):
|
||||
hrp, data, enc = bech32_decode(given_addr)
|
||||
assert enc == Encoding.BECH32
|
||||
decoded = convertbits(data[1:], 5, 8, False)
|
||||
assert hrp in {'tb', 'bc' }
|
||||
assert hrp in {'tb', 'bc' , 'bcrt'}
|
||||
assert bytes(decoded[-20:]) == pkh
|
||||
else:
|
||||
assert addr_fmt == AF_P2WPKH_P2SH
|
||||
@ -276,7 +275,7 @@ def addr_vs_path(master_xpub):
|
||||
elif addr_fmt == AF_P2WSH:
|
||||
hrp, data, enc = bech32_decode(given_addr)
|
||||
assert enc == Encoding.BECH32
|
||||
assert hrp in {'tb', 'bc' }
|
||||
assert hrp in {'tb', 'bc' , 'bcrt'}
|
||||
decoded = convertbits(data[1:], 5, 8, False)
|
||||
assert bytes(decoded[-32:]) == sha256(script).digest()
|
||||
|
||||
@ -669,6 +668,15 @@ def use_mainnet(settings_set):
|
||||
yield doit
|
||||
settings_set('chain', 'XTN')
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def use_regtest(settings_set):
|
||||
def doit():
|
||||
settings_set('chain', 'XRT')
|
||||
yield doit
|
||||
settings_set('chain', 'XTN')
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def set_seed_words(sim_exec, sim_execfile, simulator, reset_seed_words):
|
||||
# load simulator w/ a specific bip32 master key
|
||||
@ -870,7 +878,7 @@ def decode_with_bitcoind(bitcoind):
|
||||
# verify our understanding of a TXN (and esp its outputs) matches
|
||||
# the same values as what bitcoind generates
|
||||
try:
|
||||
return bitcoind.decoderawtransaction(B2A(raw_txn))
|
||||
return bitcoind.rpc.decoderawtransaction(B2A(raw_txn))
|
||||
except ConnectionResetError:
|
||||
# bitcoind sleeps on us sometimes, give it another chance.
|
||||
return bitcoind.decoderawtransaction(B2A(raw_txn))
|
||||
@ -893,17 +901,17 @@ def decode_psbt_with_bitcoind(bitcoind):
|
||||
return doit
|
||||
|
||||
@pytest.fixture()
|
||||
def check_against_bitcoind(bitcoind, sim_exec, sim_execfile):
|
||||
def check_against_bitcoind(bitcoind, use_regtest, sim_exec, sim_execfile):
|
||||
|
||||
def doit(hex_txn, fee, num_warn=0, change_outs=None, dests=[]):
|
||||
# verify our understanding of a TXN (and esp its outputs) matches
|
||||
# the same values as what bitcoind generates
|
||||
|
||||
try:
|
||||
decode = bitcoind.decoderawtransaction(hex_txn)
|
||||
decode = bitcoind.rpc.decoderawtransaction(hex_txn)
|
||||
except ConnectionResetError:
|
||||
# bitcoind sleeps on us sometimes, give it another chance.
|
||||
decode = bitcoind.decoderawtransaction(hex_txn)
|
||||
decode = bitcoind.rpc.decoderawtransaction(hex_txn)
|
||||
|
||||
#print("Bitcoin code says:", end=''); pprint(decode)
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ simulator_fixed_xfp = 0x4369050f
|
||||
|
||||
simulator_serial_number = 'F1F1F1F1F1F1'
|
||||
|
||||
from ckcc_protocol.constants import AF_P2WSH, AFC_SCRIPT, AF_P2SH, AF_P2WSH_P2SH
|
||||
from ckcc_protocol.constants import AF_P2WSH, AF_P2SH, AF_P2WSH_P2SH
|
||||
|
||||
unmap_addr_fmt = {
|
||||
'p2sh': AF_P2SH,
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#
|
||||
# unit test for address decoding for multisig
|
||||
from h import a2b_hex, b2a_hex
|
||||
from chains import BitcoinMain, BitcoinTestnet
|
||||
from chains import BitcoinMain, BitcoinTestnet, BitcoinRegtest
|
||||
from multisig import disassemble_multisig
|
||||
from public_constants import AF_CLASSIC, AF_P2SH, AF_P2WPKH, AF_P2WSH, AF_P2WPKH_P2SH, AF_P2WSH_P2SH
|
||||
from public_constants import AFC_PUBKEY, AFC_SEGWIT, AFC_BECH32, AFC_SCRIPT, AFC_WRAPPED
|
||||
@ -27,10 +27,15 @@ if 1:
|
||||
addr = BitcoinMain.p2sh_address(AF_P2SH, script)
|
||||
assert addr[0] == '3'
|
||||
assert addr == '3Kt6KxjirrFS7GexJiXLLhmuaMzSbjp275'
|
||||
|
||||
addr = BitcoinTestnet.p2sh_address(AF_P2SH, script)
|
||||
assert addr[0] == '2'
|
||||
assert addr == '2NBSJPhfkUJknK4HVyr9CxemAniCcRfhqp4'
|
||||
|
||||
addr = BitcoinRegtest.p2sh_address(AF_P2SH, script)
|
||||
assert addr[0] == '2'
|
||||
assert addr == '2NBSJPhfkUJknK4HVyr9CxemAniCcRfhqp4'
|
||||
|
||||
addr = BitcoinMain.p2sh_address(AF_P2WSH, script)
|
||||
assert addr[0:4] == 'bc1q', addr
|
||||
assert len(addr) >= 62
|
||||
@ -41,6 +46,12 @@ if 1:
|
||||
assert len(addr) >= 62
|
||||
assert addr == 'tb1qnjw7wy4e9tf4kkqaf43n2cyjwug0ystugum08c5j5hwhfncc4mkq7r26gv'
|
||||
|
||||
addr = BitcoinRegtest.p2sh_address(AF_P2WSH, script)
|
||||
print(addr)
|
||||
assert addr[0:6] == 'bcrt1q', addr
|
||||
assert len(addr) >= 64
|
||||
assert addr == 'bcrt1qnjw7wy4e9tf4kkqaf43n2cyjwug0ystugum08c5j5hwhfncc4mkqn6quak'
|
||||
|
||||
|
||||
if 1:
|
||||
from utils import xfp2str, str2xfp
|
||||
|
||||
@ -118,7 +118,7 @@ def parse_change_back(story):
|
||||
lines = story.split('\n')
|
||||
s = lines.index('Change back:')
|
||||
assert s > 3
|
||||
assert 'XTN' in lines[s+1] or 'BTC' in lines[s+1]
|
||||
assert 'XTN' in lines[s+1] or 'XRT' in lines[s+1] or 'BTC' in lines[s+1]
|
||||
val = Decimal(lines[s+1].split()[0])
|
||||
assert 'address' in lines[s+2]
|
||||
addrs = []
|
||||
|
||||
@ -230,7 +230,7 @@ class BasicPSBT:
|
||||
raw = a2b_hex(raw.strip())
|
||||
if raw[0:6] == b'cHNidP':
|
||||
raw = b64decode(raw)
|
||||
assert raw[0:5] == b'psbt\xff', "bad magic"
|
||||
assert raw[0:5] == b'psbt\xff', "bad magic {}".format(raw[0:5])
|
||||
|
||||
with io.BytesIO(raw[5:]) as fd:
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
# for testing (only)
|
||||
pytest==6.2.5
|
||||
pycoin==0.80
|
||||
python-bitcoinrpc>=1.0
|
||||
pyserial
|
||||
mnemonic==0.18
|
||||
onetimepass==1.0.1
|
||||
|
||||
@ -56,25 +56,19 @@ def test_show_addr_displayed(dev, need_keypress, addr_vs_path, path, addr_fmt, c
|
||||
assert qr == addr or qr == addr.upper()
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('example_addr', [
|
||||
'2N2VBntgcoY4wN7H6VfrhH8an1BwieRMZCF', '2N551pf65tPS7VthC1rvwFDbLA1EUDYkTg9'])
|
||||
def test_addr_vs_bitcoind(bitcoind, match_key, need_keypress, example_addr, dev):
|
||||
def test_addr_vs_bitcoind(use_regtest, match_key, need_keypress, dev, bitcoind_d_sim):
|
||||
# check our p2wpkh wrapped in p2sh is right
|
||||
|
||||
# PROBLEM: your bitcoind probably needs same transaction history as mine, so it knows
|
||||
# about this address and its contents/key path.
|
||||
use_regtest()
|
||||
for i in range(5):
|
||||
core_addr = bitcoind_d_sim.getnewaddress(f"{i}-addr", "p2sh-segwit")
|
||||
assert core_addr[0] == '2'
|
||||
resp = bitcoind_d_sim.getaddressinfo(core_addr)
|
||||
assert resp['embedded']['iswitness'] == True
|
||||
assert resp['isscript'] == True
|
||||
path = resp['hdkeypath']
|
||||
|
||||
assert example_addr[0] == '2'
|
||||
resp = bitcoind.getaddressinfo(example_addr)
|
||||
|
||||
assert resp['embedded']['iswitness'] == True
|
||||
assert resp['isscript'] == True
|
||||
path = resp['hdkeypath']
|
||||
|
||||
addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2WPKH_P2SH), timeout=None)
|
||||
need_keypress('y')
|
||||
|
||||
assert addr == example_addr
|
||||
|
||||
addr = dev.send_recv(CCProtocolPacker.show_address(path, AF_P2WPKH_P2SH), timeout=None)
|
||||
need_keypress('y')
|
||||
assert addr == core_addr
|
||||
|
||||
# EOF
|
||||
|
||||
@ -17,11 +17,11 @@ from ckcc_protocol.constants import AF_CLASSIC, AF_P2WPKH, AF_P2WSH_P2SH
|
||||
from pprint import pprint
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('acct_num', [ None, '0', '99', '123'])
|
||||
def test_export_core(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, bitcoind_wallet, bitcoind_d_wallet, enter_number):
|
||||
@pytest.mark.parametrize('acct_num', [None, '0', '99', '123'])
|
||||
def test_export_core(dev, use_regtest, acct_num, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, bitcoind_wallet, bitcoind_d_wallet, enter_number):
|
||||
# test UX and operation of the 'bitcoin core' wallet export
|
||||
from pycoin.contrib.segwit_addr import encode as sw_encode
|
||||
|
||||
use_regtest()
|
||||
goto_home()
|
||||
pick_menu_item('Advanced/Tools')
|
||||
pick_menu_item('File Management')
|
||||
@ -71,10 +71,10 @@ def test_export_core(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_sto
|
||||
elif '=>' in ln:
|
||||
path, addr = ln.strip().split(' => ', 1)
|
||||
assert path.startswith(f"m/84'/1'/{acct_num}'/0")
|
||||
assert addr.startswith('tb1q')
|
||||
assert addr.startswith('bcrt1q') # TODO here we should differentiate if testnet or smthg
|
||||
sk = BIP32Node.from_wallet_key(simulator_fixed_xprv).subkey_for_path(path[2:])
|
||||
h20 = sk.hash160()
|
||||
assert addr == sw_encode(addr[0:2], 0, h20)
|
||||
assert addr == sw_encode(addr[0:4], 0, h20) # TODO here we should differentiate if testnet or smthg
|
||||
addrs.append(addr)
|
||||
|
||||
assert len(addrs) == 3
|
||||
@ -147,9 +147,9 @@ def test_export_core(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_sto
|
||||
assert x['address'] == addrs[-1]
|
||||
assert x['iswatchonly'] == False
|
||||
assert x['iswitness'] == True
|
||||
assert x['ismine'] == True
|
||||
assert x['solvable'] == True
|
||||
assert x['hdmasterfingerprint'] == xfp2str(dev.master_fingerprint).lower()
|
||||
# assert x['ismine'] == True # TODO we have imported pubkeys - it has no idea if it is ours or solvable
|
||||
# assert x['solvable'] == True
|
||||
# assert x['hdmasterfingerprint'] == xfp2str(dev.master_fingerprint).lower()
|
||||
#assert x['hdkeypath'] == f"m/84'/1'/{acct_num}'/0/%d" % (len(addrs)-1)
|
||||
|
||||
@pytest.mark.parametrize('use_nfc', [False, True])
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#
|
||||
# py.test test_multisig.py -m ms_danger --ms-danger
|
||||
#
|
||||
import base64
|
||||
import time, pytest, os, random, json, shutil, pdb
|
||||
from psbt import BasicPSBT, BasicPSBTInput, BasicPSBTOutput, PSBT_IN_REDEEM_SCRIPT
|
||||
from ckcc.protocol import CCProtocolPacker, CCProtoError, MAX_TXN_LEN, CCUserRefused
|
||||
@ -71,10 +72,10 @@ def bitcoind_p2sh(bitcoind):
|
||||
}[fmt]
|
||||
|
||||
try:
|
||||
rv = bitcoind.createmultisig(M, [B2A(i) for i in pubkeys], fmt)
|
||||
rv = bitcoind.rpc.createmultisig(M, [B2A(i) for i in pubkeys], fmt)
|
||||
except ConnectionResetError:
|
||||
# bitcoind sleeps on us sometimes, give it another chance.
|
||||
rv = bitcoind.createmultisig(M, [B2A(i) for i in pubkeys], fmt)
|
||||
rv = bitcoind.rpc.createmultisig(M, [B2A(i) for i in pubkeys], fmt)
|
||||
|
||||
return rv['address'], rv['redeemScript']
|
||||
|
||||
@ -410,8 +411,8 @@ def test_ms_show_addr(dev, cap_story, need_keypress, addr_vs_path, bitcoind_p2sh
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('m_of_n', [(1,3), (2,3), (3,3), (3,6), (10, 15), (15,15)])
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ])
|
||||
def test_import_ranges(m_of_n, addr_fmt, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
|
||||
def test_import_ranges(m_of_n, use_regtest, addr_fmt, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
use_regtest()
|
||||
M, N = m_of_n
|
||||
|
||||
keys = import_ms_wallet(M, N, addr_fmt, accept=1)
|
||||
@ -428,7 +429,7 @@ def test_import_ranges(m_of_n, addr_fmt, clear_ms, import_ms_wallet, need_keypre
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.ms_danger
|
||||
def test_violate_bip67(clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr, has_ms_checks):
|
||||
def test_violate_bip67(clear_ms, use_regtest, import_ms_wallet, need_keypress, test_ms_show_addr, has_ms_checks):
|
||||
# detect when pubkeys are not in order in the redeem script
|
||||
M, N = 1, 15
|
||||
|
||||
@ -446,7 +447,7 @@ def test_violate_bip67(clear_ms, import_ms_wallet, need_keypress, test_ms_show_a
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('which_pubkey', [0, 1, 14])
|
||||
def test_bad_pubkey(has_ms_checks, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr, which_pubkey):
|
||||
def test_bad_pubkey(has_ms_checks, use_regtest, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr, which_pubkey):
|
||||
# give incorrect pubkey inside redeem script
|
||||
M, N = 1, 15
|
||||
keys = import_ms_wallet(M, N, accept=1)
|
||||
@ -467,7 +468,7 @@ def test_bad_pubkey(has_ms_checks, clear_ms, import_ms_wallet, need_keypress, te
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ])
|
||||
def test_zero_depth(clear_ms, addr_fmt, import_ms_wallet, need_keypress, test_ms_show_addr, make_multisig):
|
||||
def test_zero_depth(clear_ms, use_regtest, addr_fmt, import_ms_wallet, need_keypress, test_ms_show_addr, make_multisig):
|
||||
# test having a co-signer with "m" only key ... ie. depth=0
|
||||
|
||||
M, N = 1, 2
|
||||
@ -493,7 +494,7 @@ def test_zero_depth(clear_ms, addr_fmt, import_ms_wallet, need_keypress, test_ms
|
||||
@pytest.mark.parametrize('mode', ['wrong-xfp', 'long-path', 'short-path', 'zero-path'])
|
||||
@pytest.mark.ms_danger
|
||||
@pytest.mark.bitcoind
|
||||
def test_bad_xfp(mode, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr, has_ms_checks, request):
|
||||
def test_bad_xfp(mode, clear_ms, use_regtest, import_ms_wallet, need_keypress, test_ms_show_addr, has_ms_checks, request):
|
||||
# give incorrect xfp+path args during show_address
|
||||
|
||||
if has_ms_checks and (mode in {'zero-path', 'wrong-xfp'}):
|
||||
@ -541,7 +542,7 @@ def test_bad_xfp(mode, clear_ms, import_ms_wallet, need_keypress, test_ms_show_a
|
||||
"m/1/2/3/4/5/6/7/8/9/10/11/12/13", # assuming MAX_PATH_DEPTH==12
|
||||
])
|
||||
@pytest.mark.bitcoind
|
||||
def test_bad_common_prefix(cpp, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
def test_bad_common_prefix(cpp, use_regtest, clear_ms, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
# give some incorrect path values as the common prefix derivation
|
||||
|
||||
M, N = 1, 15
|
||||
@ -942,7 +943,7 @@ def test_import_dup_diff_xpub(N, clear_ms, make_multisig, offer_ms_import, need_
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('m_of_n', [(2,2), (2,3), (15,15)])
|
||||
@pytest.mark.parametrize('addr_fmt', ['p2sh-p2wsh', 'p2sh', 'p2wsh' ])
|
||||
def test_import_dup_xfp_fails(m_of_n, addr_fmt, clear_ms, make_multisig, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
def test_import_dup_xfp_fails(m_of_n, use_regtest, addr_fmt, clear_ms, make_multisig, import_ms_wallet, need_keypress, test_ms_show_addr):
|
||||
|
||||
M, N = m_of_n
|
||||
|
||||
@ -1222,7 +1223,7 @@ def test_ms_sign_simple(N, num_ins, dev, addr_fmt, clear_ms, incl_xpubs, import_
|
||||
@pytest.mark.parametrize('M', [ 2, 4, 1])
|
||||
@pytest.mark.parametrize('segwit', [True, False])
|
||||
@pytest.mark.parametrize('incl_xpubs', [ True, False ])
|
||||
def test_ms_sign_myself(M, make_myself_wallet, segwit, num_ins, dev, clear_ms,
|
||||
def test_ms_sign_myself(M, use_regtest, make_myself_wallet, segwit, num_ins, dev, clear_ms,
|
||||
fake_ms_txn, try_sign, bitcoind_finalizer, incl_xpubs, bitcoind_analyze, bitcoind_decode):
|
||||
|
||||
# IMPORTANT: wont work if you start simulator with --ms flag. Use no args
|
||||
@ -1231,6 +1232,7 @@ def test_ms_sign_myself(M, make_myself_wallet, segwit, num_ins, dev, clear_ms,
|
||||
num_outs = len(all_out_styles)
|
||||
|
||||
clear_ms()
|
||||
use_regtest()
|
||||
|
||||
# create a wallet, with 3 bip39 pw's
|
||||
keys, select_wallet = make_myself_wallet(M, do_import=(not incl_xpubs))
|
||||
@ -1240,14 +1242,14 @@ def test_ms_sign_myself(M, make_myself_wallet, segwit, num_ins, dev, clear_ms,
|
||||
psbt = fake_ms_txn(num_ins, num_outs, M, keys, segwit_in=segwit, incl_xpubs=incl_xpubs,
|
||||
outstyles=all_out_styles, change_outputs=list(range(1,num_outs)))
|
||||
|
||||
open(f'debug/myself-before.psbt', 'wb').write(psbt)
|
||||
open(f'debug/myself-before.psbt', 'w').write(base64.b64encode(psbt).decode())
|
||||
for idx in range(M):
|
||||
select_wallet(idx)
|
||||
_, updated = try_sign(psbt, accept_ms_import=(incl_xpubs and (idx==0)))
|
||||
open(f'debug/myself-after.psbt', 'wb').write(updated)
|
||||
open(f'debug/myself-after.psbt', 'w').write(base64.b64encode(updated).decode())
|
||||
assert updated != psbt
|
||||
|
||||
aft = BasicPSBT().parse(updated)
|
||||
aft = BasicPSBT().parse(updated) # TODO something is off here xpub is longer than 79 - core returs error
|
||||
|
||||
# check all inputs gained a signature
|
||||
assert all(len(i.part_sigs)==(idx+1) for i in aft.inputs)
|
||||
@ -1255,15 +1257,14 @@ def test_ms_sign_myself(M, make_myself_wallet, segwit, num_ins, dev, clear_ms,
|
||||
psbt = updated
|
||||
|
||||
# should be fully signed now.
|
||||
anal = bitcoind_analyze(aft.as_bytes())
|
||||
|
||||
anal = bitcoind_analyze(psbt)
|
||||
try:
|
||||
assert not any(inp.get('missing') for inp in anal['inputs']), "missing sigs: %r" % anal
|
||||
assert all(inp['next'] in {'finalizer','updater'} for inp in anal['inputs']), "other issue: %r" % anal
|
||||
except:
|
||||
# XXX seems to be a bug in analyzepsbt function ... not fully studied
|
||||
pprint(anal, stream=open('debug/analyzed.txt', 'wt'))
|
||||
decode = bitcoind_decode(aft.as_bytes())
|
||||
decode = bitcoind_decode(psbt)
|
||||
pprint(decode, stream=open('debug/decoded.txt', 'wt'))
|
||||
|
||||
if M==N or segwit:
|
||||
@ -1273,7 +1274,7 @@ def test_ms_sign_myself(M, make_myself_wallet, segwit, num_ins, dev, clear_ms,
|
||||
print("ignoring bug in bitcoind")
|
||||
|
||||
if 0:
|
||||
# why doesn't this work?
|
||||
# why doesn't this work? # TODO produced PSBT is invalid, cannot finalize (both core and us)
|
||||
extracted_psbt, txn, is_complete = bitcoind_finalizer(aft.as_bytes(), extract=True)
|
||||
|
||||
ex = BasicPSBT().parse(extracted_psbt)
|
||||
@ -1447,7 +1448,7 @@ def test_make_airgapped(addr_fmt, acct_num, goto_home, cap_story, pick_menu_item
|
||||
@pytest.mark.unfinalized
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('addr_style', ["legacy", "p2sh-segwit", "bech32"])
|
||||
def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora, try_sign, need_keypress, addr_style):
|
||||
def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora, try_sign, need_keypress, addr_style, use_regtest):
|
||||
# Make a P2SH wallet with local bitcoind as a co-signer (and simulator)
|
||||
# - send an receive various
|
||||
# - following text of <https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md>
|
||||
@ -1455,8 +1456,7 @@ def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora,
|
||||
# - before starting this test, have some funds already deposited to bitcoind testnet wallet
|
||||
from pycoin.encoding import sec_to_public_pair
|
||||
from binascii import a2b_hex
|
||||
import re
|
||||
|
||||
use_regtest()
|
||||
if addr_style == 'legacy':
|
||||
addr_fmt = AF_P2SH
|
||||
elif addr_style == 'p2sh-segwit':
|
||||
@ -1464,13 +1464,10 @@ def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora,
|
||||
elif addr_style == 'bech32':
|
||||
addr_fmt = AF_P2WSH
|
||||
|
||||
try:
|
||||
addr, = bitcoind.getaddressesbylabel("sim-cosign").keys()
|
||||
except:
|
||||
addr = bitcoind.getnewaddress("sim-cosign")
|
||||
|
||||
info = bitcoind.getaddressinfo(addr)
|
||||
#pprint(info)
|
||||
addr = bitcoind.supply_wallet.getnewaddress("sim-cosign")
|
||||
|
||||
info = bitcoind.supply_wallet.getaddressinfo(addr)
|
||||
|
||||
assert info['address'] == addr
|
||||
bc_xfp = swab32(int(info['hdmasterfingerprint'], 16))
|
||||
@ -1500,7 +1497,7 @@ def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora,
|
||||
|
||||
|
||||
# NOTE: bitcoind doesn't seem to implement pubkey sorting. We have to do it.
|
||||
resp = bitcoind.addmultisigaddress(M, list(sorted([cc_pubkey, bc_pubkey])),
|
||||
resp = bitcoind.supply_wallet.addmultisigaddress(M, list(sorted([cc_pubkey, bc_pubkey])),
|
||||
'shared-addr-'+addr_style, addr_style)
|
||||
ms_addr = resp['address']
|
||||
bc_redeem = a2b_hex(resp['redeemScript'])
|
||||
@ -1529,42 +1526,12 @@ def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora,
|
||||
'2N1hZJ5mazTX524GQTPKkCT4UFZn5Fqwdz6',
|
||||
'tb1qpcv2rkc003p5v8lrglrr6lhz2jg8g4qa9vgtrgkt0p5rteae5xtqn6njw9')
|
||||
|
||||
# Need some UTXO to sign
|
||||
#
|
||||
# - but bitcoind can't give me that (using listunspent) because it's only a watched addr??
|
||||
#
|
||||
did_fund = False
|
||||
while 1:
|
||||
rr = explora('address', ms_addr, 'utxo')
|
||||
pprint(rr)
|
||||
|
||||
avail = []
|
||||
amt = 0
|
||||
for i in rr:
|
||||
txn = i['txid']
|
||||
vout = i['vout']
|
||||
avail.append( (txn, vout) )
|
||||
amt += i['value']
|
||||
|
||||
# just use first UTXO available; save other for later tests
|
||||
break
|
||||
|
||||
else:
|
||||
# doesn't need to confirm, but does need to reach public testnet/blockstream
|
||||
assert not amt and not avail
|
||||
|
||||
if not did_fund:
|
||||
print(f"Sending some XTN to {ms_addr} (wait)")
|
||||
bitcoind.sendtoaddress(ms_addr, 0.0001, 'fund testing')
|
||||
did_fund = True
|
||||
else:
|
||||
print(f"Still waiting ...")
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
if amt: break
|
||||
|
||||
ret_addr = bitcoind.getrawchangeaddress()
|
||||
# fund multisig address
|
||||
bitcoind.supply_wallet.importaddress(ms_addr, 'shared-addr-'+addr_style, True)
|
||||
bitcoind.supply_wallet.sendtoaddress(address=ms_addr, amount=5)
|
||||
bitcoind.supply_wallet.generatetoaddress(101, bitcoind.supply_wallet.getnewaddress()) # mining
|
||||
unspent = bitcoind.supply_wallet.listunspent(addresses=[ms_addr])
|
||||
ret_addr = bitcoind.supply_wallet.getrawchangeaddress()
|
||||
|
||||
''' If you get insufficent funds, even tho we provide the UTXO (!!), do this:
|
||||
|
||||
@ -1574,11 +1541,13 @@ def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora,
|
||||
got from non-multisig to multisig on same bitcoin-qt instance).
|
||||
-> Now doing that, automated, above.
|
||||
'''
|
||||
resp = bitcoind.walletcreatefundedpsbt([dict(txid=t, vout=o) for t,o in avail],
|
||||
[{ret_addr: amt/1E8}], 0,
|
||||
resp = bitcoind.supply_wallet.walletcreatefundedpsbt([dict(txid=unspent[0]["txid"], vout=unspent[0]["vout"])],
|
||||
[{ret_addr: 2}], 0,
|
||||
{'subtractFeeFromOutputs': [0], 'includeWatching': True}, True)
|
||||
|
||||
assert resp['changepos'] == -1
|
||||
resp = bitcoind.supply_wallet.walletprocesspsbt(resp["psbt"])
|
||||
|
||||
# assert resp['changepos'] == -1
|
||||
psbt = b64decode(resp['psbt'])
|
||||
|
||||
open('debug/funded.psbt', 'wb').write(psbt)
|
||||
@ -1598,19 +1567,21 @@ def test_bitcoind_cosigning(dev, bitcoind, import_ms_wallet, clear_ms, explora,
|
||||
|
||||
open('debug/cc-updated.psbt', 'wb').write(updated)
|
||||
|
||||
# have bitcoind do the rest of the signing
|
||||
rr = bitcoind.walletprocesspsbt(b64encode(updated).decode('ascii'))
|
||||
pprint(rr)
|
||||
|
||||
open('debug/bc-processed.psbt', 'wt').write(rr['psbt'])
|
||||
assert rr['complete']
|
||||
# # have bitcoind do the rest of the signing
|
||||
# rr = bitcoind.supply_wallet.walletprocesspsbt(b64encode(updated).decode('ascii'))
|
||||
# pprint(rr)
|
||||
#
|
||||
# open('debug/bc-processed.psbt', 'wt').write(rr['psbt'])
|
||||
# assert rr['complete']
|
||||
# TODO I have moved this up - so that bitcoind signs first, if it signed second it failed with
|
||||
# TODO "Specified sighash value does not match value stored in PSBT"
|
||||
|
||||
# finalize and send
|
||||
rr = bitcoind.finalizepsbt(rr['psbt'], True)
|
||||
rr = bitcoind.supply_wallet.finalizepsbt(b64encode(updated).decode('ascii'), True)
|
||||
open('debug/bc-final-txn.txn', 'wt').write(rr['hex'])
|
||||
assert rr['complete']
|
||||
|
||||
txn_id = bitcoind.sendrawtransaction(rr['hex'])
|
||||
txn_id = bitcoind.supply_wallet.sendrawtransaction(rr['hex'])
|
||||
print(txn_id)
|
||||
|
||||
@pytest.mark.parametrize('addr_fmt', [AF_P2WSH] )
|
||||
|
||||
@ -95,7 +95,7 @@ def test_generate(mode, pdf, dev, cap_menu, pick_menu_item, goto_home, cap_story
|
||||
addr = Key.from_text(val)
|
||||
else:
|
||||
hrp, data, enc = bech32_decode(val)
|
||||
assert hrp in {'tb', 'bc' }
|
||||
assert hrp in {'tb', 'bc', 'bcrt'}
|
||||
assert enc == Encoding.BECH32
|
||||
decoded = convertbits(data[1:], 5, 8, False)[-20:]
|
||||
addr = Key(hash160=bytes(decoded), is_compressed=True, netcode='XTN')
|
||||
|
||||
@ -184,9 +184,10 @@ if 0:
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.veryslow
|
||||
@pytest.mark.parametrize('segwit', [True, False])
|
||||
@pytest.mark.parametrize('out_style', ADDR_STYLES)
|
||||
def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3, is_mark4,
|
||||
def test_io_size(request, use_regtest, decode_with_bitcoind, fake_txn, is_mark3, is_mark4,
|
||||
start_sign, end_sign, dev, segwit, out_style, accept = True):
|
||||
|
||||
# try a bunch of different bigger sized txns
|
||||
@ -198,6 +199,9 @@ def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3, is_mark4,
|
||||
# - only mk3 can do full amounts
|
||||
# - time on mk3, v4.0.0 firmware: 13 minutes
|
||||
|
||||
# for this test you need to configure core `repcservertimeout` to something big
|
||||
# in bitcoin.conf `rpcservertimeout=2000` should do the trick
|
||||
use_regtest()
|
||||
num_in = 10
|
||||
num_out = 10
|
||||
|
||||
@ -261,7 +265,7 @@ def test_io_size(request, decode_with_bitcoind, fake_txn, is_mark3, is_mark4,
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.parametrize('num_ins', [ 2, 7, 15 ])
|
||||
@pytest.mark.parametrize('segwit', [True, False])
|
||||
def test_real_signing(fake_txn, try_sign, dev, num_ins, segwit, decode_with_bitcoind):
|
||||
def test_real_signing(fake_txn, use_regtest, try_sign, dev, num_ins, segwit, decode_with_bitcoind):
|
||||
# create a TXN using actual addresses that are correct for DUT
|
||||
xp = dev.master_xpub
|
||||
|
||||
@ -285,11 +289,11 @@ def test_real_signing(fake_txn, try_sign, dev, num_ins, segwit, decode_with_bitc
|
||||
@pytest.mark.parametrize('we_finalize', [ False, True ])
|
||||
@pytest.mark.parametrize('num_dests', [ 1, 10, 25 ])
|
||||
@pytest.mark.bitcoind
|
||||
def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, end_sign, we_finalize, num_dests):
|
||||
def test_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, start_sign, end_sign, we_finalize, num_dests):
|
||||
|
||||
wallet_xfp = match_key()
|
||||
|
||||
bal = bitcoind.getbalance()
|
||||
use_regtest()
|
||||
bal = bitcoind.supply_wallet.getbalance()
|
||||
assert bal > 0, "need some play money; drink from a faucet"
|
||||
|
||||
amt = round((bal/4)/num_dests, 6)
|
||||
@ -297,8 +301,8 @@ def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, en
|
||||
args = {}
|
||||
|
||||
for no in range(num_dests):
|
||||
dest = bitcoind.getrawchangeaddress()
|
||||
assert dest[0] in '2mn' or dest.startswith('tb1'), dest
|
||||
dest = bitcoind.supply_wallet.getrawchangeaddress()
|
||||
assert dest.startswith('bcrt1'), dest
|
||||
|
||||
args[dest] = amt
|
||||
|
||||
@ -306,11 +310,11 @@ def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, en
|
||||
# old approach: fundraw + convert to psbt
|
||||
|
||||
# working with hex strings here
|
||||
txn = bitcoind.createrawtransaction([], args)
|
||||
txn = bitcoind.supply_wallet.createrawtransaction([], args)
|
||||
assert txn[0:2] == '02'
|
||||
#print(txn)
|
||||
|
||||
resp = bitcoind.fundrawtransaction(txn)
|
||||
resp = bitcoind.supply_wallet.fundrawtransaction(txn)
|
||||
txn2 = resp['hex']
|
||||
fee = resp['fee']
|
||||
chg_pos = resp['changepos']
|
||||
@ -318,11 +322,11 @@ def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, en
|
||||
|
||||
print("Sending %.8f XTN to %s (Change back in position: %d)" % (amt, dest, chg_pos))
|
||||
|
||||
psbt = b64decode(bitcoind.converttopsbt(txn2, True))
|
||||
psbt = b64decode(bitcoind.supply_wallet.converttopsbt(txn2, True))
|
||||
|
||||
# use walletcreatefundedpsbt
|
||||
# - updated/validated against 0.17.1
|
||||
resp = bitcoind.walletcreatefundedpsbt([], args, 0, {
|
||||
resp = bitcoind.supply_wallet.walletcreatefundedpsbt([], args, 0, {
|
||||
'subtractFeeFromOutputs': list(range(num_dests)),
|
||||
'feeRate': 0.00001500}, True)
|
||||
|
||||
@ -370,7 +374,7 @@ def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, en
|
||||
assert b4 != aft, "signing didn't change anything?"
|
||||
|
||||
open('debug/signed.psbt', 'wb').write(signed)
|
||||
resp = bitcoind.finalizepsbt(str(b64encode(signed), 'ascii'), True)
|
||||
resp = bitcoind.supply_wallet.finalizepsbt(str(b64encode(signed), 'ascii'), True)
|
||||
|
||||
#combined_psbt = b64decode(resp['psbt'])
|
||||
#open('debug/combined.psbt', 'wb').write(combined_psbt)
|
||||
@ -384,7 +388,7 @@ def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, en
|
||||
open('debug/finalized-by-btcd.txn', 'wb').write(network)
|
||||
|
||||
# try to send it
|
||||
txed = bitcoind.sendrawtransaction(B2A(network))
|
||||
txed = bitcoind.supply_wallet.sendrawtransaction(B2A(network))
|
||||
print("Final txn hash: %r" % txed)
|
||||
|
||||
else:
|
||||
@ -392,7 +396,7 @@ def test_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, en
|
||||
#print("Final txn: %s" % B2A(signed))
|
||||
open('debug/finalized-by-cc.txn', 'wb').write(signed)
|
||||
|
||||
txed = bitcoind.sendrawtransaction(B2A(signed))
|
||||
txed = bitcoind.supply_wallet.sendrawtransaction(B2A(signed))
|
||||
print("Final txn hash: %r" % txed)
|
||||
|
||||
def test_sign_example(set_master_key, sim_execfile, start_sign, end_sign):
|
||||
@ -422,7 +426,7 @@ def test_sign_example(set_master_key, sim_execfile, start_sign, end_sign):
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.unfinalized
|
||||
def test_sign_p2sh_p2wpkh(match_key, start_sign, end_sign, bitcoind):
|
||||
def test_sign_p2sh_p2wpkh(match_key, use_regtest, start_sign, end_sign, bitcoind):
|
||||
# Check we can finalize p2sh_p2wpkh inputs right.
|
||||
|
||||
# TODO fix this
|
||||
@ -447,7 +451,7 @@ def test_sign_p2sh_p2wpkh(match_key, start_sign, end_sign, bitcoind):
|
||||
|
||||
# use bitcoind to combine
|
||||
open('debug/signed.psbt', 'wb').write(signed_psbt)
|
||||
resp = bitcoind.finalizepsbt(str(b64encode(signed_psbt), 'ascii'), True)
|
||||
resp = bitcoind.rpc.finalizepsbt(str(b64encode(signed_psbt), 'ascii'), True)
|
||||
|
||||
assert resp['complete'] == True, "bitcoind wasn't able to finalize it"
|
||||
network = a2b_hex(resp['hex'])
|
||||
@ -458,7 +462,7 @@ def test_sign_p2sh_p2wpkh(match_key, start_sign, end_sign, bitcoind):
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.unfinalized
|
||||
def test_sign_p2sh_example(set_master_key, sim_execfile, start_sign, end_sign, decode_psbt_with_bitcoind, offer_ms_import, need_keypress, clear_ms):
|
||||
def test_sign_p2sh_example(set_master_key, use_regtest, sim_execfile, start_sign, end_sign, decode_psbt_with_bitcoind, offer_ms_import, need_keypress, clear_ms):
|
||||
# Use the private key given in BIP 174 and do similar signing
|
||||
# as the examples.
|
||||
|
||||
@ -537,7 +541,7 @@ def test_sign_p2sh_example(set_master_key, sim_execfile, start_sign, end_sign, d
|
||||
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
def test_change_case(start_sign, end_sign, check_against_bitcoind, cap_story):
|
||||
def test_change_case(start_sign, use_regtest, end_sign, check_against_bitcoind, cap_story):
|
||||
# is change shown/hidden at right times. no fraud checks
|
||||
|
||||
# NOTE: out#1 is change:
|
||||
@ -581,7 +585,7 @@ def test_change_case(start_sign, end_sign, check_against_bitcoind, cap_story):
|
||||
|
||||
@pytest.mark.parametrize('case', [ 1, 2])
|
||||
@pytest.mark.bitcoind
|
||||
def test_change_fraud_path(start_sign, end_sign, case, check_against_bitcoind, cap_story):
|
||||
def test_change_fraud_path(start_sign, use_regtest, end_sign, case, check_against_bitcoind, cap_story):
|
||||
# fraud: BIP-32 path of output doesn't lead to pubkey indicated
|
||||
|
||||
# NOTE: out#1 is change:
|
||||
@ -625,7 +629,7 @@ def test_change_fraud_path(start_sign, end_sign, case, check_against_bitcoind, c
|
||||
signed = end_sign(True)
|
||||
|
||||
@pytest.mark.bitcoind
|
||||
def test_change_fraud_addr(start_sign, end_sign, check_against_bitcoind, cap_story):
|
||||
def test_change_fraud_addr(start_sign, end_sign, use_regtest, check_against_bitcoind, cap_story):
|
||||
# fraud: BIP-32 path of output doesn't match TXO address
|
||||
from pycoin.tx.Tx import Tx
|
||||
from pycoin.tx.TxOut import TxOut
|
||||
@ -659,11 +663,10 @@ def test_change_fraud_addr(start_sign, end_sign, check_against_bitcoind, cap_sto
|
||||
|
||||
@pytest.mark.parametrize('case', [ 'p2wpkh', 'p2sh'])
|
||||
@pytest.mark.bitcoind
|
||||
def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, cap_story, case):
|
||||
def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, use_regtest, cap_story, case):
|
||||
# not fraud: output address encoded in various equiv forms
|
||||
from pycoin.tx.Tx import Tx
|
||||
from pycoin.tx.TxOut import TxOut
|
||||
|
||||
use_regtest()
|
||||
# NOTE: out#1 is change:
|
||||
#chg_addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo'
|
||||
|
||||
@ -678,7 +681,7 @@ def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, cap_st
|
||||
t.txs_out[1].script = bytes([0, 20]) + bytes(pkh)
|
||||
|
||||
from bech32 import encode
|
||||
expect_addr = encode('tb', 0, pkh)
|
||||
expect_addr = encode('bcrt', 0, pkh)
|
||||
|
||||
elif case == 'p2sh':
|
||||
|
||||
@ -916,12 +919,13 @@ def KEEP_test_random_psbt(try_sign, sim_exec, fname="data/ .psbt"):
|
||||
@pytest.mark.bitcoind
|
||||
@pytest.mark.unfinalized
|
||||
@pytest.mark.parametrize('num_dests', [ 1, 10, 25 ])
|
||||
def test_finalization_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, start_sign, end_sign, num_dests):
|
||||
def test_finalization_vs_bitcoind(match_key, use_regtest, check_against_bitcoind, bitcoind, start_sign, end_sign, num_dests):
|
||||
# Compare how we finalize vs bitcoind ... should be exactly the same txn
|
||||
|
||||
wallet_xfp = match_key()
|
||||
# has to be after match key
|
||||
use_regtest()
|
||||
|
||||
bal = bitcoind.getbalance()
|
||||
bal = bitcoind.supply_wallet.getbalance()
|
||||
assert bal > 0, "need some play money; drink from a faucet"
|
||||
|
||||
amt = round((bal/4)/num_dests, 6)
|
||||
@ -929,14 +933,14 @@ def test_finalization_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, s
|
||||
args = {}
|
||||
|
||||
for no in range(num_dests):
|
||||
dest = bitcoind.getrawchangeaddress()
|
||||
assert dest[0] in '2mn' or dest.startswith('tb1'), dest
|
||||
dest = bitcoind.supply_wallet.getrawchangeaddress()
|
||||
assert dest.startswith('bcrt1q'), dest
|
||||
|
||||
args[dest] = amt
|
||||
|
||||
# use walletcreatefundedpsbt
|
||||
# - updated/validated against 0.17.1
|
||||
resp = bitcoind.walletcreatefundedpsbt([], args, 0, {
|
||||
resp = bitcoind.supply_wallet.walletcreatefundedpsbt([], args, 0, {
|
||||
'subtractFeeFromOutputs': list(range(num_dests)),
|
||||
'feeRate': 0.00001500}, True)
|
||||
|
||||
@ -958,9 +962,7 @@ def test_finalization_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, s
|
||||
|
||||
# pull out included txn
|
||||
txn2 = B2A(mine.txn)
|
||||
|
||||
start_sign(psbt, finalize=True)
|
||||
|
||||
# verify against how bitcoind reads it
|
||||
check_against_bitcoind(txn2, fee)
|
||||
|
||||
@ -975,7 +977,7 @@ def test_finalization_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, s
|
||||
open('debug/vs-signed-unfin.psbt', 'wb').write(signed)
|
||||
|
||||
# Use bitcoind to finalize it this time.
|
||||
resp = bitcoind.finalizepsbt(str(b64encode(signed), 'ascii'), True)
|
||||
resp = bitcoind.supply_wallet.finalizepsbt(str(b64encode(signed), 'ascii'), True)
|
||||
assert resp['complete'] == True, "bitcoind wasn't able to finalize it"
|
||||
|
||||
network = a2b_hex(resp['hex'])
|
||||
@ -987,7 +989,7 @@ def test_finalization_vs_bitcoind(match_key, check_against_bitcoind, bitcoind, s
|
||||
assert network == signed_final, "Finalized differently"
|
||||
|
||||
# try to send it
|
||||
txed = bitcoind.sendrawtransaction(B2A(network))
|
||||
txed = bitcoind.supply_wallet.sendrawtransaction(B2A(network))
|
||||
print("Final txn hash: %r" % txed)
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user