electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

addresses.py (8888B)


      1 from typing import TYPE_CHECKING
      2 
      3 from kivy.app import App
      4 from kivy.clock import Clock
      5 from kivy.factory import Factory
      6 from kivy.properties import ObjectProperty
      7 from kivy.lang import Builder
      8 from decimal import Decimal
      9 from kivy.uix.popup import Popup
     10 
     11 from electrum.gui.kivy.i18n import _
     12 from ...util import address_colors
     13 
     14 if TYPE_CHECKING:
     15     from ...main_window import ElectrumWindow
     16 
     17 
     18 Builder.load_string('''
     19 <AddressLabel@Label>
     20     text_size: self.width, None
     21     halign: 'left'
     22     valign: 'top'
     23 
     24 <AddressItem@CardItem>
     25     address: ''
     26     memo: ''
     27     amount: ''
     28     status: ''
     29     BoxLayout:
     30         spacing: '8dp'
     31         height: '32dp'
     32         orientation: 'vertical'
     33         Widget
     34         AddressLabel:
     35             text: root.address
     36             shorten: True
     37         Widget
     38         AddressLabel:
     39             text: (root.amount if root.status == 'Funded' else root.status) + '     ' + root.memo
     40             color: .699, .699, .699, 1
     41             font_size: '13sp'
     42             shorten: True
     43         Widget
     44 
     45 <AddressButton@Button>:
     46     background_color: 1, .585, .878, 0
     47     halign: 'center'
     48     text_size: (self.width, None)
     49     shorten: True
     50     size_hint: 0.5, None
     51     default_text: ''
     52     text: self.default_text
     53     padding: '5dp', '5dp'
     54     height: '40dp'
     55     text_color: self.foreground_color
     56     disabled_color: 1, 1, 1, 1
     57     foreground_color: 1, 1, 1, 1
     58     canvas.before:
     59         Color:
     60             rgba: (0.9, .498, 0.745, 1) if self.state == 'down' else self.background_color
     61         Rectangle:
     62             size: self.size
     63             pos: self.pos
     64 
     65 <AddressesDialog@Popup>
     66     id: popup
     67     title: _('Addresses')
     68     message: ''
     69     pr_status: 'Pending'
     70     show_change: 0
     71     show_used: 0
     72     on_message:
     73         self.update()
     74     BoxLayout:
     75         id:box
     76         padding: '12dp', '12dp', '12dp', '12dp'
     77         spacing: '12dp'
     78         orientation: 'vertical'
     79         BoxLayout:
     80             spacing: '6dp'
     81             height: self.minimum_height
     82             size_hint: 1, None
     83             orientation: 'horizontal'
     84             AddressFilter:
     85                 opacity: 1
     86                 size_hint: 1, None
     87                 height: self.minimum_height
     88                 spacing: '5dp'
     89                 AddressButton:
     90                     id: search
     91                     text: {0:_('Receiving'), 1:_('Change'), 2:_('All')}[root.show_change]
     92                     on_release:
     93                         root.show_change = (root.show_change + 1) % 3
     94                         Clock.schedule_once(lambda dt: root.update())
     95             AddressFilter:
     96                 opacity: 1
     97                 size_hint: 1, None
     98                 height: self.minimum_height
     99                 spacing: '5dp'
    100                 AddressButton:
    101                     id: search
    102                     text: {0:_('All'), 1:_('Unused'), 2:_('Funded'), 3:_('Used')}[root.show_used]
    103                     on_release:
    104                         root.show_used = (root.show_used + 1) % 4
    105                         Clock.schedule_once(lambda dt: root.update())
    106             AddressFilter:
    107                 opacity: 1
    108                 size_hint: 1, None
    109                 height: self.minimum_height
    110                 spacing: '5dp'
    111                 canvas.before:
    112                     Color:
    113                         rgba: 0.9, 0.9, 0.9, 1
    114                 AddressButton:
    115                     id: change
    116                     text: root.message if root.message else _('Search')
    117                     on_release: Clock.schedule_once(lambda dt: app.description_dialog(popup))
    118         RecycleView:
    119             scroll_type: ['bars', 'content']
    120             bar_width: '15dp'
    121             viewclass: 'AddressItem'
    122             id: search_container
    123             RecycleBoxLayout:
    124                 orientation: 'vertical'
    125                 default_size: None, dp(56)
    126                 default_size_hint: 1, None
    127                 size_hint_y: None
    128                 height: self.minimum_height
    129 
    130 <AddressPopup@Popup>:
    131     address: ''
    132     balance: ''
    133     status: ''
    134     script_type: ''
    135     pk: ''
    136     address_color: 1, 1, 1, 1
    137     address_background_color: 0.3, 0.3, 0.3, 1
    138     BoxLayout:
    139         orientation: 'vertical'
    140         ScrollView:
    141             GridLayout:
    142                 cols: 1
    143                 height: self.minimum_height
    144                 size_hint_y: None
    145                 padding: '10dp'
    146                 spacing: '10dp'
    147                 TopLabel:
    148                     text: _('Address')
    149                 RefLabel:
    150                     color: root.address_color
    151                     background_color: root.address_background_color
    152                     data: root.address
    153                     name: _('Address')
    154                 GridLayout:
    155                     cols: 1
    156                     size_hint_y: None
    157                     height: self.minimum_height
    158                     spacing: '10dp'
    159                     BoxLabel:
    160                         text: _('Balance')
    161                         value: root.balance
    162                     BoxLabel:
    163                         text: _('Script type')
    164                         value: root.script_type
    165                     BoxLabel:
    166                         text: _('Status')
    167                         value: root.status
    168                 TopLabel:
    169                     text: _('Private Key')
    170                 RefLabel:
    171                     data: root.pk
    172                     name: _('Private key')
    173                     on_touched: if not self.data: root.do_export(self)
    174         Widget:
    175             size_hint: 1, 0.1
    176         BoxLayout:
    177             size_hint: 1, None
    178             height: '48dp'
    179             Button:
    180                 size_hint: 0.5, None
    181                 height: '48dp'
    182                 text: _('Receive')
    183                 on_release: root.receive_at()
    184             Button:
    185                 size_hint: 0.5, None
    186                 height: '48dp'
    187                 text: _('Close')
    188                 on_release: root.dismiss()
    189 ''')
    190 
    191 
    192 
    193 class AddressPopup(Popup):
    194 
    195     def __init__(self, parent, address, balance, status, **kwargs):
    196         super(AddressPopup, self).__init__(**kwargs)
    197         self.title = _('Address Details')
    198         self.parent_dialog = parent
    199         self.app = parent.app
    200         self.address = address
    201         self.status = status
    202         self.script_type = self.app.wallet.get_txin_type(self.address)
    203         self.balance = self.app.format_amount_and_units(balance)
    204         self.address_color, self.address_background_color = address_colors(self.app.wallet, address)
    205 
    206     def receive_at(self):
    207         self.dismiss()
    208         self.parent_dialog.dismiss()
    209         self.app.switch_to('receive')
    210         # retry until receive_screen is set
    211         Clock.schedule_interval(lambda dt: bool(self.app.receive_screen.set_address(self.address) and False) if self.app.receive_screen else True, 0.1)
    212 
    213     def do_export(self, pk_label):
    214         self.app.export_private_keys(pk_label, self.address)
    215 
    216 
    217 class AddressesDialog(Factory.Popup):
    218 
    219     def __init__(self, app):
    220         Factory.Popup.__init__(self)
    221         self.app = app  # type: ElectrumWindow
    222 
    223     def get_card(self, addr, balance, is_used, label):
    224         ci = {}
    225         ci['screen'] = self
    226         ci['address'] = addr
    227         ci['memo'] = label
    228         ci['amount'] = self.app.format_amount_and_units(balance)
    229         ci['status'] = _('Used') if is_used else _('Funded') if balance > 0 else _('Unused')
    230         return ci
    231 
    232     def update(self):
    233         wallet = self.app.wallet
    234         if self.show_change == 0:
    235             _list = wallet.get_receiving_addresses()
    236         elif self.show_change == 1:
    237             _list = wallet.get_change_addresses()
    238         else:
    239             _list = wallet.get_addresses()
    240         search = self.message
    241         container = self.ids.search_container
    242         n = 0
    243         cards = []
    244         for address in _list:
    245             label = wallet.get_label(address)
    246             balance = sum(wallet.get_addr_balance(address))
    247             is_used_and_empty = wallet.is_used(address) and balance == 0
    248             if self.show_used == 1 and (balance or is_used_and_empty):
    249                 continue
    250             if self.show_used == 2 and balance == 0:
    251                 continue
    252             if self.show_used == 3 and not is_used_and_empty:
    253                 continue
    254             card = self.get_card(address, balance, is_used_and_empty, label)
    255             if search and not self.ext_search(card, search):
    256                 continue
    257             cards.append(card)
    258             n += 1
    259         container.data = cards
    260         if not n:
    261             self.app.show_error('No address matching your search')
    262 
    263     def show_item(self, obj):
    264         address = obj.address
    265         c, u, x = self.app.wallet.get_addr_balance(address)
    266         balance = c + u + x
    267         d = AddressPopup(self, address, balance, obj.status)
    268         d.open()
    269 
    270     def ext_search(self, card, search):
    271         return card['memo'].find(search) >= 0 or card['amount'].find(search) >= 0