forked from LBRYCommunity/lbry-sdk
reorganization
This commit is contained in:
parent
c1b96a36a7
commit
5f70ae9923
60 changed files with 320 additions and 243 deletions
15
setup.py
15
setup.py
|
@ -51,6 +51,19 @@ setup(
|
||||||
'plyvel',
|
'plyvel',
|
||||||
'pylru'
|
'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]'
|
||||||
|
]
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from orchstr8.testcase import IntegrationTestCase
|
import logging
|
||||||
from asyncio import sleep
|
from torba.testcase import IntegrationTestCase
|
||||||
|
|
||||||
|
|
||||||
class BlockchainReorganizationTests(IntegrationTestCase):
|
class BlockchainReorganizationTests(IntegrationTestCase):
|
||||||
|
|
||||||
VERBOSE = True
|
VERBOSITY = logging.WARN
|
||||||
|
|
||||||
async def test_reorg(self):
|
async def test_reorg(self):
|
||||||
self.assertEqual(self.ledger.headers.height, 200)
|
self.assertEqual(self.ledger.headers.height, 200)
|
||||||
|
@ -14,11 +14,11 @@ class BlockchainReorganizationTests(IntegrationTestCase):
|
||||||
self.assertEqual(self.ledger.headers.height, 201)
|
self.assertEqual(self.ledger.headers.height, 201)
|
||||||
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, 1, 1, 2)
|
||||||
height = await self._simulate_reorg(height, 2, 1, 10)
|
height = await self._simulate_reorg(height, 2, 1, 10)
|
||||||
height = await self._simulate_reorg(height, 4, 1, 3)
|
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)
|
height = await self._simulate_reorg(height, 4, 2, 3)
|
||||||
await self._simulate_reorg(height, 4, 4, 3)
|
await self._simulate_reorg(height, 4, 4, 3)
|
||||||
|
|
||||||
|
@ -32,5 +32,5 @@ class BlockchainReorganizationTests(IntegrationTestCase):
|
||||||
await self.blockchain.generate(1)
|
await self.blockchain.generate(1)
|
||||||
height += 1
|
height += 1
|
||||||
await self.on_header(height)
|
await self.on_header(height)
|
||||||
self.assertEquals(height, self.ledger.headers.height)
|
self.assertEqual(height, self.ledger.headers.height)
|
||||||
return height
|
return height
|
|
@ -1,13 +1,13 @@
|
||||||
import logging
|
import logging
|
||||||
from asyncio import CancelledError
|
from asyncio import CancelledError
|
||||||
|
|
||||||
from torba.testing import IntegrationTestCase
|
from torba.testcase import IntegrationTestCase
|
||||||
from torba.constants import COIN
|
from torba.client.constants import COIN
|
||||||
|
|
||||||
|
|
||||||
class ReconnectTests(IntegrationTestCase):
|
class ReconnectTests(IntegrationTestCase):
|
||||||
|
|
||||||
VERBOSITY = logging.DEBUG
|
VERBOSITY = logging.WARN
|
||||||
|
|
||||||
async def test_connection_drop_still_receives_events_after_reconnected(self):
|
async def test_connection_drop_still_receives_events_after_reconnected(self):
|
||||||
address1 = await self.account.receiving.get_or_create_usable_address()
|
address1 = await self.account.receiving.get_or_create_usable_address()
|
|
@ -1,12 +1,12 @@
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from torba.testing import IntegrationTestCase
|
from torba.testcase import IntegrationTestCase
|
||||||
from torba.constants import COIN
|
from torba.client.constants import COIN
|
||||||
|
|
||||||
|
|
||||||
class BasicTransactionTests(IntegrationTestCase):
|
class BasicTransactionTests(IntegrationTestCase):
|
||||||
|
|
||||||
VERBOSITY = logging.WARNING
|
VERBOSITY = logging.WARN
|
||||||
|
|
||||||
async def test_sending_and_receiving(self):
|
async def test_sending_and_receiving(self):
|
||||||
account1, account2 = self.account, self.wallet.generate_account(self.ledger)
|
account1, account2 = self.account, self.wallet.generate_account(self.ledger)
|
BIN
tests/client_tests/unit/bitcoin_headers
Normal file
BIN
tests/client_tests/unit/bitcoin_headers
Normal file
Binary file not shown.
|
@ -1,10 +1,10 @@
|
||||||
from binascii import hexlify
|
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.coin.bitcoinsegwit import MainNetLedger as ledger_class
|
||||||
from torba.baseaccount import HierarchicalDeterministic, SingleKey
|
from torba.client.baseaccount import HierarchicalDeterministic, SingleKey
|
||||||
from torba.wallet import Wallet
|
from torba.client.wallet import Wallet
|
||||||
|
|
||||||
|
|
||||||
class TestHierarchicalDeterministicAccount(AsyncioTestCase):
|
class TestHierarchicalDeterministicAccount(AsyncioTestCase):
|
|
@ -1,6 +1,6 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from torba.bcd_data_stream import BCDataStream
|
from torba.client.bcd_data_stream import BCDataStream
|
||||||
|
|
||||||
|
|
||||||
class TestBCDataStream(unittest.TestCase):
|
class TestBCDataStream(unittest.TestCase):
|
|
@ -1,9 +1,9 @@
|
||||||
from binascii import unhexlify, hexlify
|
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 client_tests.unit.key_fixtures import expected_ids, expected_privkeys, expected_hardened_privkeys
|
||||||
from torba.bip32 import PubKey, PrivateKey, from_extended_key_string
|
from torba.client.bip32 import PubKey, PrivateKey, from_extended_key_string
|
||||||
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
|
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from types import GeneratorType
|
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.coin.bitcoinsegwit import MainNetLedger as ledger_class
|
||||||
from torba.coinselection import CoinSelector, MAXIMUM_TRIES
|
from torba.client.coinselection import CoinSelector, MAXIMUM_TRIES
|
||||||
from torba.constants import CENT
|
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
|
NULL_HASH = b'\x00'*32
|
|
@ -1,13 +1,13 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from torba.wallet import Wallet
|
from torba.client.wallet import Wallet
|
||||||
from torba.constants import COIN
|
from torba.client.constants import COIN
|
||||||
from torba.coin.bitcoinsegwit import MainNetLedger as ledger_class
|
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):
|
class TestQueryBuilder(unittest.TestCase):
|
|
@ -1,5 +1,5 @@
|
||||||
from unittest import TestCase, mock
|
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):
|
class TestAESEncryptDecrypt(TestCase):
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
from torba.testing import AsyncioTestCase
|
from torba.testcase import AsyncioTestCase
|
||||||
|
|
||||||
from torba.coin.bitcoinsegwit import MainHeaders
|
from torba.coin.bitcoinsegwit import MainHeaders
|
||||||
|
|
|
@ -2,10 +2,10 @@ import os
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
from torba.coin.bitcoinsegwit import MainNetLedger
|
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 client_tests.unit.test_transaction import get_transaction, get_output
|
||||||
from .test_headers import BitcoinHeadersTestCase, block_bytes
|
from client_tests.unit.test_headers import BitcoinHeadersTestCase, block_bytes
|
||||||
|
|
||||||
|
|
||||||
class MockNetwork:
|
class MockNetwork:
|
|
@ -1,7 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
from torba.mnemonic import Mnemonic
|
from torba.client.mnemonic import Mnemonic
|
||||||
|
|
||||||
|
|
||||||
class TestMnemonic(unittest.TestCase):
|
class TestMnemonic(unittest.TestCase):
|
|
@ -1,10 +1,10 @@
|
||||||
import unittest
|
import unittest
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
from torba.bcd_data_stream import BCDataStream
|
from torba.client.bcd_data_stream import BCDataStream
|
||||||
from torba.basescript import Template, ParseError, tokenize, push_data
|
from torba.client.basescript import Template, ParseError, tokenize, push_data
|
||||||
from torba.basescript import PUSH_SINGLE, PUSH_INTEGER, PUSH_MANY, OP_HASH160, OP_EQUAL
|
from torba.client.basescript import PUSH_SINGLE, PUSH_INTEGER, PUSH_MANY, OP_HASH160, OP_EQUAL
|
||||||
from torba.basescript import BaseInputScript, BaseOutputScript
|
from torba.client.basescript import BaseInputScript, BaseOutputScript
|
||||||
|
|
||||||
|
|
||||||
def parse(opcodes, source):
|
def parse(opcodes, source):
|
|
@ -2,11 +2,11 @@ import unittest
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
from itertools import cycle
|
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.coin.bitcoinsegwit import MainNetLedger as ledger_class
|
||||||
from torba.wallet import Wallet
|
from torba.client.wallet import Wallet
|
||||||
from torba.constants import CENT, COIN
|
from torba.client.constants import CENT, COIN
|
||||||
|
|
||||||
|
|
||||||
NULL_HASH = b'\x00'*32
|
NULL_HASH = b'\x00'*32
|
|
@ -1,6 +1,6 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from torba.util import ArithUint256
|
from torba.client.util import ArithUint256
|
||||||
|
|
||||||
|
|
||||||
class TestArithUint256(unittest.TestCase):
|
class TestArithUint256(unittest.TestCase):
|
|
@ -1,11 +1,11 @@
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from torba.testing import AsyncioTestCase
|
from torba.testcase import AsyncioTestCase
|
||||||
|
|
||||||
from torba.coin.bitcoinsegwit import MainNetLedger as BTCLedger
|
from torba.coin.bitcoinsegwit import MainNetLedger as BTCLedger
|
||||||
from torba.coin.bitcoincash import MainNetLedger as BCHLedger
|
from torba.coin.bitcoincash import MainNetLedger as BCHLedger
|
||||||
from torba.basemanager import BaseWalletManager
|
from torba.client.basemanager import BaseWalletManager
|
||||||
from torba.wallet import Wallet, WalletStorage
|
from torba.client.wallet import Wallet, WalletStorage
|
||||||
|
|
||||||
|
|
||||||
class TestWalletCreation(AsyncioTestCase):
|
class TestWalletCreation(AsyncioTestCase):
|
0
torba/client/__init__.py
Normal file
0
torba/client/__init__.py
Normal file
|
@ -2,14 +2,13 @@ import random
|
||||||
import typing
|
import typing
|
||||||
from typing import Dict, Tuple, Type, Optional, Any
|
from typing import Dict, Tuple, Type, Optional, Any
|
||||||
|
|
||||||
from torba.mnemonic import Mnemonic
|
from torba.client.mnemonic import Mnemonic
|
||||||
from torba.bip32 import PrivateKey, PubKey, from_extended_key_string
|
from torba.client.bip32 import PrivateKey, PubKey, from_extended_key_string
|
||||||
from torba.hash import aes_encrypt, aes_decrypt
|
from torba.client.hash import aes_encrypt, aes_decrypt
|
||||||
from torba.constants import COIN
|
from torba.client.constants import COIN
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from torba import baseledger
|
from torba.client import baseledger, wallet as basewallet
|
||||||
from torba import wallet as basewallet
|
|
||||||
|
|
||||||
|
|
||||||
class AddressManager:
|
class AddressManager:
|
|
@ -8,9 +8,9 @@ from typing import Tuple, List, Union, Callable, Any, Awaitable, Iterable
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from torba.hash import TXRefImmutable
|
from torba.client.hash import TXRefImmutable
|
||||||
from torba.basetransaction import BaseTransaction
|
from torba.client.basetransaction import BaseTransaction
|
||||||
from torba.baseaccount import BaseAccount
|
from torba.client.baseaccount import BaseAccount
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
|
@ -5,8 +5,8 @@ from io import BytesIO
|
||||||
from typing import Optional, Iterator, Tuple
|
from typing import Optional, Iterator, Tuple
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
from torba.util import ArithUint256
|
from torba.client.util import ArithUint256
|
||||||
from torba.hash import double_sha256
|
from torba.client.hash import double_sha256
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
|
@ -8,15 +8,13 @@ from typing import Dict, Type, Iterable
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from torba import baseaccount
|
from torba.client import baseaccount, basenetwork, basetransaction
|
||||||
from torba import basenetwork
|
from torba.client.basedatabase import BaseDatabase
|
||||||
from torba import basetransaction
|
from torba.client.baseheader import BaseHeaders
|
||||||
from torba.basedatabase import BaseDatabase
|
from torba.client.coinselection import CoinSelector
|
||||||
from torba.baseheader import BaseHeaders
|
from torba.client.constants import COIN, NULL_HASH32
|
||||||
from torba.coinselection import CoinSelector
|
|
||||||
from torba.constants import COIN, NULL_HASH32
|
|
||||||
from torba.stream import StreamController
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
|
@ -2,8 +2,8 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Type, MutableSequence, MutableMapping
|
from typing import Type, MutableSequence, MutableMapping
|
||||||
|
|
||||||
from torba.baseledger import BaseLedger, LedgerRegistry
|
from torba.client.baseledger import BaseLedger, LedgerRegistry
|
||||||
from torba.wallet import Wallet, WalletStorage
|
from torba.client.wallet import Wallet, WalletStorage
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
|
@ -3,8 +3,8 @@ from binascii import hexlify
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from torba.bcd_data_stream import BCDataStream
|
from torba.client.bcd_data_stream import BCDataStream
|
||||||
from torba.util import subclass_tuple
|
from torba.client.util import subclass_tuple
|
||||||
|
|
||||||
# bitcoin opcodes
|
# bitcoin opcodes
|
||||||
OP_0 = 0x00
|
OP_0 = 0x00
|
|
@ -3,16 +3,15 @@ import typing
|
||||||
from typing import List, Iterable, Optional
|
from typing import List, Iterable, Optional
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
from torba.basescript import BaseInputScript, BaseOutputScript
|
from torba.client.basescript import BaseInputScript, BaseOutputScript
|
||||||
from torba.baseaccount import BaseAccount
|
from torba.client.baseaccount import BaseAccount
|
||||||
from torba.constants import COIN, NULL_HASH32
|
from torba.client.constants import COIN, NULL_HASH32
|
||||||
from torba.bcd_data_stream import BCDataStream
|
from torba.client.bcd_data_stream import BCDataStream
|
||||||
from torba.hash import sha256, TXRef, TXRefImmutable
|
from torba.client.hash import sha256, TXRef, TXRefImmutable
|
||||||
from torba.util import ReadOnlyList
|
from torba.client.util import ReadOnlyList
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from torba import baseledger
|
from torba.client import baseledger
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
""" Logic for BIP32 Hierarchical Key Derivation. """
|
""" Logic for BIP32 Hierarchical Key Derivation. """
|
||||||
from coincurve import PublicKey, PrivateKey as _PrivateKey
|
from coincurve import PublicKey, PrivateKey as _PrivateKey
|
||||||
|
|
||||||
from torba.hash import Base58, hmac_sha512, hash160, double_sha256
|
from torba.client.hash import Base58, hmac_sha512, hash160, double_sha256
|
||||||
from torba.util import cachedproperty
|
from torba.client.util import cachedproperty
|
||||||
|
|
||||||
|
|
||||||
class DerivationError(Exception):
|
class DerivationError(Exception):
|
|
@ -3,8 +3,8 @@ import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from torba.testing.node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
|
from torba.orchstr8.node import Conductor, get_ledger_from_environment, get_blockchain_node_from_ledger
|
||||||
from torba.testing.service import TestingServiceAPI
|
from torba.orchstr8.service import TestingServiceAPI
|
||||||
|
|
||||||
|
|
||||||
def get_argument_parser():
|
def get_argument_parser():
|
|
@ -1,7 +1,7 @@
|
||||||
from random import Random
|
from random import Random
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from torba import basetransaction
|
from torba.client import basetransaction
|
||||||
|
|
||||||
MAXIMUM_TRIES = 100000
|
MAXIMUM_TRIES = 100000
|
||||||
|
|
|
@ -18,8 +18,8 @@ from cryptography.hazmat.primitives.ciphers.algorithms import AES
|
||||||
from cryptography.hazmat.primitives.padding import PKCS7
|
from cryptography.hazmat.primitives.padding import PKCS7
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
|
||||||
from torba.util import bytes_to_int, int_to_bytes
|
from torba.client.util import bytes_to_int, int_to_bytes
|
||||||
from torba.constants import NULL_HASH32
|
from torba.client.constants import NULL_HASH32
|
||||||
|
|
||||||
|
|
||||||
class TXRef:
|
class TXRef:
|
|
@ -12,8 +12,8 @@ from secrets import randbelow
|
||||||
|
|
||||||
import pbkdf2
|
import pbkdf2
|
||||||
|
|
||||||
from torba.hash import hmac_sha512
|
from torba.client.hash import hmac_sha512
|
||||||
from torba.words import english
|
from torba.client.words import english
|
||||||
|
|
||||||
# The hash of the mnemonic seed must begin with this
|
# The hash of the mnemonic seed must begin with this
|
||||||
SEED_PREFIX = b'01' # Standard wallet
|
SEED_PREFIX = b'01' # Standard wallet
|
|
@ -5,9 +5,7 @@ import typing
|
||||||
from typing import Sequence, MutableSequence
|
from typing import Sequence, MutableSequence
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from torba import baseaccount
|
from torba.client import basemanager, baseaccount, baseledger
|
||||||
from torba import baseledger
|
|
||||||
from torba import basemanager
|
|
||||||
|
|
||||||
|
|
||||||
class Wallet:
|
class Wallet:
|
0
torba/client/words/__init__.py
Normal file
0
torba/client/words/__init__.py
Normal file
|
@ -7,8 +7,8 @@ __node_url__ = (
|
||||||
__spvserver__ = 'torba.server.coins.BitcoinCashRegtest'
|
__spvserver__ = 'torba.server.coins.BitcoinCashRegtest'
|
||||||
|
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from torba.baseledger import BaseLedger
|
from torba.client.baseledger import BaseLedger
|
||||||
from torba.basetransaction import BaseTransaction
|
from torba.client.basetransaction import BaseTransaction
|
||||||
from .bitcoinsegwit import MainHeaders, UnverifiedHeaders
|
from .bitcoinsegwit import MainHeaders, UnverifiedHeaders
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ __spvserver__ = 'torba.server.coins.BitcoinSegwitRegtest'
|
||||||
import struct
|
import struct
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
from torba.baseledger import BaseLedger
|
from torba.client.baseledger import BaseLedger
|
||||||
from torba.baseheader import BaseHeaders, ArithUint256
|
from torba.client.baseheader import BaseHeaders, ArithUint256
|
||||||
|
|
||||||
|
|
||||||
class MainHeaders(BaseHeaders):
|
class MainHeaders(BaseHeaders):
|
||||||
|
|
2
torba/orchstr8/__init__.py
Normal file
2
torba/orchstr8/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
from .node import Conductor
|
||||||
|
from .service import ConductorService
|
89
torba/orchstr8/cli.py
Normal file
89
torba/orchstr8/cli.py
Normal 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()
|
|
@ -14,10 +14,10 @@ import requests
|
||||||
|
|
||||||
from torba.server.server import Server
|
from torba.server.server import Server
|
||||||
from torba.server.env import Env
|
from torba.server.env import Env
|
||||||
from torba.wallet import Wallet
|
from torba.client.wallet import Wallet
|
||||||
from torba.baseledger import BaseLedger, BlockHeightEvent
|
from torba.client.baseledger import BaseLedger, BlockHeightEvent
|
||||||
from torba.basemanager import BaseWalletManager
|
from torba.client.basemanager import BaseWalletManager
|
||||||
from torba.baseaccount import BaseAccount
|
from torba.client.baseaccount import BaseAccount
|
||||||
|
|
||||||
|
|
||||||
def get_manager_from_environment(default_manager=BaseWalletManager):
|
def get_manager_from_environment(default_manager=BaseWalletManager):
|
||||||
|
@ -170,6 +170,7 @@ class SPVNode:
|
||||||
}
|
}
|
||||||
os.environ.update(conf)
|
os.environ.update(conf)
|
||||||
self.server = Server(Env(self.coin_class))
|
self.server = Server(Env(self.coin_class))
|
||||||
|
self.server.bp.prefetcher.polling_delay = 0.5
|
||||||
await self.server.start()
|
await self.server.start()
|
||||||
|
|
||||||
async def stop(self, cleanup=True):
|
async def stop(self, cleanup=True):
|
|
@ -25,7 +25,7 @@ class WebSocketLogHandler(logging.Handler):
|
||||||
self.handleError(record)
|
self.handleError(record)
|
||||||
|
|
||||||
|
|
||||||
class TestingServiceAPI:
|
class ConductorService:
|
||||||
|
|
||||||
def __init__(self, stack: Conductor, loop: asyncio.AbstractEventLoop) -> None:
|
def __init__(self, stack: Conductor, loop: asyncio.AbstractEventLoop) -> None:
|
||||||
self.stack = stack
|
self.stack = stack
|
|
@ -45,6 +45,7 @@ from torba.server.hash import HASHX_LEN, hex_str_to_hash
|
||||||
from torba.server.script import ScriptPubKey, OpCodes
|
from torba.server.script import ScriptPubKey, OpCodes
|
||||||
import torba.server.tx as lib_tx
|
import torba.server.tx as lib_tx
|
||||||
import torba.server.block_processor as block_proc
|
import torba.server.block_processor as block_proc
|
||||||
|
from torba.server.db import DB
|
||||||
import torba.server.daemon as daemon
|
import torba.server.daemon as daemon
|
||||||
from torba.server.session import ElectrumX, DashElectrumX
|
from torba.server.session import ElectrumX, DashElectrumX
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ class Coin(object):
|
||||||
DESERIALIZER = lib_tx.Deserializer
|
DESERIALIZER = lib_tx.Deserializer
|
||||||
DAEMON = daemon.Daemon
|
DAEMON = daemon.Daemon
|
||||||
BLOCK_PROCESSOR = block_proc.BlockProcessor
|
BLOCK_PROCESSOR = block_proc.BlockProcessor
|
||||||
|
DB = DB
|
||||||
HEADER_VALUES = [
|
HEADER_VALUES = [
|
||||||
'version', 'prev_block_hash', 'merkle_root', 'timestamp', 'bits', 'nonce'
|
'version', 'prev_block_hash', 'merkle_root', 'timestamp', 'bits', 'nonce'
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,33 +5,35 @@
|
||||||
# See the file "LICENCE" for information about the copyright
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
'''Class for handling environment configuration and defaults.'''
|
|
||||||
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import resource
|
import resource
|
||||||
|
from os import environ
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
|
||||||
|
from torba.server.util import class_logger
|
||||||
from torba.server.coins import Coin
|
from torba.server.coins import Coin
|
||||||
from torba.server.env_base import EnvBase
|
|
||||||
import torba.server.util as lib_util
|
import torba.server.util as lib_util
|
||||||
|
|
||||||
|
|
||||||
NetIdentity = namedtuple('NetIdentity', 'host tcp_port ssl_port nick_suffix')
|
NetIdentity = namedtuple('NetIdentity', 'host tcp_port ssl_port nick_suffix')
|
||||||
|
|
||||||
|
|
||||||
class Env(EnvBase):
|
class Env:
|
||||||
'''Wraps environment configuration. Optionally, accepts a Coin class
|
|
||||||
as first argument to have ElectrumX serve custom coins not part of
|
|
||||||
the standard distribution.
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Peer discovery
|
# Peer discovery
|
||||||
PD_OFF, PD_SELF, PD_ON = range(3)
|
PD_OFF, PD_SELF, PD_ON = range(3)
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self, coin=None):
|
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.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK'])
|
||||||
self.db_dir = self.required('DB_DIRECTORY')
|
self.db_dir = self.required('DB_DIRECTORY')
|
||||||
self.db_engine = self.default('DB_ENGINE', 'leveldb')
|
self.db_engine = self.default('DB_ENGINE', 'leveldb')
|
||||||
|
@ -55,8 +57,7 @@ class Env(EnvBase):
|
||||||
self.rpc_port = self.integer('RPC_PORT', 8000)
|
self.rpc_port = self.integer('RPC_PORT', 8000)
|
||||||
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
|
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
|
||||||
self.banner_file = self.default('BANNER_FILE', None)
|
self.banner_file = self.default('BANNER_FILE', None)
|
||||||
self.tor_banner_file = self.default('TOR_BANNER_FILE',
|
self.tor_banner_file = self.default('TOR_BANNER_FILE', self.banner_file)
|
||||||
self.banner_file)
|
|
||||||
self.anon_logs = self.boolean('ANON_LOGS', False)
|
self.anon_logs = self.boolean('ANON_LOGS', False)
|
||||||
self.log_sessions = self.integer('LOG_SESSIONS', 3600)
|
self.log_sessions = self.integer('LOG_SESSIONS', 3600)
|
||||||
# Peer discovery
|
# Peer discovery
|
||||||
|
@ -83,6 +84,78 @@ class Env(EnvBase):
|
||||||
for identity in (clearnet_identity, tor_identity)
|
for identity in (clearnet_identity, tor_identity)
|
||||||
if identity is not None]
|
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):
|
def sane_max_sessions(self):
|
||||||
'''Return the maximum number of sessions to permit. Normally this
|
'''Return the maximum number of sessions to permit. Normally this
|
||||||
is MAX_SESSIONS. However, to prevent open file exhaustion, ajdust
|
is MAX_SESSIONS. However, to prevent open file exhaustion, ajdust
|
||||||
|
|
|
@ -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
|
|
|
@ -1,11 +1,8 @@
|
||||||
import signal
|
import signal
|
||||||
import time
|
|
||||||
import logging
|
import logging
|
||||||
from functools import partial
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import torba
|
import torba
|
||||||
from torba.server.db import DB
|
|
||||||
from torba.server.mempool import MemPool, MemPoolAPI
|
from torba.server.mempool import MemPool, MemPoolAPI
|
||||||
from torba.server.session import SessionManager
|
from torba.server.session import SessionManager
|
||||||
|
|
||||||
|
@ -73,21 +70,10 @@ class Server:
|
||||||
self.shutdown_event = asyncio.Event()
|
self.shutdown_event = asyncio.Event()
|
||||||
self.cancellable_tasks = []
|
self.cancellable_tasks = []
|
||||||
|
|
||||||
async def start(self):
|
self.notifications = notifications = Notifications()
|
||||||
env = self.env
|
self.daemon = daemon = env.coin.DAEMON(env.coin, env.daemon_url)
|
||||||
min_str, max_str = env.coin.SESSIONCLS.protocol_min_max_strings()
|
self.db = db = env.coin.DB(env)
|
||||||
self.log.info(f'software version: {torba.__version__}')
|
self.bp = bp = env.coin.BLOCK_PROCESSOR(env, db, daemon, notifications)
|
||||||
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)
|
|
||||||
|
|
||||||
# Set notifications up to implement the MemPoolAPI
|
# Set notifications up to implement the MemPoolAPI
|
||||||
notifications.height = daemon.height
|
notifications.height = daemon.height
|
||||||
|
@ -96,23 +82,31 @@ class Server:
|
||||||
notifications.raw_transactions = daemon.getrawtransactions
|
notifications.raw_transactions = daemon.getrawtransactions
|
||||||
notifications.lookup_utxos = db.lookup_utxos
|
notifications.lookup_utxos = db.lookup_utxos
|
||||||
MemPoolAPI.register(Notifications)
|
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
|
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):
|
def _start_cancellable(run, *args):
|
||||||
_flag = asyncio.Event()
|
_flag = asyncio.Event()
|
||||||
self.cancellable_tasks.append(asyncio.ensure_future(run(*args, _flag)))
|
self.cancellable_tasks.append(asyncio.ensure_future(run(*args, _flag)))
|
||||||
return _flag.wait()
|
return _flag.wait()
|
||||||
|
|
||||||
await _start_cancellable(bp.fetch_and_process_blocks)
|
await _start_cancellable(self.bp.fetch_and_process_blocks)
|
||||||
await db.populate_header_merkle_cache()
|
await self.db.populate_header_merkle_cache()
|
||||||
await _start_cancellable(mempool.keep_synchronized)
|
await _start_cancellable(self.mempool.keep_synchronized)
|
||||||
await _start_cancellable(session_mgr.serve, notifications)
|
await _start_cancellable(self.session_mgr.serve, self.notifications)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
for task in reversed(self.cancellable_tasks):
|
for task in reversed(self.cancellable_tasks):
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.case import _Outcome
|
from unittest.case import _Outcome
|
||||||
from .node import Conductor
|
from torba.orchstr8 import Conductor
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -14,6 +15,12 @@ except ImportError:
|
||||||
def _cancel_all_tasks(loop):
|
def _cancel_all_tasks(loop):
|
||||||
pass
|
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):
|
class AsyncioTestCase(unittest.TestCase):
|
||||||
# Implementation inspired by discussion:
|
# Implementation inspired by discussion:
|
||||||
|
@ -119,12 +126,13 @@ class IntegrationTestCase(AsyncioTestCase):
|
||||||
|
|
||||||
LEDGER = None
|
LEDGER = None
|
||||||
MANAGER = None
|
MANAGER = None
|
||||||
VERBOSITY = logging.WARNING
|
VERBOSITY = logging.WARN
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.conductor = None
|
self.conductor = None
|
||||||
self.blockchain = None
|
self.blockchain = None
|
||||||
|
self.wallet_node = None
|
||||||
self.manager = None
|
self.manager = None
|
||||||
self.ledger = None
|
self.ledger = None
|
||||||
self.wallet = None
|
self.wallet = None
|
||||||
|
@ -136,10 +144,11 @@ class IntegrationTestCase(AsyncioTestCase):
|
||||||
)
|
)
|
||||||
await self.conductor.start()
|
await self.conductor.start()
|
||||||
self.blockchain = self.conductor.blockchain_node
|
self.blockchain = self.conductor.blockchain_node
|
||||||
self.manager = self.conductor.wallet_node.manager
|
self.wallet_node = self.conductor.wallet_node
|
||||||
self.ledger = self.conductor.wallet_node.ledger
|
self.manager = self.wallet_node.manager
|
||||||
self.wallet = self.conductor.wallet_node.wallet
|
self.ledger = self.wallet_node.ledger
|
||||||
self.account = self.conductor.wallet_node.wallet.default_account
|
self.wallet = self.wallet_node.wallet
|
||||||
|
self.account = self.wallet_node.wallet.default_account
|
||||||
|
|
||||||
async def asyncTearDown(self):
|
async def asyncTearDown(self):
|
||||||
await self.conductor.stop()
|
await self.conductor.stop()
|
|
@ -1 +0,0 @@
|
||||||
from .testcase import IntegrationTestCase, AsyncioTestCase
|
|
0
torba/ui/__init__.py
Normal file
0
torba/ui/__init__.py
Normal file
6
tox.ini
6
tox.ini
|
@ -14,9 +14,9 @@ changedir = {toxinidir}/tests
|
||||||
setenv =
|
setenv =
|
||||||
integration: TORBA_LEDGER={envname}
|
integration: TORBA_LEDGER={envname}
|
||||||
commands =
|
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: torba download
|
||||||
integration: coverage run -p --source={envsitepackagesdir}/torba -m unittest integration.test_transactions
|
integration: coverage run -p --source={envsitepackagesdir}/torba -m unittest client_tests.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_reconnect
|
||||||
# Too slow on Travis
|
# Too slow on Travis
|
||||||
# integration: coverage run -p --source={envsitepackagesdir}/torba -m twisted.trial --reactor=asyncio integration.test_blockchain_reorganization
|
# integration: coverage run -p --source={envsitepackagesdir}/torba -m twisted.trial --reactor=asyncio integration.test_blockchain_reorganization
|
||||||
|
|
Loading…
Reference in a new issue