You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

251 lines
11 KiB

#! /usr/bin/env python
from __future__ import absolute_import
'''Wallet functionality tests.'''
import sys
import os
import time
import binascii
import pexpect
import random
import subprocess
import datetime
import unittest
from ConfigParser import SafeConfigParser, NoSectionError
from decimal import Decimal
from commontest import (local_command, interact, make_wallets, make_sign_and_push,
DummyBlockchainInterface, TestWallet)
import json
import jmbitcoin as bitcoin
import pytest
from jmclient import (load_program_config, jm_single, sync_wallet, AbstractWallet,
get_p2pk_vbyte, get_log, Wallet, select, select_gradual,
select_greedy, select_greediest, estimate_tx_fee, encryptData,
get_network)
from jmbase.support import chunks
from taker_test_data import t_obtained_tx
log = get_log()
def do_tx(wallet, amount):
ins_full = wallet.select_utxos(0, amount)
cj_addr = wallet.get_internal_addr(1)
change_addr = wallet.get_internal_addr(0)
wallet.update_cache_index()
txid = make_sign_and_push(ins_full, wallet, amount,
output_addr=cj_addr,
change_addr=change_addr,
estimate_fee=True)
assert txid
time.sleep(2) #blocks
jm_single().bc_interface.sync_unspent(wallet)
def test_absurd_fee(setup_wallets):
jm_single().config.set("POLICY", "absurd_fee_per_kb", "1000")
with pytest.raises(ValueError) as e_info:
estimate_tx_fee(10,2)
load_program_config()
def test_abstract_wallet(setup_wallets):
class DoNothingWallet(AbstractWallet):
pass
for algo in ["default", "gradual", "greedy", "greediest", "none"]:
jm_single().config.set("POLICY", "merge_algorithm", algo)
if algo == "none":
with pytest.raises(Exception) as e_info:
dnw = DoNothingWallet()
#also test if the config is blank
jm_single().config = SafeConfigParser()
dnw = DoNothingWallet()
assert dnw.utxo_selector == select
else:
dnw = DoNothingWallet()
assert not dnw.get_key_from_addr("a")
assert not dnw.get_utxos_by_mixdepth()
assert not dnw.get_external_addr(1)
assert not dnw.get_internal_addr(0)
dnw.update_cache_index()
dnw.remove_old_utxos("a")
dnw.add_new_utxos("b", "c")
load_program_config()
def create_default_testnet_wallet():
walletdir = "wallets"
testwalletname = "testwallet.json"
pathtowallet = os.path.join(walletdir, testwalletname)
if os.path.exists(pathtowallet):
os.remove(pathtowallet)
seed = "hello"
return (walletdir, pathtowallet, testwalletname, Wallet(seed,
5,
6,
extend_mixdepth=False,
storepassword=False))
@pytest.mark.parametrize(
"includecache, wrongnet, storepwd, extendmd, pwdnumtries",
[
(False, False, False, False, 1000),
(True, False, False, True, 1),
(False, True, False, False, 1),
(False, False, True, False, 1)
])
def test_wallet_create(setup_wallets, includecache, wrongnet, storepwd, extendmd,
pwdnumtries):
walletdir, pathtowallet, testwalletname, wallet = create_default_testnet_wallet()
assert wallet.get_key(4,1,17) == "1289ca322f96673acef83f396a9735840e3ab69f0459cf9bfa8d9985a876534401"
assert wallet.get_addr(2,0,5) == "myWPu9QJWHGE79XAmuKkwKgNk8vsr5evpk"
jm_single().bc_interface.wallet_synced = True
assert wallet.get_new_addr(1, 0) == "mi88ZgDGPmarzcsU6S437h9CY9BLmgH5M6"
assert wallet.get_external_addr(3) == "mvChQuChnXVhqvH67wfMxrodPQ7xccdVJU"
addr3internal = wallet.get_internal_addr(3)
assert addr3internal == "mv26o79Bauf2miJMoxoSu1vXmfXnk85YPQ"
assert wallet.get_key_from_addr(addr3internal) == "2a283c9a2168a25509e2fb944939637228c50c8b4fecd9024650316c4584246501"
dummyaddr = "mvw1NazKDRbeNufFANqpYNAANafsMC2zVU"
assert not wallet.get_key_from_addr(dummyaddr)
#Make a new Wallet(), and prepare a testnet wallet file for this wallet
password = "dummypassword"
password_key = bitcoin.bin_dbl_sha256(password)
seed = bitcoin.sha256("\xaa"*64)[:32]
encrypted_seed = encryptData(password_key, seed.decode('hex'))
timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
net = get_network() if not wrongnet else 'mainnnet'
walletfilejson = {'creator': 'joinmarket project',
'creation_time': timestamp,
'encrypted_seed': encrypted_seed.encode('hex'),
'network': net}
if includecache:
mmd = wallet.max_mix_depth if not extendmd else wallet.max_mix_depth + 5
print("using mmd: " + str(mmd))
walletfilejson.update({'index_cache': [[0,0]]*mmd})
walletfile = json.dumps(walletfilejson)
if not os.path.exists(walletdir):
os.makedirs(walletdir)
with open(pathtowallet, "wb") as f:
f.write(walletfile)
if wrongnet:
with pytest.raises(ValueError) as e_info:
TestWallet(testwalletname, 5, 6, extend_mixdepth=extendmd,
storepassword=storepwd, pwd=password)
return
from string import ascii_letters
for i in range(pwdnumtries): #multiple tries to ensure pkcs7 error is triggered
with pytest.raises(ValueError) as e_info:
wrongpwd = "".join([random.choice(ascii_letters) for _ in range(20)])
TestWallet(testwalletname, 5, 6, extend_mixdepth=extendmd,
storepassword=storepwd, pwd=wrongpwd)
with pytest.raises(ValueError) as e_info:
TestWallet(testwalletname, 5, 6, extend_mixdepth=extendmd,
storepassword=storepwd, pwd=None)
newwallet = TestWallet(testwalletname, 5, 6, extend_mixdepth=extendmd,
storepassword=storepwd, pwd=password)
assert newwallet.seed == seed
#now we have a functional wallet + file, update the cache; first try
#with failed paths
oldpath = newwallet.path
newwallet.path = None
newwallet.update_cache_index()
newwallet.path = "fake-path-definitely-doesnt-exist"
newwallet.update_cache_index()
#with real path
newwallet.path = oldpath
newwallet.index = [[1,1]]*5
newwallet.update_cache_index()
#ensure we cannot find a mainnet wallet from seed
seed = "goodbye"
jm_single().config.set("BLOCKCHAIN", "network", "mainnet")
with pytest.raises(IOError) as e_info:
Wallet(seed, 5, 6, False, False)
load_program_config()
def test_imported_privkey(setup_wallets):
jm_single().config.set("BLOCKCHAIN", "network", "mainnet")
password = "dummypassword"
password_key = bitcoin.bin_dbl_sha256(password)
privkey = "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi"
#to verify use from_wif_privkey and privkey_to_address
iaddr = "1LDsjB43N2NAQ1Vbc2xyHca4iBBciN8iwC"
privkey_bin = bitcoin.from_wif_privkey(privkey,
vbyte=get_p2pk_vbyte()).decode('hex')[:-1]
encrypted_privkey = encryptData(password_key, privkey_bin)
encrypted_privkey_bad = encryptData(password_key, privkey_bin[:6])
walletdir = "wallets"
testwalletname = "testreal"
pathtowallet = os.path.join(walletdir, testwalletname)
seed = bitcoin.sha256("\xaa"*64)[:32]
encrypted_seed = encryptData(password_key, seed.decode('hex'))
timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
for ep in [encrypted_privkey, encrypted_privkey_bad]:
walletfilejson = {'creator': 'joinmarket project',
'creation_time': timestamp,
'encrypted_seed': encrypted_seed.encode('hex'),
'network': get_network(),
'index_cache': [[0,0]]*5,
'imported_keys': [
{'encrypted_privkey': ep.encode('hex'),
'mixdepth': 0}]}
walletfile = json.dumps(walletfilejson)
if not os.path.exists(walletdir):
os.makedirs(walletdir)
with open(pathtowallet, "wb") as f:
f.write(walletfile)
if ep == encrypted_privkey_bad:
with pytest.raises(Exception) as e_info:
TestWallet(testwalletname, 5, 6, False, False, pwd=password)
continue
newwallet = TestWallet(testwalletname, 5, 6, False, False, pwd=password)
assert newwallet.seed == seed
#test accessing the key from the addr
assert newwallet.get_key_from_addr(iaddr) == bitcoin.from_wif_privkey(privkey)
load_program_config()
def test_add_remove_utxos(setup_wallets):
#Make a fake wallet and inject and then remove fake utxos
walletdir, pathtowallet, testwalletname, wallet = create_default_testnet_wallet()
assert wallet.get_addr(2,0,5) == "myWPu9QJWHGE79XAmuKkwKgNk8vsr5evpk"
wallet.addr_cache["myWPu9QJWHGE79XAmuKkwKgNk8vsr5evpk"] = (2, 0, 5)
#'76a914c55738deaa9861b6022e53a129968cbf354898b488ac'
#these calls automatically update the addr_cache:
assert wallet.get_new_addr(1, 0) == "mi88ZgDGPmarzcsU6S437h9CY9BLmgH5M6"
#76a9141c9761f5fef73bef6aca378c930c59e7e795088488ac
assert wallet.get_external_addr(3) == "mvChQuChnXVhqvH67wfMxrodPQ7xccdVJU"
#76a914a115fa0394ce881437a96d443e236b39e07db1f988ac
#using the above pubkey scripts:
faketxforwallet = {'outs':
[{'script': '76a914c55738deaa9861b6022e53a129968cbf354898b488ac',
'value': 110000000},
{'script': '76a9141c9761f5fef73bef6aca378c930c59e7e795088488ac',
'value': 89910900},
{'script': '76a914a115fa0394ce881437a96d443e236b39e07db1f988ac',
'value': 90021000},
{'script': '76a9145ece2dac945c8ff5b2b6635360ca0478ade305d488ac', #not ours
'value': 110000000}],
'version': 1}
wallet.add_new_utxos(faketxforwallet, "aa"*32)
faketxforspending = {'ins':
[{'outpoint': {'hash': 'aa'*32,
'index': 0}},
{'outpoint': {'hash': 'aa'*32,
'index': 1}},
{'outpoint': {'hash': 'aa'*32,
'index': 2}},
{'outpoint': {'hash': '3f3ea820d706e08ad8dc1d2c392c98facb1b067ae4c671043ae9461057bd2a3c',
'index': 1},
'script': '',
'sequence': 4294967295}]}
wallet.select_utxos(1, 100000)
with pytest.raises(Exception) as e_info:
wallet.select_utxos(0, 100000)
#ensure get_utxos_by_mixdepth can handle utxos outside of maxmixdepth
wallet.max_mix_depth = 2
mul = wallet.get_utxos_by_mixdepth()
assert mul[3] != {}
wallet.remove_old_utxos(faketxforspending)
@pytest.fixture(scope="module")
def setup_wallets():
load_program_config()