reorganization

This commit is contained in:
Lex Berezhny 2018-11-04 01:55:50 -04:00
parent c1b96a36a7
commit 5f70ae9923
60 changed files with 320 additions and 243 deletions

View file

@ -51,6 +51,19 @@ setup(
'plyvel',
'pylru'
),
'ui': (
'pyside2',
)
},
entry_points={'console_scripts': ['torba=torba.cli:main']}
entry_points={
'console_scripts': [
'torba-client=torba.client.cli:main',
'torba-server=torba.server.cli:main [server]',
'orchstr8=torba.orchstr8.cli:main [server]',
],
'gui_scripts': [
'torba=torba.ui:main [ui]'
'torba-workbench=torba.workbench:main [ui]'
]
}
)

View file

@ -1,10 +1,10 @@
from orchstr8.testcase import IntegrationTestCase
from asyncio import sleep
import logging
from torba.testcase import IntegrationTestCase
class BlockchainReorganizationTests(IntegrationTestCase):
VERBOSE = True
VERBOSITY = logging.WARN
async def test_reorg(self):
self.assertEqual(self.ledger.headers.height, 200)
@ -14,11 +14,11 @@ class BlockchainReorganizationTests(IntegrationTestCase):
self.assertEqual(self.ledger.headers.height, 201)
height = 201
#simple fork (rewind+advance to immediate best)
# simple fork (rewind+advance to immediate best)
height = await self._simulate_reorg(height, 1, 1, 2)
height = await self._simulate_reorg(height, 2, 1, 10)
height = await self._simulate_reorg(height, 4, 1, 3)
#lagged fork (rewind+batch catch up with immediate best)
# lagged fork (rewind+batch catch up with immediate best)
height = await self._simulate_reorg(height, 4, 2, 3)
await self._simulate_reorg(height, 4, 4, 3)
@ -32,5 +32,5 @@ class BlockchainReorganizationTests(IntegrationTestCase):
await self.blockchain.generate(1)
height += 1
await self.on_header(height)
self.assertEquals(height, self.ledger.headers.height)
self.assertEqual(height, self.ledger.headers.height)
return height

View file

@ -1,13 +1,13 @@
import logging
from asyncio import CancelledError
from torba.testing import IntegrationTestCase
from torba.constants import COIN
from torba.testcase import IntegrationTestCase
from torba.client.constants import COIN
class ReconnectTests(IntegrationTestCase):
VERBOSITY = logging.DEBUG
VERBOSITY = logging.WARN
async def test_connection_drop_still_receives_events_after_reconnected(self):
address1 = await self.account.receiving.get_or_create_usable_address()

View file

@ -1,12 +1,12 @@
import logging
import asyncio
from torba.testing import IntegrationTestCase
from torba.constants import COIN
from torba.testcase import IntegrationTestCase
from torba.client.constants import COIN
class BasicTransactionTests(IntegrationTestCase):
VERBOSITY = logging.WARNING
VERBOSITY = logging.WARN
async def test_sending_and_receiving(self):
account1, account2 = self.account, self.wallet.generate_account(self.ledger)

Binary file not shown.

View file

@ -1,10 +1,10 @@
from binascii import hexlify
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
from torba.baseaccount import HierarchicalDeterministic, SingleKey
from torba.wallet import Wallet
from torba.client.baseaccount import HierarchicalDeterministic, SingleKey
from torba.client.wallet import Wallet
class TestHierarchicalDeterministicAccount(AsyncioTestCase):

View file

@ -1,6 +1,6 @@
import unittest
from torba.bcd_data_stream import BCDataStream
from torba.client.bcd_data_stream import BCDataStream
class TestBCDataStream(unittest.TestCase):

View file

@ -1,9 +1,9 @@
from binascii import unhexlify, hexlify
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from .key_fixtures import expected_ids, expected_privkeys, expected_hardened_privkeys
from torba.bip32 import PubKey, PrivateKey, from_extended_key_string
from client_tests.unit.key_fixtures import expected_ids, expected_privkeys, expected_hardened_privkeys
from torba.client.bip32 import PubKey, PrivateKey, from_extended_key_string
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class

