2.Electrum 钱包源码研读(二)

前言

在ubuntu上安装了PyCharm,并使用其打开下载的Electrum代码,安装及使用方式如下:
(https://blog.csdn.net/zhuanshu666/article/details/73554885)

代码地址如下:

(https://github.com/spesmilo/electrum)

下面开始代码的运行。

在路径electrum-master下找到 run_electrum 文件,将文件中的所有方法逐一打上断点之后,使用Run->Toggle Line Breakpoint 后 debug 来逐行运行,可以看到代码的运行顺序:

  1. if is_local or is_android
    a. 用于判断是在本地运行或者是在安卓端运行,如果是其中的一项,则将 script_dir + 'packages'这个路径加入 sys.path中
    b. 由于是在本地运行所以此时 is_local=true,is _android=false

在导入这几个依赖是有几个需要注意的地方

  1. if not is_android:该方法的运行是基于第一步的判断
    a. 随后开始跳转到 check_imports() 方法

  2. def check_imports():

    1. 程序会在这个方法中检查try_catch 中的几个依赖是否导入,如果没有导入,程序会报错
    2. 在导入这几个依赖是有几个需要注意的地方
      1. 可以使用本机的终端会cmd工具来导入,也可以使用PyCharm自带的 Terminal终端工具进行命令行的执行
      2. 如果发现某个依赖找不到,可以使用pip search 进行查询
      3. 在导入 dns 这个依赖时,应该使用 pip install dnsPython 而不是 pip install dns
      4. 导入 google.protobuf ,使用 pip install protobuf
    3. 之后程序会顺序执行 Line67- Line324, 直至进入 Line-327
  1. if __name__ == '__main__':

    1. 该方法为程序的主入口方法,进入该方法后程序会检查当前环境的各种配置如:wallet_path、testnet、cmd 等
    2. 此时程序会进入 if cmdname== 'gui':
    3. 而后执行
      • d.init_gui(config, plugins)
      • 进而跳转到
      • daemon.py --> def init_gui(self, config, plugins):在此方法中进行 gui 的初始化操作
  2. daemon.py 文件中的 def init_gui(self, config, plugins):

    1. 在此方法中会首先进行 'gui' 对象的实例化

      • self.gui = gui.ElectrumGui(config, self, plugins)
      • 实例化方法在 _init_.py 中的
      • def __init__(self, config, daemon, plugins):
      • 而后程序会返回daemon.py 文件中的 def init_gui(self, config, plugins):
      • 去执行 try: self.gui.main()
      • 即跳转到_init_.py 中的 def main(self):方法中,代码如下:
      def main(self):
          try:
              self.init_network()
          except UserCancelled:
              return
          except GoBack:
              return
          except BaseException as e:
              traceback.print_exc(file=sys.stdout)
              return
          self.timer.start()
          self.config.open_last_wallet()
          path = self.config.get_wallet_path()
          if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
              return
          signal.signal(signal.SIGINT, lambda *args: self.app.quit())
      
          def quit_after_last_window():
              # on some platforms, not only does exec_ not return but not even
              # aboutToQuit is emitted (but following this, it should be emitted)
              if self.app.quitOnLastWindowClosed():
                  self.app.quit()
          self.app.lastWindowClosed.connect(quit_after_last_window)
      
          def clean_up():
              # Shut down the timer cleanly
              self.timer.stop()
              # clipboard persistence. see http://www.mail-archive.com/[email protected]/msg17328.html
              event = QtCore.QEvent(QtCore.QEvent.Clipboard)
              self.app.sendEvent(self.app.clipboard(), event)
              self.tray.hide()
          self.app.aboutToQuit.connect(clean_up)
      
          # main loop
          self.app.exec_()
          # on some platforms the exec_ call may not return, so use clea
      
    2. 在此方法中,程序会根据本地的路径来判断是否已存在钱包,如存在则打开,不存在则创建新钱包

      1. 首先创建钱包的UI ---
      path = self.config.get_wallet_path()
      if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
      
      1. 随后程序会跳转到_init_.py 中的
      def start_new_window(self, path, uri, app_is_starting=False):
      
      1. start_new_window方法中,程序会再次在本地路径中判断钱包是否存在,如果不存在,则执行
      storage = WalletStorage(path, manual_upgrades=True)
      wizard = InstallWizard(self.config, self.app, self.plugins, storage)
      
      1. 此时程序会跳转到 installwizard.py 文件中执行实例化方法def __init__(self, config, app, plugins, storage):

        1. 在这份代码中首先运行BaseWizard.__init__(self, config, plugins, storage)
          • 改方法存在于base_wizard.py
        2. 在此方法中我们可以修改部分UI,例如钱包的名称:
        self.setWindowTitle('HxWallet  -  ' + _('Install Wizard'))
        
      2. def __init__(self, config, app, plugins, storage):方法中,程序会顺序执行到self.show() 此时 gui画面会开始展示。

      3. 此时程序会跳转到 _init_.py 文件中 执行

        try:
            wallet = wizard.run_and_get_wallet(self.daemon.get_wallet)
        

        在这里来执行一些针对gui上点击事件的操作,例如:用户点击了 ‘取消’ 、‘返回’ 等按钮。
        继而,跳转到 installwizard.py 文件中 的 def run_and_get_wallet(self, get_wallet_from_daemon):方法中。

      4. def run_and_get_wallet(self, get_wallet_from_daemon): 方法中会针对用户的选择例如用户的钱包是否存在进行一些判断。

      5. 在UI上点击了 Next 按钮之后,程序会跳转到

        • installwizard.py文件中的 if self.storage.requires_split():
        • 而后继续顺序执行至:self.run(action)
      6. 此后,程序跳转至base_wizard.py 中的 def new(self):方法,并在此方法中进行UI的初始化工作,初始化完成后,UI上回展示 让用户来选择希望创建的钱包类型。

      7. 选择了其中一项并点击next按钮之后,程序会执行

        def on_wallet_type(self, choice):
           self.wallet_type = choice
           if choice == 'standard':
               action = 'choose_keystore'
           elif choice == 'multisig':
               action = 'choose_multisig'
           elif choice == '2fa':
               self.load_2fa()
               action = self.storage.get_action()
           elif choice == 'imported':
               action = 'import_addresses_or_keys'
           self.run(action)
        

        在这里选择第一项Standard wallet,而后点击 Next

      8. 随后程序会执行 def choose_keystore(self):方法来让用户选择创建种子或使用已有种子来恢复一个钱包,

        • 在弹出的选择页面上选择Create a new seed ,而后点击 Next,
        • 程序会执行base_wizard.py 文件中的def run(self, *args):
        • 在条件判断
        elif hasattr(self, action):
           f = getattr(self, action)
           f(*args
        

        中执行跳转至:def choose_seed_type(self):

      9. 此时程序跳转到 installwizard.py 文件中的 def wizard_dialog(func): ,随后在执行

        try:
            out = func(*args, **kwargs)
        

        时跳转到 Seed 类型的选择页面 Choose Seed type

        • 在这里选择: Standard 并点击Next 按钮
        • 程序进入base_wizard.py 中的
        • def create_standard_seed(self): self.create_seed('standard') 方法
        • 然后再次进入 installwizard.py 文件中的 def wizard_dialog(func):
        • 此时UI页面会跳转到 seed 的显示及提示页面。
        • 在此UI页面上点击Next 按钮,程序会跳转到installwizard.py 中的
          if type(out) is not tuple:
              out = (out,)
          run_next(*out)
          

        进而运行base_wizard.py 中的

        • def request_passphrase(self, seed, opt_passphrase):
        • def confirm_seed(self, seed, passphrase):
        • 后 再次进入 installwizard.py 中的 def wizard_dialog(func)

        之后UI跳转至Confirm Seed 页面

      10. 在输入框中输入Seed 后点击Next 后程序跳转至base_wizard.py中的def confirm_passphrase(self, seed, passphrase):

      11. 随后程序进入 def create_keystore(self, seed, passphrase): 创建秘钥

        • 中进行一系列的判断,如果条件满足,则进行钱包的创建
        • base_wizard.py ---> def create_wallet(self):
        • 在此方法中程序会先判断钱包有无密码,若没有,则执行 self.request_password
        • 来跳转到UI密码设置页面
        • 而后执行def on_password(self, password, *, encrypt_storage, storage_enc_version=STO_EV_USER_PW, encrypt_keystore):
        • 进行密码的设置
      12. 然后程序顺序执行到self.run('create_addresses') 来创建地址

      13. 此方法的定义在 base_wizard.pydef create_addresses(self):

      14. 程序再次回到_init_.py --> w = self.create_window_for_wallet(wallet)

      15. _init_.py --> def create_window_for_wallet(self, wallet):

      16. 程序再次执行 _init_.py --> if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):

        • 而后继续顺序执行值 self.app.exec_()
  3. 至此,一个stantard 类型的钱包创建完毕,也可以看到完整的,包含History Send Receive的UI界面出现

你可能感兴趣的:(2.Electrum 钱包源码研读(二))