176 lines
4.7 KiB
Python
176 lines
4.7 KiB
Python
# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
|
#
|
|
# Access a local bitcoin-Qt/bitcoind on testnet (must be v22 or higher)
|
|
#
|
|
# Must have these lines in the bitcoin.conf file:
|
|
#
|
|
# testnet=1
|
|
# server=1
|
|
#
|
|
import pytest, os
|
|
from bitcoinrpc.authproxy import AuthServiceProxy
|
|
from base64 import b64encode, b64decode
|
|
|
|
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')
|
|
|
|
return cookie
|
|
|
|
@pytest.fixture(scope='function')
|
|
def bitcoind():
|
|
# JSON-RPC connection to a bitcoind instance
|
|
|
|
# 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):
|
|
# load simulator w/ existing bip32 master key of testnet instance
|
|
|
|
# bummer: dumpmasterprivkey RPC call was removed!
|
|
#prv = bitcoind.dumpmasterprivkey()
|
|
|
|
def doit():
|
|
print("match_key: doit()")
|
|
from tempfile import mktemp
|
|
fn = mktemp()
|
|
bitcoind.dumpwallet(fn)
|
|
prv = None
|
|
|
|
for ln in open(fn, 'rt').readlines():
|
|
if 'extended private masterkey' in ln:
|
|
assert not prv
|
|
prv = ln.split(": ", 1)[1].strip()
|
|
|
|
os.unlink(fn)
|
|
|
|
assert prv.startswith('tprv')
|
|
|
|
xfp = set_master_key(prv)
|
|
|
|
return xfp
|
|
|
|
# NOTE: set_master_key does teardown/reset
|
|
return doit
|
|
|
|
@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)
|
|
|
|
return b64decode(rv.get('psbt', '')), rv.get('hex'), rv['complete']
|
|
|
|
return doit
|
|
|
|
@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 doit
|
|
|
|
@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 doit
|
|
|
|
@pytest.fixture()
|
|
def explora():
|
|
def doit(*parts):
|
|
import urllib.request
|
|
import json
|
|
url = 'https://blockstream.info/testnet/api/' + '/'.join(parts)
|
|
with urllib.request.urlopen(url) as response:
|
|
return json.load(response)
|
|
|
|
return doit
|
|
|
|
|
|
@pytest.fixture(scope='function')
|
|
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
|
|
|
|
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')
|
|
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)
|
|
|
|
|
|
|
|
# EOF
|