Example #1
0
 def test_encrypt_decrypt_wallet(self):
     wallet = self.enc_wallet
     wallet._cmd_runner = Commands(wallet.config, wallet.wallet,
                                   wallet.network, None,
                                   self.enc_wallet_password)
     wallet.encrypt_wallet("secret2", False)
     wallet.decrypt_wallet()
Example #2
0
def run_offline_command(config, config_options):
    cmdname = config.get('cmd')
    cmd = known_commands[cmdname]
    storage = WalletStorage(config.get_wallet_path())
    wallet = Wallet(storage) if cmd.requires_wallet else None
    # check password
    if cmd.requires_password and storage.get('use_encryption'):
        password = config_options.get('password')
        try:
            seed = wallet.check_password(password)
        except InvalidPassword:
            print "Error: This password does not decode this wallet."
            sys.exit(1)
    if cmd.requires_network:
        print "Warning: running command offline"
    # arguments passed to function
    args = map(lambda x: config.get(x), cmd.params)
    # decode json arguments
    args = map(json_decode, args)
    # options
    args += map(lambda x: config.get(x), cmd.options)
    cmd_runner = Commands(config,
                          wallet,
                          None,
                          password=config_options.get('password'),
                          new_password=config_options.get('new_password'))
    func = getattr(cmd_runner, cmd.name)
    result = func(*args)
    # save wallet
    if wallet:
        wallet.storage.write()
    return result
Example #3
0
 def test_encrypt_decrypt_wallet(self):
     self.wallet._cmd_runner = Commands(self.wallet.config,
                                        self.wallet.wallet,
                                        self.wallet.network, None,
                                        "password")
     self.wallet.encrypt_wallet("secret2", False)
     self.wallet.decrypt_wallet()
Example #4
0
 def _get_value_for_name(self, name):
     block_header = self.network.blockchain.read_header(
         self.network.get_local_height() - RECOMMENDED_CLAIMTRIE_HASH_CONFIRMS + 1)
     block_hash = self.network.blockchain.hash_header(block_header)
     d = self._run_cmd_as_defer_to_thread('requestvalueforname', name, block_hash)
     d.addCallback(lambda response: Commands._verify_proof(name, block_header['claim_trie_root'],
                                                           response))
     return d
Example #5
0
 def test_unlock_wallet(self):
     self.wallet._cmd_runner = Commands(self.wallet.config,
                                        self.wallet.wallet,
                                        self.wallet.network, None,
                                        "password")
     cmd_runner = self.wallet.get_cmd_runner()
     cmd_runner.unlock_wallet("password")
     self.assertIsNone(cmd_runner.new_password)
     self.assertEqual(cmd_runner._password, "password")
Example #6
0
 def test_unlock_wallet(self):
     wallet = self.enc_wallet
     wallet._cmd_runner = Commands(wallet.config, wallet.wallet,
                                   wallet.network, None,
                                   self.enc_wallet_password)
     cmd_runner = wallet.get_cmd_runner()
     cmd_runner.unlock_wallet(self.enc_wallet_password)
     self.assertIsNone(cmd_runner.new_password)
     self.assertEqual(cmd_runner._password, self.enc_wallet_password)
Example #7
0
 def _get_value_for_name(self, name):
     block_header = self.network.blockchain.read_header(
         self.network.get_local_height() -
         RECOMMENDED_CLAIMTRIE_HASH_CONFIRMS + 1)
     block_hash = self.network.blockchain.hash_header(block_header)
     d = self._run_cmd_as_defer_to_thread('requestvalueforname', name,
                                          block_hash)
     d.addCallback(lambda response: Commands._verify_proof(
         name, block_header['claim_trie_root'], response))
     return d
Example #8
0
    def test_update_password_keyring_off(self):
        self.wallet.config.use_keyring = False
        self.wallet._cmd_runner = Commands(self.wallet.config,
                                           self.wallet.wallet,
                                           self.wallet.network, None,
                                           "password")

        # no keyring available, so ValueError is expected
        with self.assertRaises(ValueError):
            self.wallet.encrypt_wallet("secret2", True)
Example #9
0
 def _get_value_for_name(self, name):
     height_to_check = self.network.get_local_height() - RECOMMENDED_CLAIMTRIE_HASH_CONFIRMS + 1
     if height_to_check < 0:
         msg = "Height to check is less than 0, blockchain headers are likely not initialized"
         raise Exception(msg)
     block_header = self.network.blockchain.read_header(height_to_check)
     block_hash = self.network.blockchain.hash_header(block_header)
     d = self._run_cmd_as_defer_to_thread('requestvalueforname', name, block_hash)
     d.addCallback(lambda response: Commands._verify_proof(name, block_header['claim_trie_root'],
                                                           response))
     return d
Example #10
0
 def __init__(self, lbryum_path):
     self.config = SimpleConfig()
     self.config.set_key('chain', 'lbrycrd_main')
     self.storage = WalletStorage(lbryum_path)
     self.wallet = Wallet(self.storage)
     self.cmd_runner = Commands(self.config, self.wallet, None)
     if not self.wallet.has_seed():
         seed = self.wallet.make_seed()
         self.wallet.add_seed(seed, "derp")
         self.wallet.create_master_keys("derp")
         self.wallet.create_main_account()
         self.wallet.update_password("derp", "")
     self.network = Network(self.config)
     self.blockchain = get_blockchain(self.config, self.network)
     print self.config.get('chain'), self.blockchain
     self.wallet.storage.write()
Example #11
0
 def __init__(self, config, network):
     DaemonThread.__init__(self)
     self.config = config
     self.network = network
     self.wallets = {}
     self.wallet = self.load_wallet(config.get_wallet_path())
     self.cmd_runner = Commands(self.config, self.wallet, self.network)
     host = config.get('rpchost', 'localhost')
     port = config.get('rpcport', 0)
     self.server = SimpleJSONRPCServer((host, port), logRequests=False)
     with open(lockfile(config), 'w') as f:
         f.write(repr(self.server.socket.getsockname()))
     self.server.timeout = 0.1
     for cmdname in Commands.known_commands:
         self.server.register_function(getattr(self.cmd_runner, cmdname), cmdname)
     self.server.register_function(self.run_cmdline, 'run_cmdline')
     self.server.register_function(self.ping, 'ping')
     self.server.register_function(self.run_daemon, 'daemon')
Example #12
0
 def run_cmdline(self, config_options):
     config = SimpleConfig(config_options)
     cmdname = config.get('cmd')
     cmd = Commands.known_commands[cmdname]
     path = config.get_wallet_path()
     wallet = self.load_wallet(path) if cmd.requires_wallet else None
     # arguments passed to function
     args = map(lambda x: config.get(x), cmd.params)
     # decode json arguments
     args = map(json_decode, args)
     # options
     args += map(lambda x: config.get(x), cmd.options)
     cmd_runner = Commands(config, wallet, self.network,
                           password=config_options.get('password'),
                           new_password=config_options.get('new_password'))
     func = getattr(cmd_runner, cmd.name)
     result = func(*args)
     return result
Example #13
0
 def _get_cmd_runner(self):
     return Commands(self.config, self.wallet, self.network)
Example #14
0
 def command(self, command_name, *args, **kwargs):
     cmd_runner = Commands(self.config, self.wallet, None)
     cmd = known_commands[command_name]
     func = getattr(cmd_runner, cmd.name)
     return func(*args, **kwargs)
Example #15
0
    def get_cmd_runner(self):
        if self._cmd_runner is None:
            self._cmd_runner = Commands(self.config, self.wallet, self.network)

        return self._cmd_runner
Example #16
0
class LBRYumWallet(Wallet):
    def __init__(self, storage, config=None):
        Wallet.__init__(self, storage)
        self._config = config
        self.config = make_config(self._config)
        self.network = None
        self.wallet = None
        self._cmd_runner = None
        self.wallet_unlocked_d = defer.Deferred()
        self.is_first_run = False
        self.printed_retrieving_headers = False
        self._start_check = None
        self._catch_up_check = None
        self._caught_up_counter = 0
        self._lag_counter = 0
        self.blocks_behind = 0
        self.catchup_progress = 0

        # fired when the wallet actually unlocks (wallet_unlocked_d can be called multiple times)
        self.wallet_unlock_success = defer.Deferred()

    def _is_first_run(self):
        return (not self.printed_retrieving_headers and
                self.network.blockchain.retrieving_headers)

    def get_cmd_runner(self):
        if self._cmd_runner is None:
            self._cmd_runner = Commands(self.config, self.wallet, self.network)

        return self._cmd_runner

    def check_locked(self):
        if not self.wallet.use_encryption:
            log.info("Wallet is not encrypted")
            self.wallet_unlock_success.callback(True)
        elif not self._cmd_runner:
            raise Exception("Command runner hasn't been initialized yet")
        elif self._cmd_runner.locked:
            log.info("Waiting for wallet password")
            self.wallet_unlocked_d.addCallback(self.unlock)
        return self.wallet_unlock_success

    def unlock(self, password):
        if self._cmd_runner and self._cmd_runner.locked:
            try:
                self._cmd_runner.unlock_wallet(password)
                self.wallet_unlock_success.callback(True)
                log.info("Unlocked the wallet!")
            except InvalidPassword:
                log.warning("Incorrect password, try again")
                self.wallet_unlocked_d = defer.Deferred()
                self.wallet_unlocked_d.addCallback(self.unlock)
                return defer.succeed(False)
        return defer.succeed(True)

    def _start(self):
        network_start_d = defer.Deferred()

        def setup_network():
            self.network = Network(self.config)
            log.info("Loading the wallet")
            return defer.succeed(self.network.start())

        def check_started():
            if self.network.is_connecting():
                if self._is_first_run():
                    log.info("Running the wallet for the first time. This may take a moment.")
                    self.printed_retrieving_headers = True
                return False
            self._start_check.stop()
            self._start_check = None
            if self.network.is_connected():
                network_start_d.callback(True)
            else:
                network_start_d.errback(ValueError("Failed to connect to network."))

        self._start_check = task.LoopingCall(check_started)

        d = setup_network()
        d.addCallback(lambda _: self._load_wallet())
        d.addCallback(lambda _: self._start_check.start(.1))
        d.addCallback(lambda _: network_start_d)
        d.addCallback(lambda _: self._load_blockchain())
        d.addCallback(lambda _: log.info("Subscribing to addresses"))
        d.addCallback(lambda _: self.wallet.wait_until_synchronized(lambda _: None))
        d.addCallback(lambda _: log.info("Synchronized wallet"))
        d.addCallback(lambda _: self.get_cmd_runner())
        d.addCallbacks(lambda _: log.info("Set up lbryum command runner"))
        return d

    def _stop(self):
        if self._start_check is not None:
            self._start_check.stop()
            self._start_check = None

        if self._catch_up_check is not None:
            if self._catch_up_check.running:
                self._catch_up_check.stop()
            self._catch_up_check = None

        d = defer.Deferred()

        def check_stopped():
            if self.network:
                if self.network.is_connected():
                    return False
            stop_check.stop()
            self.network = None
            d.callback(True)

        if self.wallet:
            self.wallet.stop_threads()
            log.info("Stopped wallet")
        if self.network:
            self.network.stop()
            log.info("Stopped connection to lbryum server")

        stop_check = task.LoopingCall(check_stopped)
        stop_check.start(.1)
        return d

    def _load_wallet(self):
        path = self.config.get_wallet_path()
        storage = lbryum_wallet.WalletStorage(path)
        wallet = lbryum_wallet.Wallet(storage)
        if not storage.file_exists:
            self.is_first_run = True
            seed = wallet.make_seed()
            wallet.add_seed(seed, None)
            wallet.create_master_keys(None)
            wallet.create_main_account()
            wallet.synchronize()
        self.wallet = wallet
        self._check_large_wallet()
        return defer.succeed(True)

    def _check_large_wallet(self):
        addr_count = len(self.wallet.addresses(include_change=False))
        if addr_count > 1000:
            log.warning("Your wallet is excessively large (%i addresses), "
                        "please follow instructions here: "
                        "https://github.com/lbryio/lbry/issues/437 to reduce your wallet size",
                        addr_count)
        else:
            log.info("Wallet has %i addresses", addr_count)

    def _load_blockchain(self):
        blockchain_caught_d = defer.Deferred()

        def on_update_callback(event, *args):
            # This callback is called by lbryum when something chain
            # related has happened
            local_height = self.network.get_local_height()
            remote_height = self.network.get_server_height()
            updated_blocks_behind = self.network.get_blocks_behind()
            log.info(
                'Local Height: %s, remote height: %s, behind: %s',
                local_height, remote_height, updated_blocks_behind)

            self.blocks_behind = updated_blocks_behind
            if local_height != remote_height:
                return

            assert self.blocks_behind == 0
            self.network.unregister_callback(on_update_callback)
            log.info("Wallet Loaded")
            reactor.callFromThread(blockchain_caught_d.callback, True)

        self.network.register_callback(on_update_callback, ['updated'])

        d = defer.succeed(self.wallet.start_threads(self.network))
        d.addCallback(lambda _: blockchain_caught_d)
        return d

    # run commands as a defer.succeed,
    # lbryum commands should be run this way , unless if the command
    # only makes a lbrum server query, use _run_cmd_as_defer_to_thread()
    def _run_cmd_as_defer_succeed(self, command_name, *args, **kwargs):
        cmd_runner = self.get_cmd_runner()
        cmd = Commands.known_commands[command_name]
        func = getattr(cmd_runner, cmd.name)
        return defer.succeed(func(*args, **kwargs))

    # run commands as a deferToThread,  lbryum commands that only make
    # queries to lbryum server should be run this way
    # TODO: keep track of running threads and cancel them on `stop`
    #       otherwise the application will hang, waiting for threads to complete
    def _run_cmd_as_defer_to_thread(self, command_name, *args, **kwargs):
        cmd_runner = self.get_cmd_runner()
        cmd = Commands.known_commands[command_name]
        func = getattr(cmd_runner, cmd.name)
        return threads.deferToThread(func, *args, **kwargs)

    def _update_balance(self):
        accounts = None
        exclude_claimtrietx = True
        d = self._run_cmd_as_defer_succeed('getbalance', accounts, exclude_claimtrietx)
        d.addCallback(
            lambda result: Decimal(result['confirmed']) + Decimal(result.get('unconfirmed', 0.0)))
        return d

    # Always create and return a brand new address
    def get_new_address(self, for_change=False, account=None):
        return defer.succeed(self.wallet.create_new_address(account=account,
                                                            for_change=for_change))

    # Get the balance of a given address.
    def get_address_balance(self, address, include_balance=False):
        c, u, x = self.wallet.get_addr_balance(address)
        if include_balance is False:
            return Decimal(float(c) / COIN)
        else:
            return Decimal((float(c) + float(u) + float(x)) / COIN)

    @defer.inlineCallbacks
    def create_addresses_with_balance(self, num_addresses, amount, broadcast=True):
        addresses = self.wallet.get_unused_addresses(account=None)
        if len(addresses) > num_addresses:
            addresses = addresses[:num_addresses]
        elif len(addresses) < num_addresses:
            for i in range(len(addresses), num_addresses):
                address = self.wallet.create_new_address(account=None)
                addresses.append(address)

        outputs = [[address, amount] for address in addresses]
        tx = yield self._run_cmd_as_defer_succeed('payto', outputs, broadcast=broadcast)
        defer.returnValue(tx)

    # Return an address with no balance in it, if
    # there is none, create a brand new address
    @defer.inlineCallbacks
    def get_unused_address(self):
        addr = self.wallet.get_unused_address(account=None)
        if addr is None:
            addr = yield self.get_new_address()
        defer.returnValue(addr)

    def get_least_used_address(self, account=None, for_change=False, max_count=100):
        return defer.succeed(self.wallet.get_least_used_address(account, for_change, max_count))

    def get_block(self, blockhash):
        return self._run_cmd_as_defer_to_thread('getblock', blockhash)

    def get_most_recent_blocktime(self):
        height = self.network.get_local_height()
        if height < 0:
            return defer.succeed(None)
        header = self.network.get_header(self.network.get_local_height())
        return defer.succeed(header['timestamp'])

    def get_best_blockhash(self):
        height = self.network.get_local_height()
        if height < 0:
            return defer.succeed(None)
        header = self.network.blockchain.read_header(height)
        return defer.succeed(self.network.blockchain.hash_header(header))

    def _get_blockhash(self, height):
        header = self.network.blockchain.read_header(height)
        return defer.succeed(self.network.blockchain.hash_header(header))

    def _get_transaction(self, txid):
        return self._run_cmd_as_defer_to_thread("gettransaction", txid)

    def get_name_claims(self):
        return self._run_cmd_as_defer_succeed('getnameclaims')

    def _get_claims_for_name(self, name):
        return self._run_cmd_as_defer_to_thread('getclaimsforname', name)

    @defer.inlineCallbacks
    def _send_name_claim(self, name, value, amount,
                            certificate_id=None, claim_address=None, change_address=None):
        log.info("Send claim: %s for %s: %s ", name, amount, value)
        claim_out = yield self._run_cmd_as_defer_succeed('claim', name, value, amount,
                                                         certificate_id=certificate_id,
                                                         claim_addr=claim_address,
                                                         change_addr=change_address)
        defer.returnValue(claim_out)

    @defer.inlineCallbacks
    def _abandon_claim(self, claim_id, txid, nout):
        log.debug("Abandon %s" % claim_id)
        tx_out = yield self._run_cmd_as_defer_succeed('abandon', claim_id, txid, nout)
        defer.returnValue(tx_out)

    @defer.inlineCallbacks
    def _support_claim(self, name, claim_id, amount):
        log.debug("Support %s %s %f" % (name, claim_id, amount))
        claim_out = yield self._run_cmd_as_defer_succeed('support', name, claim_id, amount)
        defer.returnValue(claim_out)

    @defer.inlineCallbacks
    def _tip_claim(self, claim_id, amount):
        log.debug("Tip %s %f", claim_id, amount)
        claim_out = yield self._run_cmd_as_defer_succeed('sendwithsupport', claim_id, amount)
        defer.returnValue(claim_out)

    def _do_send_many(self, payments_to_send):
        def handle_payto_out(payto_out):
            if not payto_out['success']:
                raise Exception("Failed payto, reason:{}".format(payto_out['reason']))
            return payto_out['txid']

        log.debug("Doing send many. payments to send: %s", str(payments_to_send))
        d = self._run_cmd_as_defer_succeed('payto', payments_to_send.iteritems())
        d.addCallback(lambda out: handle_payto_out(out))
        return d

    def _get_value_for_name(self, name):
        if not name:
            raise Exception("No name given")
        return self._run_cmd_as_defer_to_thread('getvalueforname', name)

    def _get_value_for_uri(self, uri):
        if not uri:
            raise Exception("No uri given")
        return self._run_cmd_as_defer_to_thread('getvalueforuri', uri)

    def _get_values_for_uris(self, page, page_size, *uris):
        return self._run_cmd_as_defer_to_thread('getvaluesforuris', False, page, page_size,
                                                *uris)

    def _claim_certificate(self, name, amount):
        return self._run_cmd_as_defer_succeed('claimcertificate', name, amount)

    def _get_certificate_claims(self):
        return self._run_cmd_as_defer_succeed('getcertificateclaims')

    def get_claims_from_tx(self, txid):
        return self._run_cmd_as_defer_to_thread('getclaimsfromtx', txid)

    def _get_claim_by_outpoint(self, txid, nout):
        return self._run_cmd_as_defer_to_thread('getclaimbyoutpoint', txid, nout)

    def _get_claim_by_claimid(self, claim_id):
        return self._run_cmd_as_defer_to_thread('getclaimbyid', claim_id)

    def _get_claims_by_claimids(self, *claim_ids):
        return self._run_cmd_as_defer_to_thread('getclaimsbyids', claim_ids)

    def _get_balance_for_address(self, address):
        return defer.succeed(Decimal(self.wallet.get_addr_received(address)) / COIN)

    def get_nametrie(self):
        return self._run_cmd_as_defer_to_thread('getclaimtrie')

    def _get_history(self):
        return self._run_cmd_as_defer_succeed('claimhistory')

    def _address_is_mine(self, address):
        return self._run_cmd_as_defer_succeed('ismine', address)

    # returns a list of public keys associated with address
    # (could be multiple public keys if a multisig address)
    def get_pub_keys(self, address):
        return self._run_cmd_as_defer_succeed('getpubkeys', address)

    def list_addresses(self):
        return self._run_cmd_as_defer_succeed('listaddresses')

    def list_unspent(self):
        return self._run_cmd_as_defer_succeed('listunspent')

    def send_claim_to_address(self, claim_id, destination, amount):
        return self._run_cmd_as_defer_succeed('sendclaimtoaddress', claim_id, destination, amount)

    def import_certificate_info(self, serialized_certificate_info):
        return self._run_cmd_as_defer_succeed('importcertificateinfo', serialized_certificate_info)

    def export_certificate_info(self, certificate_claim_id):
        return self._run_cmd_as_defer_succeed('exportcertificateinfo', certificate_claim_id)

    def get_certificates_for_signing(self):
        return self._run_cmd_as_defer_succeed('getcertificatesforsigning')

    def claim_renew_all_before_expiration(self, height):
        return self._run_cmd_as_defer_succeed('renewclaimsbeforeexpiration', height)

    def claim_renew(self, txid, nout):
        return self._run_cmd_as_defer_succeed('renewclaim', txid, nout)

    def decrypt_wallet(self):
        if not self.wallet.use_encryption:
            return False
        if not self._cmd_runner:
            return False
        if self._cmd_runner.locked:
            return False
        self._cmd_runner.decrypt_wallet()
        return not self.wallet.use_encryption

    def encrypt_wallet(self, new_password, update_keyring=False):
        if not self._cmd_runner:
            return False
        if self._cmd_runner.locked:
            return False
        self._cmd_runner.update_password(new_password, update_keyring)
        return not self.wallet.use_encryption