View file

@ -1,12 +1,12 @@
from types import GeneratorType
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
from torba.coinselection import CoinSelector, MAXIMUM_TRIES
from torba.constants import CENT
from torba.client.coinselection import CoinSelector, MAXIMUM_TRIES
from torba.client.constants import CENT
from .test_transaction import get_output as utxo
from client_tests.unit.test_transaction import get_output as utxo
NULL_HASH = b'\x00'*32

View file

@ -1,13 +1,13 @@
import unittest
from torba.wallet import Wallet
from torba.constants import COIN
from torba.client.wallet import Wallet
from torba.client.constants import COIN
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
from torba.basedatabase import query, constraints_to_sql
from torba.client.basedatabase import query, constraints_to_sql
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from .test_transaction import get_output, NULL_HASH
from client_tests.unit.test_transaction import get_output, NULL_HASH
class TestQueryBuilder(unittest.TestCase):

View file

@ -1,5 +1,5 @@
from unittest import TestCase, mock
from torba.hash import aes_decrypt, aes_encrypt
from torba.client.hash import aes_decrypt, aes_encrypt
class TestAESEncryptDecrypt(TestCase):

View file

@ -1,7 +1,7 @@
import os
from urllib.request import Request, urlopen
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from torba.coin.bitcoinsegwit import MainHeaders

View file

@ -2,10 +2,10 @@ import os
from binascii import hexlify
from torba.coin.bitcoinsegwit import MainNetLedger
from torba.wallet import Wallet
from torba.client.wallet import Wallet
from .test_transaction import get_transaction, get_output
from .test_headers import BitcoinHeadersTestCase, block_bytes
from client_tests.unit.test_transaction import get_transaction, get_output
from client_tests.unit.test_headers import BitcoinHeadersTestCase, block_bytes
class MockNetwork:

View file

@ -1,7 +1,7 @@
import unittest
from binascii import hexlify
from torba.mnemonic import Mnemonic
from torba.client.mnemonic import Mnemonic
class TestMnemonic(unittest.TestCase):

View file

@ -1,10 +1,10 @@
import unittest
from binascii import hexlify, unhexlify
from torba.bcd_data_stream import BCDataStream
from torba.basescript import Template, ParseError, tokenize, push_data
from torba.basescript import PUSH_SINGLE, PUSH_INTEGER, PUSH_MANY, OP_HASH160, OP_EQUAL
from torba.basescript import BaseInputScript, BaseOutputScript
from torba.client.bcd_data_stream import BCDataStream
from torba.client.basescript import Template, ParseError, tokenize, push_data
from torba.client.basescript import PUSH_SINGLE, PUSH_INTEGER, PUSH_MANY, OP_HASH160, OP_EQUAL
from torba.client.basescript import BaseInputScript, BaseOutputScript
def parse(opcodes, source):

View file

@ -2,11 +2,11 @@ import unittest
from binascii import hexlify, unhexlify
from itertools import cycle
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
from torba.wallet import Wallet
from torba.constants import CENT, COIN
from torba.client.wallet import Wallet
from torba.client.constants import CENT, COIN
NULL_HASH = b'\x00'*32

View file

@ -1,6 +1,6 @@
import unittest
from torba.util import ArithUint256
from torba.client.util import ArithUint256
class TestArithUint256(unittest.TestCase):

View file

@ -1,11 +1,11 @@
import tempfile
from torba.testing import AsyncioTestCase
from torba.testcase import AsyncioTestCase
from torba.coin.bitcoinsegwit import MainNetLedger as BTCLedger
from torba.coin.bitcoincash import MainNetLedger as BCHLedger
from torba.basemanager import BaseWalletManager
from torba.wallet import Wallet, WalletStorage
from torba.client.basemanager import BaseWalletManager
from torba.client.wallet import Wallet, WalletStorage
class TestWalletCreation(AsyncioTestCase):

