前言
在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 来逐行运行,可以看到代码的运行顺序:
if is_local or is_android
a. 用于判断是在本地运行或者是在安卓端运行,如果是其中的一项,则将 script_dir + 'packages’这个路径加入 sys.path中
b. 由于是在本地运行所以此时is_local=true,is _android=false
在导入这几个依赖是有几个需要注意的地方
-
if not is_android:
该方法的运行是基于第一步的判断
a. 随后开始跳转到 check_imports() 方法 -
def check_imports():
- 程序会在这个方法中检查
try_catch
中的几个依赖是否导入,如果没有导入,程序会报错 - 在导入这几个依赖是有几个需要注意的地方
- 可以使用本机的终端会cmd工具来导入,也可以使用PyCharm自带的 Terminal终端工具进行命令行的执行
- 如果发现某个依赖找不到,可以使用
pip search <model_name>
进行查询 - 在导入 dns 这个依赖时,应该使用
pip install dnsPython
而不是pip install dns
- 导入
google.protobuf
,使用pip install protobuf
- 之后程序会顺序执行 Line67- Line324, 直至进入 Line-327
- 程序会在这个方法中检查
-
if __name__ == '__main__':
- 该方法为程序的主入口方法,进入该方法后程序会检查当前环境的各种配置如:wallet_path、testnet、cmd 等
- 此时程序会进入 i
f cmdname== 'gui':
- 而后执行
d.init_gui(config, plugins)
- 进而跳转到
daemon.py --> def init_gui(self, config, plugins):
在此方法中进行 gui 的初始化操作
-
daemon.py
文件中的def init_gui(self, config, plugins):
-
在此方法中会首先进行 ‘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/pyqt@riverbankcomputing.com/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
-
在此方法中,程序会根据本地的路径来判断是否已存在钱包,如存在则打开,不存在则创建新钱包
- 首先创建钱包的UI —
path = self.config.get_wallet_path() if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
- 随后程序会跳转到
_init_.py
中的
def start_new_window(self, path, uri, app_is_starting=False):
- 在
start_new_window
方法中,程序会再次在本地路径中判断钱包是否存在,如果不存在,则执行
storage = WalletStorage(path, manual_upgrades=True) wizard = InstallWizard(self.config, self.app, self.plugins, storage)
-
此时程序会跳转到
installwizard.py
文件中执行实例化方法def __init__(self, config, app, plugins, storage):
- 在这份代码中首先运行
BaseWizard.__init__(self, config, plugins, storage)
- 改方法存在于
base_wizard.py
中
- 改方法存在于
- 在此方法中我们可以修改部分UI,例如钱包的名称:
self.setWindowTitle('HxWallet - ' + _('Install Wizard'))
- 在这份代码中首先运行
-
在
def __init__(self, config, app, plugins, storage):
方法中,程序会顺序执行到self.show()
此时gui
画面会开始展示。 -
此时程序会跳转到
_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):
方法中。 -
在
def run_and_get_wallet(self, get_wallet_from_daemon):
方法中会针对用户的选择例如用户的钱包是否存在进行一些判断。 -
在UI上点击了
Next
按钮之后,程序会跳转到installwizard.py
文件中的if self.storage.requires_split():
- 而后继续顺序执行至:
self.run(action)
-
此后,程序跳转至
base_wizard.py
中的def new(self):
方法,并在此方法中进行UI的初始化工作,初始化完成后,UI上回展示 让用户来选择希望创建的钱包类型。 -
选择了其中一项并点击
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
-
随后程序会执行
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):
- 在弹出的选择页面上选择
-
此时程序跳转到
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
页面 - 在这里选择:
-
在输入框中输入Seed 后点击
Next
后程序跳转至base_wizard.py
中的def confirm_passphrase(self, seed, passphrase):
-
随后程序进入
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):
- 进行密码的设置
-
然后程序顺序执行到
self.run('create_addresses')
来创建地址 -
此方法的定义在
base_wizard.py
中def create_addresses(self):
-
程序再次回到
_init_.py
-->w = self.create_window_for_wallet(wallet)
-
_init_.py
-->def create_window_for_wallet(self, wallet):
-
程序再次执行
_init_.py
-->if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
- 而后继续顺序执行值
self.app.exec_()
- 而后继续顺序执行值
-
-
至此,一个
stantard
类型的钱包创建完毕,也可以看到完整的,包含History
Send
Receive
的UI界面出现