def make_unsigned_transaction(self, *, coins: Sequence[PartialTxInput], outputs: List[PartialTxOutput], fee=None, change_addr: str = None, is_sweep=False) -> PartialTransaction: mk_tx = lambda o: Multisig_Wallet.make_unsigned_transaction( self, coins=coins, outputs=o, fee=fee, change_addr=change_addr) extra_fee = self.extra_fee() if not is_sweep else 0 if extra_fee: address = self.billing_info['billing_address_segwit'] fee_output = PartialTxOutput.from_address_and_value( address, extra_fee) try: tx = mk_tx(outputs + [fee_output]) except NotEnoughFunds: # TrustedCoin won't charge if the total inputs is # lower than their fee tx = mk_tx(outputs) if tx.input_value() >= extra_fee: raise self.logger.info("not charging for this tx") else: tx = mk_tx(outputs) return tx
def make_tx(self, amount): self._logger.debug('make_tx amount = %s' % str(amount)) if self.f_make_tx: tx = self.f_make_tx(amount) else: # default impl coins = self._wallet.wallet.get_spendable_coins(None) outputs = [ PartialTxOutput.from_address_and_value(self.address, amount) ] tx = self._wallet.wallet.make_unsigned_transaction(coins=coins, outputs=outputs, fee=None, rbf=self._rbf) self._logger.debug('fee: %d, inputs: %d, outputs: %d' % (tx.get_fee(), len(tx.inputs()), len(tx.outputs()))) outputs = [] for o in tx.outputs(): outputs.append({ 'address': o.get_ui_address_str(), 'value_sats': o.value, 'is_mine': self._wallet.wallet.is_mine(o.get_ui_address_str()) }) self.outputs = outputs return tx
def parse_address_and_amount(self, line) -> PartialTxOutput: x, y = line.split(',') scriptpubkey = self.parse_output(x) amount = self.parse_amount(y) return PartialTxOutput(scriptpubkey=scriptpubkey, value=amount, is_display=True)
def get_max_amount(self): from electrum.transaction import PartialTxOutput if run_hook('abort_send', self): return '' inputs = self.wallet.get_spendable_coins(None) if not inputs: return '' addr = None if self.send_screen: addr = str(self.send_screen.screen.address) if not addr: addr = self.wallet.dummy_address() outputs = [PartialTxOutput.from_address_and_value(addr, '!')] try: tx = self.wallet.make_unsigned_transaction(coins=inputs, outputs=outputs) except NoDynamicFeeEstimates as e: Clock.schedule_once( lambda dt, bound_e=e: self.show_error(str(bound_e))) return '' except NotEnoughFunds: return '' except InternalAddressCorruption as e: self.show_error(str(e)) send_exception_to_crash_reporter(e) return '' amount = tx.output_value() __, x_fee_amount = run_hook('get_tx_extra_fee', self.wallet, tx) or (None, 0) amount_after_all_fees = amount - x_fee_amount return format_satoshis_plain(amount_after_all_fees, self.decimal_point())
def read_invoice(self): address = str(self.address) if not address: self.app.show_error( _('Recipient not specified.') + ' ' + _('Please scan a Doichain address or a payment request')) return if not self.amount: self.app.show_error(_('Please enter an amount')) return try: amount = self.app.get_amount(self.amount) except: self.app.show_error(_('Invalid amount') + ':\n' + self.amount) return message = self.message if self.is_lightning: return LNInvoice.from_bech32(address) else: # on-chain if self.payment_request: outputs = self.payment_request.get_outputs() else: if not bitcoin.is_address(address): self.app.show_error( _('Invalid Doichain Address') + ':\n' + address) return outputs = [ PartialTxOutput.from_address_and_value(address, amount) ] return self.app.wallet.create_invoice(outputs=outputs, message=message, pr=self.payment_request, URI=self.parsed_URI)
def get_outputs(self, is_max): if self.payto_scriptpubkey: asset = self.win.get_asset_from_spend_tab() script = self.payto_scriptpubkey if is_max: amount_rval = sum([ txin.value_sats() for txin in self.win.get_coins(asset=asset) ], RavenValue()) if asset: amount = amount_rval.assets.get(asset, Satoshis(0)) else: amount = amount_rval.rvn_value else: amount = Satoshis(self.amount_edit.get_amount()) if asset: script = assets.create_transfer_asset_script( script, asset, amount) if amount == 0: self.errors.append( PayToLineError('The amount cannot be 0.', None)) return [] self.outputs = [ PartialTxOutput(scriptpubkey=script, value=amount, asset=asset, is_max=is_max) ] return self.outputs[:]
def parse_address_and_amount(self, line) -> PartialTxOutput: try: x, y = line.split(',') except ValueError: raise Exception("expected two comma-separated values: (address, amount)") from None scriptpubkey = self.parse_output(x) amount = self.parse_amount(y) return PartialTxOutput(scriptpubkey=scriptpubkey, value=amount)
def parse_address_and_amount(self, line) -> PartialTxOutput: try: x, y = line.split(',') except ValueError: raise Exception( "expected two comma-separated values: (address, amount)" ) from None scriptpubkey = self.parse_output(x) amount = self.parse_amount(y) asset = self.win.get_asset_from_spend_tab() if asset is not None: script = assets.create_transfer_asset_script( scriptpubkey, asset, amount) return PartialTxOutput(scriptpubkey=script, value=amount, asset=asset) else: return PartialTxOutput(scriptpubkey=scriptpubkey, value=amount)
def get_outputs(self, is_max): if self.payto_scriptpubkey: if is_max: amount = '!' else: amount = self.amount_edit.get_amount() self.outputs = [PartialTxOutput(scriptpubkey=self.payto_scriptpubkey, value=amount)] return self.outputs[:]
def update_tx(self, onchain_amount): if onchain_amount is None: self.tx = None return outputs = [PartialTxOutput.from_address_and_value(ln_dummy_address(), onchain_amount)] coins = self.window.get_coins() self.tx = self.window.wallet.make_unsigned_transaction( coins=coins, outputs=outputs)
def do_send(self): if not is_address(self.str_recipient): print(_('Invalid address')) return try: amount = int(Decimal(self.str_amount) * COIN) except Exception: print(_('Invalid Amount')) return try: fee = int(Decimal(self.str_fee) * COIN) except Exception: print(_('Invalid Fee')) return if self.wallet.has_password(): password = self.password_dialog() if not password: return else: password = None c = "" while c != "y": c = input("ok to send (y/n)?") if c == "n": return try: tx = self.wallet.mktx(outputs=[ PartialTxOutput.from_address_and_value(self.str_recipient, amount) ], password=password, fee=fee) except Exception as e: print(repr(e)) return if self.str_description: self.wallet.labels[tx.txid()] = self.str_description print(_("Please wait...")) try: self.network.run_from_another_thread( self.network.broadcast_transaction(tx)) except TxBroadcastError as e: msg = e.get_message_for_gui() print(msg) except BestEffortRequestFailed as e: msg = repr(e) print(msg) else: print(_('Payment sent.'))
def get_outputs(self, is_max: bool) -> List[PartialTxOutput]: if self.payto_scriptpubkey: if is_max: amount = '!' else: amount = self.amount_edit.get_amount() if amount is None: return [] self.outputs = [ PartialTxOutput(scriptpubkey=self.payto_scriptpubkey, value=amount) ] return self.outputs[:]
def update_tx(self, onchain_amount): if onchain_amount is None: self.tx = None self.ok_button.setEnabled(False) return outputs = [PartialTxOutput.from_address_and_value(ln_dummy_address(), onchain_amount)] coins = self.window.get_coins() try: self.tx = self.window.wallet.make_unsigned_transaction( coins=coins, outputs=outputs) except (NotEnoughFunds, NoDynamicFeeEstimates) as e: self.tx = None self.ok_button.setEnabled(False)
def do_send(self): if not is_address(self.str_recipient): self.show_message( _('Invalid {name} address').format(name=constants.net.NAME)) return try: amount = int(Decimal(self.str_amount) * constants.net.COIN) except Exception: self.show_message(_('Invalid Amount')) return try: fee = int(Decimal(self.str_fee) * constants.net.COIN) except Exception: self.show_message(_('Invalid Fee')) return if self.wallet.has_password(): password = self.password_dialog() if not password: return else: password = None try: tx = self.wallet.mktx(outputs=[ PartialTxOutput.from_address_and_value(self.str_recipient, amount) ], password=password, fee=fee) except Exception as e: self.show_message(repr(e)) return if self.str_description: self.wallet.labels[tx.txid()] = self.str_description self.show_message(_("Please wait..."), getchar=False) try: self.network.run_from_another_thread( self.network.broadcast_transaction(tx)) except TxBroadcastError as e: msg = e.get_message_for_gui() self.show_message(msg) except BestEffortRequestFailed as e: msg = repr(e) self.show_message(msg) else: self.show_message(_('Payment sent.')) self.do_clear()
def update_tx(self, onchain_amount: Union[int, str]): """Updates the transaction associated with a forward swap.""" if onchain_amount is None: self.tx = None self.ids.ok_button.disabled = True return outputs = [PartialTxOutput.from_address_and_value(ln_dummy_address(), onchain_amount)] coins = self.app.wallet.get_spendable_coins(None) try: self.tx = self.app.wallet.make_unsigned_transaction( coins=coins, outputs=outputs) except (NotEnoughFunds, NoDynamicFeeEstimates): self.tx = None self.ids.ok_button.disabled = True
def _update_tx(self, onchain_amount): """Updates self.tx. No other side-effects.""" if self.is_reverse: return if onchain_amount is None: self.tx = None return outputs = [ PartialTxOutput.from_address_and_value(ln_dummy_address(), onchain_amount) ] coins = self.window.get_coins() try: self.tx = self.window.wallet.make_unsigned_transaction( coins=coins, outputs=outputs) except (NotEnoughFunds, NoDynamicFeeEstimates) as e: self.tx = None
def save_invoice(self): assert self.canSave assert not self._amount.isMax self._logger.debug('saving invoice to %s, amount=%s, message=%s' % (self._recipient, repr(self._amount), self._message)) inv_amt = self._amount.satsInt try: outputs = [PartialTxOutput.from_address_and_value(self._recipient, inv_amt)] self._logger.debug(repr(outputs)) invoice = self._wallet.wallet.create_invoice(outputs=outputs, message=self._message, pr=None, URI=None) except InvoiceError as e: self.invoiceCreateError.emit('fatal', _('Error creating payment') + ':\n' + str(e)) return self.key = self._wallet.wallet.get_key_for_outgoing_invoice(invoice) self._wallet.wallet.save_invoice(invoice) self.invoiceSaved.emit()
def do_send(self): from electrum.bitcoin import is_p2pkh, is_hash160, b58_address_to_hash160, bh2u, TYPE_SCRIPT from electrum.transaction import opcodes, contract_script, PartialTxOutput address = str(self.to_addr) if not address: self.app.show_error( _('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request')) return if is_p2pkh(address): addr_type, hash160 = b58_address_to_hash160(address) hash160 = bh2u(hash160) elif is_hash160(address): hash160 = address.lower() else: self.app.show_error(_('Invalid Bitcoin Address') + ':\n' + address) return if address == self.bind_addr: self.app.show_error(_('You can not send to bind address!')) return try: amount = self.app.get_token_amount(self.amount, self.symbol, self.decimals) except: self.app.show_error( _('Invalid amount') + ':\n' + self.screen.amount) return if self.balance < amount: self.app.show_error(_('token not enough')) return datahex = 'a9059cbb{}{:064x}'.format(hash160.zfill(64), amount) tx_desc = _('Pay out {} {}').format(amount / (10**self.decimals), self.symbol) gas_limit = int(self.gas_limit) gas_price = int(float(self.gas_price) * (10**8)) script = contract_script(gas_limit, gas_price, datahex, self.contract_addr, opcodes.OP_CALL) outputs = [PartialTxOutput(scriptpubkey=script, value=0)] amount = sum(map(lambda x: x[2], outputs)) self._do_send(amount, tx_desc, outputs, gas_limit * gas_price)
def send_onchain(self, address, amount, fee=None, rbf=False): self._logger.info('send_onchain: %s %d' % (address, amount)) coins = self.wallet.get_spendable_coins(None) if not bitcoin.is_address(address): self._logger.warning('Invalid Bitcoin Address: ' + address) return False outputs = [PartialTxOutput.from_address_and_value(address, amount)] self._logger.info(str(outputs)) output_values = [x.value for x in outputs] if any(parse_max_spend(outval) for outval in output_values): output_value = '!' else: output_value = sum(output_values) self._logger.info(str(output_value)) # see qt/confirm_tx_dialog qt/main_window tx = self.wallet.make_unsigned_transaction(coins=coins, outputs=outputs, fee=None) self._logger.info(str(tx.to_json())) use_rbf = bool(self.wallet.config.get('use_rbf', True)) tx.set_rbf(use_rbf) self.sign_and_broadcast(tx)
locktime = 1602565200 # Build the Transaction Input _, privkey, compressed = deserialize_privkey(wif) pubkey = ECPrivkey(privkey).get_public_key_hex(compressed=compressed) prevout = TxOutpoint(txid=txid, out_idx=vout) txin = PartialTxInput(prevout=prevout) txin.nsequence = sequence txin.script_type = script_type expiry = b2x(lx(b2x(locktime))) redeem_script = compile([ expiry, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', pubkey, 'OP_CHECKSIG']) txin.redeem_script = x(redeem_script) # Build the Transaction Output txout = PartialTxOutput.from_address_and_value(address, sats_less_fees) # Build and sign the transaction tx = P2SHPartialTransaction.from_io([txin], [txout], locktime=locktime) tx.version = 1 sig = tx.sign_txin(0, privkey) txin.script_sig = x(compile([sig , redeem_script])) # Get the serialized txn and compute txid txn = tx.serialize() txid = b2lx(sha256d(x(txn))) # Ensure we arrived at where we intended if txid != otxid: print("Did not achive target TXID hash") print("Perhaps R-value hashing needs to be reverted")
def validateRecipient(self, recipient): if not recipient: self.setInvoiceType(QEInvoice.Type.Invalid) return maybe_lightning_invoice = recipient def _payment_request_resolved(request): self._logger.debug('resolved payment request') outputs = request.get_outputs() invoice = self.create_onchain_invoice(outputs, None, request, None) self.setValidOnchainInvoice(invoice) try: self._bip21 = parse_URI(recipient, _payment_request_resolved) if self._bip21: if 'r' in self._bip21 or ('name' in self._bip21 and 'sig' in self._bip21): # TODO set flag in util? # let callback handle state return if ':' not in recipient: # address only self.setValidAddressOnly() self.validationSuccess.emit() return else: # fallback lightning invoice? if 'lightning' in self._bip21: maybe_lightning_invoice = self._bip21['lightning'] except InvalidBitcoinURI as e: self._bip21 = None self._logger.debug(repr(e)) lninvoice = None maybe_lightning_invoice = maybe_extract_lightning_payment_identifier(maybe_lightning_invoice) if maybe_lightning_invoice is not None: try: lninvoice = Invoice.from_bech32(maybe_lightning_invoice) except InvoiceError as e: e2 = e.__cause__ if isinstance(e2, LnInvoiceException): self.validationError.emit('unknown', _("Error parsing Lightning invoice") + f":\n{e2}") self.clear() return if isinstance(e2, lnutil.IncompatibleOrInsaneFeatures): self.validationError.emit('unknown', _("Invoice requires unknown or incompatible Lightning feature") + f":\n{e2!r}") self.clear() return self._logger.exception(repr(e)) if not lninvoice and not self._bip21: self.validationError.emit('unknown',_('Unknown invoice')) self.clear() return if lninvoice: if not self._wallet.wallet.has_lightning(): if not self._bip21: # TODO: lightning onchain fallback in ln invoice #self.validationError.emit('no_lightning',_('Detected valid Lightning invoice, but Lightning not enabled for wallet')) self.setValidLightningInvoice(lninvoice) self.clear() return else: self._logger.debug('flow with LN but not LN enabled AND having bip21 uri') self.setValidOnchainInvoice(self._bip21['address']) else: self.setValidLightningInvoice(lninvoice) if not self._wallet.wallet.lnworker.channels: self.validationWarning.emit('no_channels',_('Detected valid Lightning invoice, but there are no open channels')) else: self.validationSuccess.emit() else: self._logger.debug('flow without LN but having bip21 uri') if 'amount' not in self._bip21: #TODO can we have amount-less invoices? self.validationError.emit('no_amount', 'no amount in uri') return outputs = [PartialTxOutput.from_address_and_value(self._bip21['address'], self._bip21['amount'])] self._logger.debug(outputs) message = self._bip21['message'] if 'message' in self._bip21 else '' invoice = self.create_onchain_invoice(outputs, message, None, self._bip21) self._logger.debug(repr(invoice)) self.setValidOnchainInvoice(invoice) self.validationSuccess.emit()