0
torba/client/__init__.py Normal file
View file

View file

@ -2,14 +2,13 @@ import random
import typing
from typing import Dict, Tuple, Type, Optional, Any
from torba.mnemonic import Mnemonic
from torba.bip32 import PrivateKey, PubKey, from_extended_key_string
from torba.hash import aes_encrypt, aes_decrypt
from torba.constants import COIN
from torba.client.mnemonic import Mnemonic
from torba.client.bip32 import PrivateKey, PubKey, from_extended_key_string
from torba.client.hash import aes_encrypt, aes_decrypt
from torba.client.constants import COIN
if typing.TYPE_CHECKING:
from torba import baseledger
from torba import wallet as basewallet
from torba.client import baseledger, wallet as basewallet
class AddressManager:

View file

@ -8,9 +8,9 @@ from typing import Tuple, List, Union, Callable, Any, Awaitable, Iterable
import sqlite3
from torba.hash import TXRefImmutable
from torba.basetransaction import BaseTransaction
from torba.baseaccount import BaseAccount
from torba.client.hash import TXRefImmutable
from torba.client.basetransaction import BaseTransaction
from torba.client.baseaccount import BaseAccount
log = logging.getLogger(__name__)

View file

@ -5,8 +5,8 @@ from io import BytesIO
from typing import Optional, Iterator, Tuple
from binascii import hexlify
from torba.util import ArithUint256
from torba.hash import double_sha256
from torba.client.util import ArithUint256
from torba.client.hash import double_sha256
log = logging.getLogger(__name__)

View file

@ -8,15 +8,13 @@ from typing import Dict, Type, Iterable
from operator import itemgetter
from collections import namedtuple
from torba import baseaccount
from torba import basenetwork
from torba import basetransaction
from torba.basedatabase import BaseDatabase
from torba.baseheader import BaseHeaders
from torba.coinselection import CoinSelector
from torba.constants import COIN, NULL_HASH32
from torba.client import baseaccount, basenetwork, basetransaction
from torba.client.basedatabase import BaseDatabase
from torba.client.baseheader import BaseHeaders
from torba.client.coinselection import CoinSelector
from torba.client.constants import COIN, NULL_HASH32
from torba.stream import StreamController
from torba.hash import hash160, double_sha256, sha256, Base58
from torba.client.hash import hash160, double_sha256, sha256, Base58
log = logging.getLogger(__name__)

View file

@ -2,8 +2,8 @@ import asyncio
import logging
from typing import Type, MutableSequence, MutableMapping
from torba.baseledger import BaseLedger, LedgerRegistry
from torba.wallet import Wallet, WalletStorage
from torba.client.baseledger import BaseLedger, LedgerRegistry
from torba.client.wallet import Wallet, WalletStorage
log = logging.getLogger(__name__)

View file

@ -3,8 +3,8 @@ from binascii import hexlify
from collections import namedtuple
from typing import List
from torba.bcd_data_stream import BCDataStream
from torba.util import subclass_tuple
from torba.client.bcd_data_stream import BCDataStream
from torba.client.util import subclass_tuple
# bitcoin opcodes
OP_0 = 0x00

View file

@ -3,16 +3,15 @@ import typing
from typing import List, Iterable, Optional
from binascii import hexlify
from torba.basescript import BaseInputScript, BaseOutputScript
from torba.baseaccount import BaseAccount
from torba.constants import COIN, NULL_HASH32
from torba.bcd_data_stream import BCDataStream
from torba.hash import sha256, TXRef, TXRefImmutable
from torba.util import ReadOnlyList
from torba.client.basescript import BaseInputScript, BaseOutputScript
from torba.client.baseaccount import BaseAccount
from torba.client.constants import COIN, NULL_HASH32
from torba.client.bcd_data_stream import BCDataStream
from torba.client.hash import sha256, TXRef, TXRefImmutable
from torba.client.util import ReadOnlyList
if typing.TYPE_CHECKING:
from torba import baseledger
from torba.client import baseledger
log = logging.getLogger()

