• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lbryio / lbry-sdk / 4599645360

pending completion
4599645360

push

github

GitHub
Bump cryptography from 2.5 to 39.0.1

2807 of 6557 branches covered (42.81%)

Branch coverage included in aggregate %.

12289 of 19915 relevant lines covered (61.71%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.93
/lbry/wallet/orchstr8/node.py
1
# pylint: disable=import-error
2
import os
1✔
3
import json
1✔
4
import shutil
1✔
5
import asyncio
1✔
6
import zipfile
1✔
7
import tarfile
1✔
8
import logging
1✔
9
import tempfile
1✔
10
import subprocess
1✔
11
import platform
1✔
12

13
from binascii import hexlify
1✔
14
from typing import Type, Optional
1✔
15
import urllib.request
1✔
16
from uuid import uuid4
1✔
17

18

19
import lbry
1✔
20
from lbry.wallet import Wallet, Ledger, RegTestLedger, WalletManager, Account, BlockHeightEvent
1✔
21
from lbry.conf import KnownHubsList, Config
1✔
22

23
log = logging.getLogger(__name__)
1✔
24

25
try:
1✔
26
    from hub.herald.env import ServerEnv
1✔
27
    from hub.scribe.env import BlockchainEnv
×
28
    from hub.elastic_sync.env import ElasticEnv
×
29
    from hub.herald.service import HubServerService
×
30
    from hub.elastic_sync.service import ElasticSyncService
×
31
    from hub.scribe.service import BlockchainProcessorService
×
32
except ImportError:
1✔
33
    pass
1✔
34

35

36
def get_lbcd_node_from_ledger(ledger_module):
1✔
37
    return LBCDNode(
×
38
        ledger_module.__lbcd_url__,
39
        ledger_module.__lbcd__,
40
        ledger_module.__lbcctl__
41
    )
42

43

44
def get_lbcwallet_node_from_ledger(ledger_module):
1✔
45
    return LBCWalletNode(
×
46
        ledger_module.__lbcwallet_url__,
47
        ledger_module.__lbcwallet__,
48
        ledger_module.__lbcctl__
49
    )
50

51

52
class Conductor:
1✔
53

54
    def __init__(self, seed=None):
1✔
55
        self.manager_module = WalletManager
×
56
        self.lbcd_node = get_lbcd_node_from_ledger(lbry.wallet)
×
57
        self.lbcwallet_node = get_lbcwallet_node_from_ledger(lbry.wallet)
×
58
        self.spv_node = SPVNode()
×
59
        self.wallet_node = WalletNode(
×
60
            self.manager_module, RegTestLedger, default_seed=seed
61
        )
62
        self.lbcd_started = False
×
63
        self.lbcwallet_started = False
×
64
        self.spv_started = False
×
65
        self.wallet_started = False
×
66

67
        self.log = log.getChild('conductor')
×
68

69
    async def start_lbcd(self):
1✔
70
        if not self.lbcd_started:
×
71
            await self.lbcd_node.start()
×
72
            self.lbcd_started = True
×
73

74
    async def stop_lbcd(self, cleanup=True):
1✔
75
        if self.lbcd_started:
×
76
            await self.lbcd_node.stop(cleanup)
×
77
            self.lbcd_started = False
×
78

79
    async def start_spv(self):
1✔
80
        if not self.spv_started:
×
81
            await self.spv_node.start(self.lbcwallet_node)
×
82
            self.spv_started = True
×
83

84
    async def stop_spv(self, cleanup=True):
1✔
85
        if self.spv_started:
×
86
            await self.spv_node.stop(cleanup)
×
87
            self.spv_started = False
×
88

89
    async def start_wallet(self):
1✔
90
        if not self.wallet_started:
×
91
            await self.wallet_node.start(self.spv_node)
×
92
            self.wallet_started = True
×
93

94
    async def stop_wallet(self, cleanup=True):
1✔
95
        if self.wallet_started:
×
96
            await self.wallet_node.stop(cleanup)
×
97
            self.wallet_started = False
×
98

99
    async def start_lbcwallet(self, clean=True):
1✔
100
        if not self.lbcwallet_started:
×
101
            await self.lbcwallet_node.start()
×
102
            if clean:
×
103
                mining_addr = await self.lbcwallet_node.get_new_address()
×
104
                self.lbcwallet_node.mining_addr = mining_addr
×
105
                await self.lbcwallet_node.generate(200)
×
106
            # unlock the wallet for the next 1 hour
107
            await self.lbcwallet_node.wallet_passphrase("password", 3600)
×
108
            self.lbcwallet_started = True
×
109

110
    async def stop_lbcwallet(self, cleanup=True):
1✔
111
        if self.lbcwallet_started:
×
112
            await self.lbcwallet_node.stop(cleanup)
×
113
            self.lbcwallet_started = False
×
114

115
    async def start(self):
1✔
116
        await self.start_lbcd()
×
117
        await self.start_lbcwallet()
×
118
        await self.start_spv()
×
119
        await self.start_wallet()
×
120

121
    async def stop(self):
1✔
122
        all_the_stops = [
×
123
            self.stop_wallet,
124
            self.stop_spv,
125
            self.stop_lbcwallet,
126
            self.stop_lbcd
127
        ]
128
        for stop in all_the_stops:
×
129
            try:
×
130
                await stop()
×
131
            except Exception as e:
×
132
                log.exception('Exception raised while stopping services:', exc_info=e)
×
133

134
    async def clear_mempool(self):
1✔
135
        await self.stop_lbcwallet(cleanup=False)
×
136
        await self.stop_lbcd(cleanup=False)
×
137
        await self.start_lbcd()
×
138
        await self.start_lbcwallet(clean=False)
×
139

140

141
class WalletNode:
1✔
142

143
    def __init__(self, manager_class: Type[WalletManager], ledger_class: Type[Ledger],
1✔
144
                 verbose: bool = False, port: int = 5280, default_seed: str = None,
145
                 data_path: str = None) -> None:
146
        self.manager_class = manager_class
×
147
        self.ledger_class = ledger_class
×
148
        self.verbose = verbose
×
149
        self.manager: Optional[WalletManager] = None
×
150
        self.ledger: Optional[Ledger] = None
×
151
        self.wallet: Optional[Wallet] = None
×
152
        self.account: Optional[Account] = None
×
153
        self.data_path: str = data_path or tempfile.mkdtemp()
×
154
        self.port = port
×
155
        self.default_seed = default_seed
×
156
        self.known_hubs = KnownHubsList()
×
157

158
    async def start(self, spv_node: 'SPVNode', seed=None, connect=True, config=None):
1✔
159
        wallets_dir = os.path.join(self.data_path, 'wallets')
×
160
        wallet_file_name = os.path.join(wallets_dir, 'my_wallet.json')
×
161
        if not os.path.isdir(wallets_dir):
×
162
            os.mkdir(wallets_dir)
×
163
            with open(wallet_file_name, 'w') as wallet_file:
×
164
                wallet_file.write('{"version": 1, "accounts": []}\n')
×
165
        self.manager = self.manager_class.from_config({
×
166
            'ledgers': {
167
                self.ledger_class.get_id(): {
168
                    'api_port': self.port,
169
                    'explicit_servers': [(spv_node.hostname, spv_node.port)],
170
                    'default_servers': Config.lbryum_servers.default,
171
                    'data_path': self.data_path,
172
                    'known_hubs': config.known_hubs if config else KnownHubsList(),
173
                    'hub_timeout': 30,
174
                    'concurrent_hub_requests': 32,
175
                    'fee_per_name_char': 200000
176
                }
177
            },
178
            'wallets': [wallet_file_name]
179
        })
180
        self.manager.config = config
×
181
        self.ledger = self.manager.ledgers[self.ledger_class]
×
182
        self.wallet = self.manager.default_wallet
×
183
        if not self.wallet:
×
184
            raise ValueError('Wallet is required.')
×
185
        if seed or self.default_seed:
×
186
            Account.from_dict(
×
187
                self.ledger, self.wallet, {'seed': seed or self.default_seed}
188
            )
189
        else:
190
            self.wallet.generate_account(self.ledger)
×
191
        self.account = self.wallet.default_account
×
192
        if connect:
×
193
            await self.manager.start()
×
194

195
    async def stop(self, cleanup=True):
1✔
196
        try:
×
197
            await self.manager.stop()
×
198
        finally:
199
            cleanup and self.cleanup()
×
200

201
    def cleanup(self):
1✔
202
        shutil.rmtree(self.data_path, ignore_errors=True)
×
203

204

205
class SPVNode:
1✔
206
    def __init__(self, node_number=1):
1✔
207
        self.node_number = node_number
×
208
        self.controller = None
×
209
        self.data_path = None
×
210
        self.server: Optional[HubServerService] = None
×
211
        self.writer: Optional[BlockchainProcessorService] = None
×
212
        self.es_writer: Optional[ElasticSyncService] = None
×
213
        self.hostname = 'localhost'
×
214
        self.port = 50001 + node_number  # avoid conflict with default daemon
×
215
        self.udp_port = self.port
×
216
        self.elastic_notifier_port = 19080 + node_number
×
217
        self.elastic_services = f'localhost:9200/localhost:{self.elastic_notifier_port}'
×
218
        self.session_timeout = 600
×
219
        self.stopped = True
×
220
        self.index_name = uuid4().hex
×
221

222
    async def start(self, lbcwallet_node: 'LBCWalletNode', extraconf=None):
1✔
223
        if not self.stopped:
×
224
            log.warning("spv node is already running")
×
225
            return
×
226
        self.stopped = False
×
227
        try:
×
228
            self.data_path = tempfile.mkdtemp()
×
229
            conf = {
×
230
                'description': '',
231
                'payment_address': '',
232
                'daily_fee': '0',
233
                'db_dir': self.data_path,
234
                'daemon_url': lbcwallet_node.rpc_url,
235
                'reorg_limit': 100,
236
                'host': self.hostname,
237
                'tcp_port': self.port,
238
                'udp_port': self.udp_port,
239
                'elastic_services': self.elastic_services,
240
                'session_timeout': self.session_timeout,
241
                'max_query_workers': 0,
242
                'es_index_prefix': self.index_name,
243
                'chain': 'regtest',
244
                'index_address_status': False
245
            }
246
            if extraconf:
×
247
                conf.update(extraconf)
×
248
            self.writer = BlockchainProcessorService(
×
249
                BlockchainEnv(db_dir=self.data_path, daemon_url=lbcwallet_node.rpc_url,
250
                              reorg_limit=100, max_query_workers=0, chain='regtest', index_address_status=False)
251
            )
252
            self.server = HubServerService(ServerEnv(**conf))
×
253
            self.es_writer = ElasticSyncService(
×
254
                ElasticEnv(
255
                    db_dir=self.data_path, reorg_limit=100, max_query_workers=0, chain='regtest',
256
                    elastic_notifier_port=self.elastic_notifier_port,
257
                    es_index_prefix=self.index_name,
258
                    filtering_channel_ids=(extraconf or {}).get('filtering_channel_ids'),
259
                    blocking_channel_ids=(extraconf or {}).get('blocking_channel_ids')
260
                )
261
            )
262
            await self.writer.start()
×
263
            await self.es_writer.start()
×
264
            await self.server.start()
×
265
        except Exception as e:
×
266
            self.stopped = True
×
267
            log.exception("failed to start spv node")
×
268
            raise e
×
269

270
    async def stop(self, cleanup=True):
1✔
271
        if self.stopped:
×
272
            log.warning("spv node is already stopped")
×
273
            return
×
274
        try:
×
275
            await self.server.stop()
×
276
            await self.es_writer.delete_index()
×
277
            await self.es_writer.stop()
×
278
            await self.writer.stop()
×
279
            self.stopped = True
×
280
        except Exception as e:
×
281
            log.exception("failed to stop spv node")
×
282
            raise e
×
283
        finally:
284
            cleanup and self.cleanup()
×
285

286
    def cleanup(self):
1✔
287
        shutil.rmtree(self.data_path, ignore_errors=True)
×
288

289

290
class LBCDProcess(asyncio.SubprocessProtocol):
1✔
291

292
    IGNORE_OUTPUT = [
1✔
293
        b'keypool keep',
294
        b'keypool reserve',
295
        b'keypool return',
296
        b'Block submitted',
297
    ]
298

299
    def __init__(self):
1✔
300
        self.ready = asyncio.Event()
×
301
        self.stopped = asyncio.Event()
×
302
        self.log = log.getChild('lbcd')
×
303

304
    def pipe_data_received(self, fd, data):
1✔
305
        if self.log and not any(ignore in data for ignore in self.IGNORE_OUTPUT):
×
306
            if b'Error:' in data:
×
307
                self.log.error(data.decode())
×
308
            else:
309
                self.log.info(data.decode())
×
310
        if b'Error:' in data:
×
311
            self.ready.set()
×
312
            raise SystemError(data.decode())
×
313
        if b'RPCS: RPC server listening on' in data:
×
314
            self.ready.set()
×
315

316
    def process_exited(self):
1✔
317
        self.stopped.set()
×
318
        self.ready.set()
×
319

320

321
class WalletProcess(asyncio.SubprocessProtocol):
1✔
322

323
    IGNORE_OUTPUT = [
1✔
324
    ]
325

326
    def __init__(self):
1✔
327
        self.ready = asyncio.Event()
×
328
        self.stopped = asyncio.Event()
×
329
        self.log = log.getChild('lbcwallet')
×
330
        self.transport: Optional[asyncio.transports.SubprocessTransport] = None
×
331

332
    def pipe_data_received(self, fd, data):
1✔
333
        if self.log and not any(ignore in data for ignore in self.IGNORE_OUTPUT):
×
334
            if b'Error:' in data:
×
335
                self.log.error(data.decode())
×
336
            else:
337
                self.log.info(data.decode())
×
338
        if b'Error:' in data:
×
339
            self.ready.set()
×
340
            raise SystemError(data.decode())
×
341
        if b'WLLT: Finished rescan' in data:
×
342
            self.ready.set()
×
343

344
    def process_exited(self):
1✔
345
        self.stopped.set()
×
346
        self.ready.set()
×
347

348

349
class LBCDNode:
1✔
350
    def __init__(self, url, daemon, cli):
1✔
351
        self.latest_release_url = url
×
352
        self.project_dir = os.path.dirname(os.path.dirname(__file__))
×
353
        self.bin_dir = os.path.join(self.project_dir, 'bin')
×
354
        self.daemon_bin = os.path.join(self.bin_dir, daemon)
×
355
        self.cli_bin = os.path.join(self.bin_dir, cli)
×
356
        self.log = log.getChild('lbcd')
×
357
        self.data_path = tempfile.mkdtemp()
×
358
        self.protocol = None
×
359
        self.transport = None
×
360
        self.hostname = 'localhost'
×
361
        self.peerport = 29246
×
362
        self.rpcport = 29245
×
363
        self.rpcuser = 'rpcuser'
×
364
        self.rpcpassword = 'rpcpassword'
×
365
        self.stopped = True
×
366
        self.running = asyncio.Event()
×
367

368
    @property
1✔
369
    def rpc_url(self):
1✔
370
        return f'http://{self.rpcuser}:{self.rpcpassword}@{self.hostname}:{self.rpcport}/'
×
371

372
    @property
1✔
373
    def exists(self):
1✔
374
        return (
×
375
            os.path.exists(self.cli_bin) and
376
            os.path.exists(self.daemon_bin)
377
        )
378

379
    def download(self):
1✔
380
        uname = platform.uname()
×
381
        target_os = str.lower(uname.system)
×
382
        target_arch = str.replace(uname.machine, 'x86_64', 'amd64')
×
383
        target_platform = target_os + '_' + target_arch
×
384
        self.latest_release_url = str.replace(self.latest_release_url, 'TARGET_PLATFORM', target_platform)
×
385

386
        downloaded_file = os.path.join(
×
387
            self.bin_dir,
388
            self.latest_release_url[self.latest_release_url.rfind('/')+1:]
389
        )
390

391
        if not os.path.exists(self.bin_dir):
×
392
            os.mkdir(self.bin_dir)
×
393

394
        if not os.path.exists(downloaded_file):
×
395
            self.log.info('Downloading: %s', self.latest_release_url)
×
396
            with urllib.request.urlopen(self.latest_release_url) as response:
×
397
                with open(downloaded_file, 'wb') as out_file:
×
398
                    shutil.copyfileobj(response, out_file)
×
399

400
        self.log.info('Extracting: %s', downloaded_file)
×
401

402
        if downloaded_file.endswith('.zip'):
×
403
            with zipfile.ZipFile(downloaded_file) as dotzip:
×
404
                dotzip.extractall(self.bin_dir)
×
405
                # zipfile bug https://bugs.python.org/issue15795
406
                os.chmod(self.cli_bin, 0o755)
×
407
                os.chmod(self.daemon_bin, 0o755)
×
408

409
        elif downloaded_file.endswith('.tar.gz'):
×
410
            with tarfile.open(downloaded_file) as tar:
×
411
                tar.extractall(self.bin_dir)
×
412

413
        return self.exists
×
414

415
    def ensure(self):
1✔
416
        return self.exists or self.download()
×
417

418
    async def start(self):
1✔
419
        if not self.stopped:
×
420
            return
×
421
        self.stopped = False
×
422
        try:
×
423
            assert self.ensure()
×
424
            loop = asyncio.get_event_loop()
×
425
            asyncio.get_child_watcher().attach_loop(loop)
×
426
            command = [
×
427
                self.daemon_bin,
428
                '--notls',
429
                f'--datadir={self.data_path}',
430
                '--regtest', f'--listen=127.0.0.1:{self.peerport}', f'--rpclisten=127.0.0.1:{self.rpcport}',
431
                '--txindex', f'--rpcuser={self.rpcuser}', f'--rpcpass={self.rpcpassword}'
432
            ]
433
            self.log.info(' '.join(command))
×
434
            self.transport, self.protocol = await loop.subprocess_exec(
×
435
                LBCDProcess, *command
436
            )
437
            await self.protocol.ready.wait()
×
438
            assert not self.protocol.stopped.is_set()
×
439
            self.running.set()
×
440
        except asyncio.CancelledError:
×
441
            self.running.clear()
×
442
            self.stopped = True
×
443
            raise
×
444
        except Exception as e:
×
445
            self.running.clear()
×
446
            self.stopped = True
×
447
            log.exception('failed to start lbcd', exc_info=e)
×
448
            raise
×
449

450
    async def stop(self, cleanup=True):
1✔
451
        if self.stopped:
×
452
            return
×
453
        try:
×
454
            if self.transport:
×
455
                self.transport.terminate()
×
456
                await self.protocol.stopped.wait()
×
457
                self.transport.close()
×
458
        except Exception as e:
×
459
            log.exception('failed to stop lbcd', exc_info=e)
×
460
            raise
×
461
        finally:
462
            self.log.info("Done shutting down " + self.daemon_bin)
×
463
            self.stopped = True
×
464
            if cleanup:
×
465
                self.cleanup()
×
466
            self.running.clear()
×
467

468
    def cleanup(self):
1✔
469
        assert self.stopped
×
470
        shutil.rmtree(self.data_path, ignore_errors=True)
×
471

472

473
class LBCWalletNode:
1✔
474
    P2SH_SEGWIT_ADDRESS = "p2sh-segwit"
1✔
475
    BECH32_ADDRESS = "bech32"
1✔
476

477
    def __init__(self, url, lbcwallet, cli):
1✔
478
        self.latest_release_url = url
×
479
        self.project_dir = os.path.dirname(os.path.dirname(__file__))
×
480
        self.bin_dir = os.path.join(self.project_dir, 'bin')
×
481
        self.lbcwallet_bin = os.path.join(self.bin_dir, lbcwallet)
×
482
        self.cli_bin = os.path.join(self.bin_dir, cli)
×
483
        self.log = log.getChild('lbcwallet')
×
484
        self.protocol = None
×
485
        self.transport = None
×
486
        self.hostname = 'localhost'
×
487
        self.lbcd_rpcport = 29245
×
488
        self.lbcwallet_rpcport = 29244
×
489
        self.rpcuser = 'rpcuser'
×
490
        self.rpcpassword = 'rpcpassword'
×
491
        self.data_path = tempfile.mkdtemp()
×
492
        self.stopped = True
×
493
        self.running = asyncio.Event()
×
494
        self.block_expected = 0
×
495
        self.mining_addr = ''
×
496

497
    @property
1✔
498
    def rpc_url(self):
1✔
499
        # FIXME: somehow the hub/sdk doesn't learn the blocks through the Walet RPC port, why?
500
        # return f'http://{self.rpcuser}:{self.rpcpassword}@{self.hostname}:{self.lbcwallet_rpcport}/'
501
        return f'http://{self.rpcuser}:{self.rpcpassword}@{self.hostname}:{self.lbcd_rpcport}/'
×
502

503
    def is_expected_block(self, e: BlockHeightEvent):
1✔
504
        return self.block_expected == e.height
×
505

506
    @property
1✔
507
    def exists(self):
1✔
508
        return (
×
509
            os.path.exists(self.lbcwallet_bin)
510
        )
511

512
    def download(self):
1✔
513
        uname = platform.uname()
×
514
        target_os = str.lower(uname.system)
×
515
        target_arch = str.replace(uname.machine, 'x86_64', 'amd64')
×
516
        target_platform = target_os + '_' + target_arch
×
517
        self.latest_release_url = str.replace(self.latest_release_url, 'TARGET_PLATFORM', target_platform)
×
518

519
        downloaded_file = os.path.join(
×
520
            self.bin_dir,
521
            self.latest_release_url[self.latest_release_url.rfind('/')+1:]
522
        )
523

524
        if not os.path.exists(self.bin_dir):
×
525
            os.mkdir(self.bin_dir)
×
526

527
        if not os.path.exists(downloaded_file):
×
528
            self.log.info('Downloading: %s', self.latest_release_url)
×
529
            with urllib.request.urlopen(self.latest_release_url) as response:
×
530
                with open(downloaded_file, 'wb') as out_file:
×
531
                    shutil.copyfileobj(response, out_file)
×
532

533
        self.log.info('Extracting: %s', downloaded_file)
×
534

535
        if downloaded_file.endswith('.zip'):
×
536
            with zipfile.ZipFile(downloaded_file) as dotzip:
×
537
                dotzip.extractall(self.bin_dir)
×
538
                # zipfile bug https://bugs.python.org/issue15795
539
                os.chmod(self.lbcwallet_bin, 0o755)
×
540

541
        elif downloaded_file.endswith('.tar.gz'):
×
542
            with tarfile.open(downloaded_file) as tar:
×
543
                tar.extractall(self.bin_dir)
×
544

545
        return self.exists
×
546

547
    def ensure(self):
1✔
548
        return self.exists or self.download()
×
549

550
    async def start(self):
1✔
551
        assert self.ensure()
×
552
        loop = asyncio.get_event_loop()
×
553
        asyncio.get_child_watcher().attach_loop(loop)
×
554

555
        command = [
×
556
            self.lbcwallet_bin,
557
            '--noservertls', '--noclienttls',
558
            '--regtest',
559
            f'--rpcconnect=127.0.0.1:{self.lbcd_rpcport}', f'--rpclisten=127.0.0.1:{self.lbcwallet_rpcport}',
560
            '--createtemp', f'--appdata={self.data_path}',
561
            f'--username={self.rpcuser}', f'--password={self.rpcpassword}'
562
        ]
563
        self.log.info(' '.join(command))
×
564
        try:
×
565
            self.transport, self.protocol = await loop.subprocess_exec(
×
566
                WalletProcess, *command
567
            )
568
            self.protocol.transport = self.transport
×
569
            await self.protocol.ready.wait()
×
570
            assert not self.protocol.stopped.is_set()
×
571
            self.running.set()
×
572
            self.stopped = False
×
573
        except asyncio.CancelledError:
×
574
            self.running.clear()
×
575
            raise
×
576
        except Exception as e:
×
577
            self.running.clear()
×
578
            log.exception('failed to start lbcwallet', exc_info=e)
×
579

580
    def cleanup(self):
1✔
581
        assert self.stopped
×
582
        shutil.rmtree(self.data_path, ignore_errors=True)
×
583

584
    async def stop(self, cleanup=True):
1✔
585
        if self.stopped:
×
586
            return
×
587
        try:
×
588
            self.transport.terminate()
×
589
            await self.protocol.stopped.wait()
×
590
            self.transport.close()
×
591
        except Exception as e:
×
592
            log.exception('failed to stop lbcwallet', exc_info=e)
×
593
            raise
×
594
        finally:
595
            self.log.info("Done shutting down " + self.lbcwallet_bin)
×
596
            self.stopped = True
×
597
            if cleanup:
×
598
                self.cleanup()
×
599
            self.running.clear()
×
600

601
    async def _cli_cmnd(self, *args):
1✔
602
        cmnd_args = [
×
603
            self.cli_bin,
604
            f'--rpcuser={self.rpcuser}', f'--rpcpass={self.rpcpassword}', '--notls', '--regtest', '--wallet'
605
        ] + list(args)
606
        self.log.info(' '.join(cmnd_args))
×
607
        loop = asyncio.get_event_loop()
×
608
        asyncio.get_child_watcher().attach_loop(loop)
×
609
        process = await asyncio.create_subprocess_exec(
×
610
            *cmnd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE
611
        )
612
        out, err = await process.communicate()
×
613
        result = out.decode().strip()
×
614
        err = err.decode().strip()
×
615
        if len(result) <= 0 and err.startswith('-'):
×
616
            raise Exception(err)
×
617
        if err and 'creating a default config file' not in err:
×
618
            log.warning(err)
×
619
        self.log.info(result)
×
620
        if result.startswith('error code'):
×
621
            raise Exception(result)
×
622
        return result
×
623

624
    def generate(self, blocks):
1✔
625
        self.block_expected += blocks
×
626
        return self._cli_cmnd('generatetoaddress', str(blocks), self.mining_addr)
×
627

628
    def generate_to_address(self, blocks, addr):
1✔
629
        self.block_expected += blocks
×
630
        return self._cli_cmnd('generatetoaddress', str(blocks), addr)
×
631

632
    def wallet_passphrase(self, passphrase, timeout):
1✔
633
        return self._cli_cmnd('walletpassphrase', passphrase, str(timeout))
×
634

635
    def invalidate_block(self, blockhash):
1✔
636
        return self._cli_cmnd('invalidateblock', blockhash)
×
637

638
    def get_block_hash(self, block):
1✔
639
        return self._cli_cmnd('getblockhash', str(block))
×
640

641
    def sendrawtransaction(self, tx):
1✔
642
        return self._cli_cmnd('sendrawtransaction', tx)
×
643

644
    async def get_block(self, block_hash):
1✔
645
        return json.loads(await self._cli_cmnd('getblock', block_hash, '1'))
×
646

647
    def get_raw_change_address(self):
1✔
648
        return self._cli_cmnd('getrawchangeaddress')
×
649

650
    def get_new_address(self, address_type='legacy'):
1✔
651
        return self._cli_cmnd('getnewaddress', "", address_type)
×
652

653
    async def get_balance(self):
1✔
654
        return await self._cli_cmnd('getbalance')
×
655

656
    def send_to_address(self, address, amount):
1✔
657
        return self._cli_cmnd('sendtoaddress', address, str(amount))
×
658

659
    def send_raw_transaction(self, tx):
1✔
660
        return self._cli_cmnd('sendrawtransaction', tx.decode())
×
661

662
    def create_raw_transaction(self, inputs, outputs):
1✔
663
        return self._cli_cmnd('createrawtransaction', json.dumps(inputs), json.dumps(outputs))
×
664

665
    async def sign_raw_transaction_with_wallet(self, tx):
1✔
666
        # the "withwallet" portion should only come into play if we are doing segwit.
667
        # and "withwallet" doesn't exist on lbcd yet.
668
        result = await self._cli_cmnd('signrawtransaction', tx)
×
669
        return json.loads(result)['hex'].encode()
×
670

671
    def decode_raw_transaction(self, tx):
1✔
672
        return self._cli_cmnd('decoderawtransaction', hexlify(tx.raw).decode())
×
673

674
    def get_raw_transaction(self, txid):
1✔
675
        return self._cli_cmnd('getrawtransaction', txid, '1')
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc