def _pay_key_fee(self, address, fee_lbc, name): log.info("Pay key fee %s --> %s", dewies_to_lbc(fee_lbc), address) reserved_points = self.wallet.reserve_points(address, fee_lbc) if reserved_points is None: raise InsufficientFundsError( 'Unable to pay the key fee of %s for %s' % (dewies_to_lbc(fee_lbc), name) ) return self.wallet.send_points_to_address(reserved_points, fee_lbc)
async def parse_and_validate_claim_result(self, claim_result, certificate=None): if not claim_result or 'value' not in claim_result: return claim_result claim_result = _decode_claim_result(claim_result) if claim_result['value']: claim_result['has_signature'] = False if claim_result['value'].is_signed: claim_result['has_signature'] = True claim_tx = await self._fetch_tx(claim_result['txid']) if certificate is None: log.info("fetching certificate to check claim signature") channel_id = claim_result['value'].signing_channel_id certificate = (await self.network.get_claims_by_ids(channel_id) ).get(channel_id) if not certificate: log.warning('Certificate %s not found', channel_id) claim_result['channel_name'] = certificate[ 'name'] if certificate else None cert_tx = await self._fetch_tx(certificate['txid'] ) if certificate else None claim_result[ 'signature_is_valid'] = validate_claim_signature_and_get_channel_name( claim_result, certificate, self.ledger, claim_tx=claim_tx, cert_tx=cert_tx) # fixme: workaround while json encoder isnt used here if cert_tx: channel_txo = cert_tx.outputs[certificate['nout']] claim_result['signing_channel'] = { 'name': channel_txo.claim_name, 'claim_id': channel_txo.claim_id, 'value': channel_txo.claim } claim_result['is_channel_signature_valid'] = claim_result[ 'signature_is_valid'] if 'amount' in claim_result: claim_result['amount'] = dewies_to_lbc(claim_result['amount']) claim_result['effective_amount'] = dewies_to_lbc( claim_result['effective_amount']) claim_result['supports'] = [{ 'txid': txid, 'nout': nout, 'amount': dewies_to_lbc(amount) } for (txid, nout, amount) in claim_result['supports']] claim_result['height'] = claim_result.get('height', -1) or -1 claim_result[ 'permanent_url'] = f"lbry://{claim_result['name']}#{claim_result['claim_id']}" return claim_result
def encode_transaction(self, tx): return { 'txid': tx.id, 'height': tx.height, 'inputs': [self.encode_input(txo) for txo in tx.inputs], 'outputs': [self.encode_output(txo) for txo in tx.outputs], 'total_input': dewies_to_lbc(tx.input_sum), 'total_output': dewies_to_lbc(tx.input_sum - tx.fee), 'total_fee': dewies_to_lbc(tx.fee), 'hex': hexlify(tx.raw).decode(), }
def encode_output(self, txo): output = { 'txid': txo.tx_ref.id, 'nout': txo.position, 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), } if txo.is_change is not None: output['is_change'] = txo.is_change if txo.is_my_account is not None: output['is_mine'] = txo.is_my_account if txo.script.is_claim_involved: output.update({ 'name': txo.claim_name, 'claim_id': txo.claim_id, 'permanent_url': txo.permanent_url, 'is_claim': txo.script.is_claim_name, 'is_support': txo.script.is_support_claim, 'is_update': txo.script.is_update_claim }) if txo.script.is_claim_name or txo.script.is_update_claim: output['value'] = txo.claim.claim_dict if txo.claim_name.startswith('@'): output['has_signature'] = txo.has_signature if txo.script.is_claim_name: output['category'] = 'claim' elif txo.script.is_update_claim: output['category'] = 'update' elif txo.script.is_support_claim: output['category'] = 'support' return output
def format_amount_value(obj): if isinstance(obj, dict): for k, v in obj.items(): if k in ('amount', 'effective_amount'): if not isinstance(obj[k], float): obj[k] = dewies_to_lbc(obj[k]) elif k == 'supports' and isinstance(v, list): obj[k] = [{ 'txid': txid, 'nout': nout, 'amount': dewies_to_lbc(amount) } for (txid, nout, amount) in v] elif isinstance(v, (list, dict)): obj[k] = format_amount_value(v) elif isinstance(obj, list): obj = [format_amount_value(o) for o in obj] return obj
def _format_support(outpoint, supported_id, amount, address): return { "txid": outpoint.split(":")[0], "nout": int(outpoint.split(":")[1]), "claim_id": supported_id, "amount": dewies_to_lbc(amount), "address": address, }
def encode_output(self, txo, check_signature=True): tx_height = txo.tx_ref.height best_height = self.ledger.headers.height output = { 'txid': txo.tx_ref.id, 'nout': txo.position, 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), 'height': tx_height, 'confirmations': (best_height + 1) - tx_height if tx_height > 0 else tx_height } if txo.is_change is not None: output['is_change'] = txo.is_change if txo.is_my_account is not None: output['is_mine'] = txo.is_my_account if txo.script.is_claim_involved: output.update({ 'name': txo.claim_name, 'claim_id': txo.claim_id, 'permanent_url': txo.permanent_url, }) if txo.script.is_claim_name or txo.script.is_update_claim: claim = txo.claim output['value'] = claim if claim.is_signed: output['valid_signature'] = None if check_signature and txo.channel is not None: output['channel_name'] = txo.channel.claim_name try: output['valid_signature'] = txo.is_signed_by( txo.channel, self.ledger) except BadSignatureError: output['valid_signature'] = False except ValueError: log.exception( 'txo.id: %s, txo.channel.id:%s, output: %s', txo.id, txo.channel.id, output) output['valid_signature'] = False if txo.script.is_claim_name: output['type'] = 'claim' elif txo.script.is_update_claim: output['type'] = 'update' elif txo.script.is_support_claim: output['type'] = 'support' else: output['type'] = 'basic' return output
def encode_claim_meta(self, meta): for key, value in meta.items(): if key.endswith('_amount'): if isinstance(value, int): meta[key] = dewies_to_lbc(value) if meta.get('creation_height', 0) > 0: meta['creation_timestamp'] = self.ledger.headers[ meta['creation_height']]['timestamp'] return meta
def encode_output(self, txo): output = { 'txid': txo.tx_ref.id, 'nout': txo.position, 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), 'is_claim': txo.script.is_claim_name, 'is_support': txo.script.is_support_claim, 'is_update': txo.script.is_update_claim, } if txo.is_change is not None: output['is_change'] = txo.is_change return output
async def _report_state(self): for account in self.accounts: total_receiving = len((await account.receiving.get_addresses())) total_change = len((await account.change.get_addresses())) channel_count = await account.get_channel_count() claim_count = await account.get_claim_count() balance = dewies_to_lbc(await account.get_balance()) log.info( "Loaded account %s with %s LBC, %d receiving addresses (gap: %d), " "%d change addresses (gap: %d), %d channels, %d certificates and %d claims. ", account.id, balance, total_receiving, account.receiving.gap, total_change, account.change.gap, channel_count, len(account.channel_keys), claim_count)
def as_dict(self) -> typing.Dict: return { "name": self.claim_name, "claim_id": self.claim_id, "address": self.claim_address, "claim_sequence": self.claim_sequence, "value": self.claim, "height": self.height, "amount": dewies_to_lbc(self.amount), "nout": self.nout, "txid": self.txid, "channel_claim_id": self.channel_claim_id, "channel_name": self.channel_name }
def encode_output(self, txo): tx_height = txo.tx_ref.height best_height = self.ledger.headers.height output = { 'txid': txo.tx_ref.id, 'nout': txo.position, 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), 'height': tx_height, 'confirmations': best_height - tx_height if tx_height > 0 else tx_height } if txo.is_change is not None: output['is_change'] = txo.is_change if txo.is_my_account is not None: output['is_mine'] = txo.is_my_account if txo.script.is_claim_involved: output.update({ 'name': txo.claim_name, 'claim_id': txo.claim_id, 'permanent_url': txo.permanent_url, }) if txo.script.is_claim_name or txo.script.is_update_claim: claim = txo.claim output['value'] = claim.claim_dict if claim.has_signature: output['valid_signature'] = None if txo.channel is not None: output['valid_signature'] = claim.validate_signature( txo.get_address(self.ledger), txo.channel.claim ) if txo.script.is_claim_name: output['type'] = 'claim' elif txo.script.is_update_claim: output['type'] = 'update' elif txo.script.is_support_claim: output['type'] = 'support' else: output['type'] = 'basic' # deprecated, will be removed after 0.30 release output['category'] = output['type'] return output
def calculate_effective_amount( amount: str, supports: typing.Optional[typing.List[typing.Dict]] = None) -> str: return dewies_to_lbc( lbc_to_dewies(amount) + sum([lbc_to_dewies(support['amount']) for support in supports]))
def encode_output(self, txo, check_signature=True, include_meta=True): tx_height = txo.tx_ref.height best_height = self.ledger.headers.height output = { 'txid': txo.tx_ref.id, 'nout': txo.position, 'height': tx_height, 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), 'confirmations': (best_height + 1) - tx_height if tx_height > 0 else tx_height, 'timestamp': self.ledger.headers[tx_height]['timestamp'] if tx_height > 0 else None } if txo.is_change is not None: output['is_change'] = txo.is_change if txo.is_my_account is not None: output['is_mine'] = txo.is_my_account if txo.script.is_claim_name: output['type'] = 'claim' output['claim_op'] = 'create' elif txo.script.is_update_claim: output['type'] = 'claim' output['claim_op'] = 'update' elif txo.script.is_support_claim: output['type'] = 'support' else: output['type'] = 'payment' if txo.script.is_claim_involved: output.update({ 'name': txo.claim_name, 'normalized': txo.normalized_name, 'claim_id': txo.claim_id, 'permanent_url': txo.permanent_url }) if include_meta: output['meta'] = self.encode_claim_meta(txo.meta) if txo.script.is_claim_name or txo.script.is_update_claim: try: output['value'] = txo.claim output['value_type'] = txo.claim.claim_type if self.include_protobuf: output['protobuf'] = hexlify(txo.claim.to_bytes()) if txo.channel is not None: output['signing_channel'] = self.encode_output( txo.channel, include_meta=False) if check_signature and txo.claim.is_signed: output['is_channel_signature_valid'] = False if txo.channel: output[ 'is_channel_signature_valid'] = txo.is_signed_by( txo.channel, self.ledger) except DecodeError: pass return output
def encode_output(self, txo, check_signature=True): tx_height = txo.tx_ref.height best_height = self.ledger.headers.height output = { 'txid': txo.tx_ref.id, 'nout': txo.position, 'height': tx_height, 'amount': dewies_to_lbc(txo.amount), 'address': txo.get_address(self.ledger), 'confirmations': (best_height + 1) - tx_height if tx_height > 0 else tx_height } if txo.is_change is not None: output['is_change'] = txo.is_change if txo.is_my_account is not None: output['is_mine'] = txo.is_my_account if txo.script.is_claim_name: output['type'] = 'claim' output['claim_op'] = 'create' elif txo.script.is_update_claim: output['type'] = 'claim' output['claim_op'] = 'update' elif txo.script.is_support_claim: output['type'] = 'support' else: output['type'] = 'payment' if txo.script.is_claim_involved: output.update({ 'name': txo.claim_name, 'claim_id': txo.claim_id, 'permanent_url': txo.permanent_url, }) if txo.script.is_claim_name or txo.script.is_update_claim: output['value'] = txo.claim output['value_type'] = txo.claim.claim_type if self.include_protobuf: output['protobuf'] = hexlify(txo.claim.to_bytes()) if txo.channel is not None: output['signing_channel'] = { 'name': txo.channel.claim_name, 'claim_id': txo.channel.claim_id, 'value': txo.channel.claim } if check_signature and txo.claim.is_signed: output['is_channel_signature_valid'] = False try: output[ 'is_channel_signature_valid'] = txo.is_signed_by( txo.channel, self.ledger) except BadSignatureError: pass except ValueError: log.exception( 'txo.id: %s, txo.channel.id:%s, output: %s', txo.id, txo.channel.id, output) return output
def encode_claim_meta(self, meta): for key, value in meta.items(): if key.endswith('_amount'): if isinstance(value, int): meta[key] = dewies_to_lbc(value) return meta
async def get_history(account: BaseAccount, **constraints): headers = account.ledger.headers txs = await account.get_transactions(**constraints) history = [] for tx in txs: ts = headers[tx.height]['timestamp'] if tx.height > 0 else None item = { 'txid': tx.id, 'timestamp': ts, 'date': datetime.fromtimestamp(ts).isoformat(' ')[:-3] if tx.height > 0 else None, 'confirmations': (headers.height + 1) - tx.height if tx.height > 0 else 0, 'claim_info': [], 'update_info': [], 'support_info': [], 'abandon_info': [] } is_my_inputs = all([txi.is_my_account for txi in tx.inputs]) if is_my_inputs: # fees only matter if we are the ones paying them item['value'] = dewies_to_lbc(tx.net_account_balance + tx.fee) item['fee'] = dewies_to_lbc(-tx.fee) else: # someone else paid the fees item['value'] = dewies_to_lbc(tx.net_account_balance) item['fee'] = '0.0' for txo in tx.my_claim_outputs: item['claim_info'].append({ 'address': txo.get_address(account.ledger), 'balance_delta': dewies_to_lbc(-txo.amount), 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position }) for txo in tx.my_update_outputs: if is_my_inputs: # updating my own claim previous = None for txi in tx.inputs: if txi.txo_ref.txo is not None: other_txo = txi.txo_ref.txo if (other_txo.is_claim or other_txo.script.is_support_claim) \ and other_txo.claim_id == txo.claim_id: previous = other_txo break if previous is not None: item['update_info'].append({ 'address': txo.get_address(account.ledger), 'balance_delta': dewies_to_lbc(previous.amount - txo.amount), 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position }) else: # someone sent us their claim item['update_info'].append({ 'address': txo.get_address(account.ledger), 'balance_delta': dewies_to_lbc(0), 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position }) for txo in tx.my_support_outputs: item['support_info'].append({ 'address': txo.get_address(account.ledger), 'balance_delta': dewies_to_lbc( txo.amount if not is_my_inputs else -txo.amount), 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'is_tip': not is_my_inputs, 'nout': txo.position }) if is_my_inputs: for txo in tx.other_support_outputs: item['support_info'].append({ 'address': txo.get_address(account.ledger), 'balance_delta': dewies_to_lbc(-txo.amount), 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'is_tip': is_my_inputs, 'nout': txo.position }) for txo in tx.my_abandon_outputs: item['abandon_info'].append({ 'address': txo.get_address(account.ledger), 'balance_delta': dewies_to_lbc(txo.amount), 'amount': dewies_to_lbc(txo.amount), 'claim_id': txo.claim_id, 'claim_name': txo.claim_name, 'nout': txo.position }) history.append(item) return history