View file

@ -9,8 +9,8 @@
""" Logic for BIP32 Hierarchical Key Derivation. """
from coincurve import PublicKey, PrivateKey as _PrivateKey
from torba.hash import Base58, hmac_sha512, hash160, double_sha256
from torba.util import cachedproperty
from torba.client.hash import Base58, hmac_sha512, hash160, double_sha256
from torba.client.util import cachedproperty
class DerivationError(Exception):

View file

@ -3,8 +3,8 @@ import argparse
import asyncio
import aiohttp
from torba.testing.node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
from torba.testing.service import TestingServiceAPI
from torba.orchstr8.node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
from torba.orchstr8.service import TestingServiceAPI
def get_argument_parser():

View file

@ -1,7 +1,7 @@
from random import Random
from typing import List
from torba import basetransaction
from torba.client import basetransaction
MAXIMUM_TRIES = 100000

View file

@ -18,8 +18,8 @@ from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.backends import default_backend
from torba.util import bytes_to_int, int_to_bytes
from torba.constants import NULL_HASH32
from torba.client.util import bytes_to_int, int_to_bytes
from torba.client.constants import NULL_HASH32
class TXRef:

View file

@ -12,8 +12,8 @@ from secrets import randbelow
import pbkdf2
from torba.hash import hmac_sha512
from torba.words import english
from torba.client.hash import hmac_sha512
from torba.client.words import english
# The hash of the mnemonic seed must begin with this
SEED_PREFIX = b'01' # Standard wallet

View file

@ -5,9 +5,7 @@ import typing
from typing import Sequence, MutableSequence
if typing.TYPE_CHECKING:
from torba import baseaccount
from torba import baseledger
from torba import basemanager
from torba.client import basemanager, baseaccount, baseledger
class Wallet:

View file

View file

@ -7,8 +7,8 @@ __node_url__ = (
__spvserver__ = 'torba.server.coins.BitcoinCashRegtest'
from binascii import unhexlify
from torba.baseledger import BaseLedger
from torba.basetransaction import BaseTransaction
from torba.client.baseledger import BaseLedger
from torba.client.basetransaction import BaseTransaction
from .bitcoinsegwit import MainHeaders, UnverifiedHeaders

View file

@ -9,8 +9,8 @@ __spvserver__ = 'torba.server.coins.BitcoinSegwitRegtest'
import struct
from typing import Optional
from binascii import hexlify, unhexlify
from torba.baseledger import BaseLedger
from torba.baseheader import BaseHeaders, ArithUint256
from torba.client.baseledger import BaseLedger
from torba.client.baseheader import BaseHeaders, ArithUint256
class MainHeaders(BaseHeaders):

View file

@ -0,0 +1,2 @@
from .node import Conductor
from .service import ConductorService

89
torba/orchstr8/cli.py Normal file
View file

@ -0,0 +1,89 @@
import logging
import argparse
import asyncio
import aiohttp
from .node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
from .service import ConductorService
def get_argument_parser():
parser = argparse.ArgumentParser(
prog="torba"
)
subparsers = parser.add_subparsers(dest='command', help='sub-command help')
subparsers.add_parser("gui", help="Start Qt GUI.")
subparsers.add_parser("download", help="Download blockchain node binary.")
start = subparsers.add_parser("start", help="Start orchstr8 service.")
start.add_argument("--blockchain", help="Start blockchain node.", action="store_true")
start.add_argument("--spv", help="Start SPV server.", action="store_true")
start.add_argument("--wallet", help="Start wallet daemon.", action="store_true")
generate = subparsers.add_parser("generate", help="Call generate method on running orchstr8 instance.")
generate.add_argument("blocks", type=int, help="Number of blocks to generate")
subparsers.add_parser("transfer", help="Call transfer method on running orchstr8 instance.")
return parser
async def run_remote_command(command, **kwargs):
async with aiohttp.ClientSession() as session:
async with session.post('http://localhost:7954/'+command, data=kwargs) as resp:
print(resp.status)
print(await resp.text())
def main():
parser = get_argument_parser()
args = parser.parse_args()
command = getattr(args, 'command', 'help')
if command == 'gui':
from torba.workbench import main as start_app # pylint: disable=E0611,E0401
return start_app()
loop = asyncio.get_event_loop()
ledger = get_ledger_from_environment()
if command == 'download':
logging.getLogger('blockchain').setLevel(logging.INFO)
get_blockchain_node_from_ledger(ledger).ensure()
elif command == 'generate':
loop.run_until_complete(run_remote_command(
'generate', blocks=args.blocks
))
elif command == 'start':
conductor = Conductor()
if getattr(args, 'blockchain', False):
loop.run_until_complete(conductor.start_blockchain())
if getattr(args, 'spv', False):
loop.run_until_complete(conductor.start_spv())
if getattr(args, 'wallet', False):
loop.run_until_complete(conductor.start_wallet())
service = ConductorService(conductor, loop)
loop.run_until_complete(service.start())
try:
print('========== Orchstr8 API Service Started ========')
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
loop.run_until_complete(service.stop())
loop.run_until_complete(conductor.stop())
loop.close()
else:
parser.print_help()
if __name__ == "__main__":
main()

View file

@ -14,10 +14,10 @@ import requests
from torba.server.server import Server
from torba.server.env import Env
from torba.wallet import Wallet
from torba.baseledger import BaseLedger, BlockHeightEvent
from torba.basemanager import BaseWalletManager
from torba.baseaccount import BaseAccount
from torba.client.wallet import Wallet
from torba.client.baseledger import BaseLedger, BlockHeightEvent
from torba.client.basemanager import BaseWalletManager
from torba.client.baseaccount import BaseAccount
def get_manager_from_environment(default_manager=BaseWalletManager):
@ -170,6 +170,7 @@ class SPVNode:
}
os.environ.update(conf)
self.server = Server(Env(self.coin_class))
self.server.bp.prefetcher.polling_delay = 0.5
await self.server.start()
async def stop(self, cleanup=True):

View file

@ -25,7 +25,7 @@ class WebSocketLogHandler(logging.Handler):
self.handleError(record)
class TestingServiceAPI:
class ConductorService:
def __init__(self, stack: Conductor, loop: asyncio.AbstractEventLoop) -> None:
self.stack = stack

View file

@ -45,6 +45,7 @@ from torba.server.hash import HASHX_LEN, hex_str_to_hash
from torba.server.script import ScriptPubKey, OpCodes
import torba.server.tx as lib_tx
import torba.server.block_processor as block_proc
from torba.server.db import DB
import torba.server.daemon as daemon
from torba.server.session import ElectrumX, DashElectrumX
@ -71,6 +72,7 @@ class Coin(object):
DESERIALIZER = lib_tx.Deserializer
DAEMON = daemon.Daemon
BLOCK_PROCESSOR = block_proc.BlockProcessor
DB = DB
HEADER_VALUES = [
'version', 'prev_block_hash', 'merkle_root', 'timestamp', 'bits', 'nonce'
]

View file

@ -5,33 +5,35 @@
# See the file "LICENCE" for information about the copyright
# and warranty status of this software.
'''Class for handling environment configuration and defaults.'''
import re
import resource
from os import environ
from collections import namedtuple
from ipaddress import ip_address
from torba.server.util import class_logger
from torba.server.coins import Coin
from torba.server.env_base import EnvBase
import torba.server.util as lib_util
NetIdentity = namedtuple('NetIdentity', 'host tcp_port ssl_port nick_suffix')
class Env(EnvBase):
'''Wraps environment configuration. Optionally, accepts a Coin class
as first argument to have ElectrumX serve custom coins not part of
the standard distribution.
'''
class Env:
# Peer discovery
PD_OFF, PD_SELF, PD_ON = range(3)
class Error(Exception):
pass
def __init__(self, coin=None):
super().__init__()
self.logger = class_logger(__name__, self.__class__.__name__)
self.allow_root = self.boolean('ALLOW_ROOT', False)
self.host = self.default('HOST', 'localhost')
self.rpc_host = self.default('RPC_HOST', 'localhost')
self.loop_policy = self.event_loop_policy()
self.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK'])
self.db_dir = self.required('DB_DIRECTORY')
self.db_engine = self.default('DB_ENGINE', 'leveldb')
@ -55,8 +57,7 @@ class Env(EnvBase):
self.rpc_port = self.integer('RPC_PORT', 8000)
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
self.banner_file = self.default('BANNER_FILE', None)
self.tor_banner_file = self.default('TOR_BANNER_FILE',
self.banner_file)
self.tor_banner_file = self.default('TOR_BANNER_FILE', self.banner_file)
self.anon_logs = self.boolean('ANON_LOGS', False)
self.log_sessions = self.integer('LOG_SESSIONS', 3600)
# Peer discovery
@ -83,6 +84,78 @@ class Env(EnvBase):
for identity in (clearnet_identity, tor_identity)
if identity is not None]
@classmethod
def default(cls, envvar, default):
return environ.get(envvar, default)
@classmethod
def boolean(cls, envvar, default):
default = 'Yes' if default else ''
return bool(cls.default(envvar, default).strip())
@classmethod
def required(cls, envvar):
value = environ.get(envvar)
if value is None:
raise cls.Error('required envvar {} not set'.format(envvar))
return value
@classmethod
def integer(cls, envvar, default):
value = environ.get(envvar)
if value is None:
return default
try:
return int(value)
except Exception:
raise cls.Error('cannot convert envvar {} value {} to an integer'
.format(envvar, value))
@classmethod
def custom(cls, envvar, default, parse):
value = environ.get(envvar)
if value is None:
return default
try:
return parse(value)
except Exception as e:
raise cls.Error('cannot parse envvar {} value {}'
.format(envvar, value)) from e
@classmethod
def obsolete(cls, envvars):
bad = [envvar for envvar in envvars if environ.get(envvar)]
if bad:
raise cls.Error('remove obsolete environment variables {}'
.format(bad))
def event_loop_policy(self):
policy = self.default('EVENT_LOOP_POLICY', None)
if policy is None:
return None
if policy == 'uvloop':
import uvloop
return uvloop.EventLoopPolicy()
raise self.Error('unknown event loop policy "{}"'.format(policy))
def cs_host(self, *, for_rpc):
'''Returns the 'host' argument to pass to asyncio's create_server
call. The result can be a single host name string, a list of
host name strings, or an empty string to bind to all interfaces.
If rpc is True the host to use for the RPC server is returned.
Otherwise the host to use for SSL/TCP servers is returned.
'''
host = self.rpc_host if for_rpc else self.host
result = [part.strip() for part in host.split(',')]
if len(result) == 1:
result = result[0]
# An empty result indicates all interfaces, which we do not
# permitted for an RPC server.
if for_rpc and not result:
result = 'localhost'
return result
def sane_max_sessions(self):
'''Return the maximum number of sessions to permit. Normally this
is MAX_SESSIONS. However, to prevent open file exhaustion, ajdust

View file

@ -1,99 +0,0 @@
# Copyright (c) 2017, Neil Booth
#
# All rights reserved.
#
# See the file "LICENCE" for information about the copyright
# and warranty status of this software.
'''Class for server environment configuration and defaults.'''
from os import environ
from torba.server.util import class_logger
class EnvBase(object):
'''Wraps environment configuration.'''
class Error(Exception):
pass
def __init__(self):
self.logger = class_logger(__name__, self.__class__.__name__)
self.allow_root = self.boolean('ALLOW_ROOT', False)
self.host = self.default('HOST', 'localhost')
self.rpc_host = self.default('RPC_HOST', 'localhost')
self.loop_policy = self.event_loop_policy()
@classmethod
def default(cls, envvar, default):
return environ.get(envvar, default)
@classmethod
def boolean(cls, envvar, default):
default = 'Yes' if default else ''
return bool(cls.default(envvar, default).strip())
@classmethod
def required(cls, envvar):
value = environ.get(envvar)
if value is None:
raise cls.Error('required envvar {} not set'.format(envvar))
return value
@classmethod
def integer(cls, envvar, default):
value = environ.get(envvar)
if value is None:
return default
try:
return int(value)
except Exception:
raise cls.Error('cannot convert envvar {} value {} to an integer'
.format(envvar, value))
@classmethod
def custom(cls, envvar, default, parse):
value = environ.get(envvar)
if value is None:
return default
try:
return parse(value)
except Exception as e:
raise cls.Error('cannot parse envvar {} value {}'
.format(envvar, value)) from e
@classmethod
def obsolete(cls, envvars):
bad = [envvar for envvar in envvars if environ.get(envvar)]
if bad:
raise cls.Error('remove obsolete environment variables {}'
.format(bad))
def event_loop_policy(self):
policy = self.default('EVENT_LOOP_POLICY', None)
if policy is None:
return None
if policy == 'uvloop':
import uvloop
return uvloop.EventLoopPolicy()
raise self.Error('unknown event loop policy "{}"'.format(policy))
def cs_host(self, *, for_rpc):
'''Returns the 'host' argument to pass to asyncio's create_server
call. The result can be a single host name string, a list of
host name strings, or an empty string to bind to all interfaces.
If rpc is True the host to use for the RPC server is returned.
Otherwise the host to use for SSL/TCP servers is returned.
'''
host = self.rpc_host if for_rpc else self.host
result = [part.strip() for part in host.split(',')]
if len(result) == 1:
result = result[0]
# An empty result indicates all interfaces, which we do not
# permitted for an RPC server.
if for_rpc and not result:
result = 'localhost'
return result

View file

@ -1,11 +1,8 @@
import signal
import time
import logging
from functools import partial
import asyncio
import torba
from torba.server.db import DB
from torba.server.mempool import MemPool, MemPoolAPI
from torba.server.session import SessionManager
@ -73,21 +70,10 @@ class Server:
self.shutdown_event = asyncio.Event()
self.cancellable_tasks = []
async def start(self):
env = self.env
min_str, max_str = env.coin.SESSIONCLS.protocol_min_max_strings()
self.log.info(f'software version: {torba.__version__}')
self.log.info(f'supported protocol versions: {min_str}-{max_str}')
self.log.info(f'event loop policy: {env.loop_policy}')
self.log.info(f'reorg limit is {env.reorg_limit:,d} blocks')
notifications = Notifications()
Daemon = env.coin.DAEMON
BlockProcessor = env.coin.BLOCK_PROCESSOR
daemon = Daemon(env.coin, env.daemon_url)
db = DB(env)
bp = BlockProcessor(env, db, daemon, notifications)
self.notifications = notifications = Notifications()
self.daemon = daemon = env.coin.DAEMON(env.coin, env.daemon_url)
self.db = db = env.coin.DB(env)
self.bp = bp = env.coin.BLOCK_PROCESSOR(env, db, daemon, notifications)
# Set notifications up to implement the MemPoolAPI
notifications.height = daemon.height
@ -96,23 +82,31 @@ class Server:
notifications.raw_transactions = daemon.getrawtransactions
notifications.lookup_utxos = db.lookup_utxos
MemPoolAPI.register(Notifications)
mempool = MemPool(env.coin, notifications)
self.mempool = mempool = MemPool(env.coin, notifications)
session_mgr = SessionManager(
self.session_mgr = SessionManager(
env, db, bp, daemon, mempool, self.shutdown_event
)
await daemon.height()
async def start(self):
env = self.env
min_str, max_str = env.coin.SESSIONCLS.protocol_min_max_strings()
self.log.info(f'software version: {torba.__version__}')
self.log.info(f'supported protocol versions: {min_str}-{max_str}')
self.log.info(f'event loop policy: {env.loop_policy}')
self.log.info(f'reorg limit is {env.reorg_limit:,d} blocks')
await self.daemon.height()
def _start_cancellable(run, *args):
_flag = asyncio.Event()
self.cancellable_tasks.append(asyncio.ensure_future(run(*args, _flag)))
return _flag.wait()
await _start_cancellable(bp.fetch_and_process_blocks)
await db.populate_header_merkle_cache()
await _start_cancellable(mempool.keep_synchronized)
await _start_cancellable(session_mgr.serve, notifications)
await _start_cancellable(self.bp.fetch_and_process_blocks)
await self.db.populate_header_merkle_cache()
await _start_cancellable(self.mempool.keep_synchronized)
await _start_cancellable(self.session_mgr.serve, self.notifications)
def stop(self):
for task in reversed(self.cancellable_tasks):

View file

@ -1,7 +1,8 @@
import sys
import logging
import unittest
from unittest.case import _Outcome
from .node import Conductor
from torba.orchstr8 import Conductor
try:
@ -14,6 +15,12 @@ except ImportError:
def _cancel_all_tasks(loop):
pass
HANDLER = logging.StreamHandler(sys.stdout)
HANDLER.setFormatter(
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
)
logging.getLogger().addHandler(HANDLER)
class AsyncioTestCase(unittest.TestCase):
# Implementation inspired by discussion:
@ -119,12 +126,13 @@ class IntegrationTestCase(AsyncioTestCase):
LEDGER = None
MANAGER = None
VERBOSITY = logging.WARNING
VERBOSITY = logging.WARN
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.conductor = None
self.blockchain = None
self.wallet_node = None
self.manager = None
self.ledger = None
self.wallet = None
@ -136,10 +144,11 @@ class IntegrationTestCase(AsyncioTestCase):
)
await self.conductor.start()
self.blockchain = self.conductor.blockchain_node
self.manager = self.conductor.wallet_node.manager
self.ledger = self.conductor.wallet_node.ledger
self.wallet = self.conductor.wallet_node.wallet
self.account = self.conductor.wallet_node.wallet.default_account
self.wallet_node = self.conductor.wallet_node
self.manager = self.wallet_node.manager
self.ledger = self.wallet_node.ledger
self.wallet = self.wallet_node.wallet
self.account = self.wallet_node.wallet.default_account
async def asyncTearDown(self):
await self.conductor.stop()

View file

@ -1 +0,0 @@
from .testcase import IntegrationTestCase, AsyncioTestCase

0
torba/ui/__init__.py Normal file
View file

View file

@ -14,9 +14,9 @@ changedir = {toxinidir}/tests
setenv =
integration: TORBA_LEDGER={envname}
commands =
unit: coverage run -p --source={envsitepackagesdir}/torba -m unittest discover -t . unit
unit: coverage run -p --source={envsitepackagesdir}/torba -m unittest discover -t . client_tests.unit
integration: torba download
integration: coverage run -p --source={envsitepackagesdir}/torba -m unittest integration.test_transactions
integration: coverage run -p --source={envsitepackagesdir}/torba -m unittest integration.test_reconnect
integration: coverage run -p --source={envsitepackagesdir}/torba -m unittest client_tests.integration.test_transactions
integration: coverage run -p --source={envsitepackagesdir}/torba -m unittest client_tests.integration.test_reconnect
# Too slow on Travis
# integration: coverage run -p --source={envsitepackagesdir}/torba -m twisted.trial --reactor=asyncio integration.test_blockchain_reorganization