In `build-electrum-git.sh`, the `-w` CLI arg is removed: it was apparently ignored as we are using a .spec file,
and pyinstaller 5.0+ is now raising a hard error (see https://github.com/pyinstaller/pyinstaller/issues/6660).
```
option(s) not allowed:
--console/--nowindowed/--windowed/--noconsole
makespec options not valid when a .spec file is given
```
-----
In ecc_fast.py, we don't sys.exit() anymore as pyinstaller 5.0+ tries to import electrum during the Analysis phase.
see https://github.com/pyinstaller/pyinstaller/pull/6171
```
57912 INFO: Looking for dynamic libraries
1746 INFO: gettext setting initial language to None
1932 ERROR: libsecp256k1 library failed to load. exceptions: [FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-2.dll' (or one of its depende
ncies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-1.dll' (or one of its dependenc
ies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'C:\\python3\\lib\\site-packages\\electrum\\libsecp256k1-0.dll' (or one of its dependencie
s). Try using the full path with constructor syntax."), FileNotFoundError("Could not find module 'libsecp256k1-2.dll' (or one of its dependencies). Try using the full path with constructor
syntax."), FileNotFoundError("Could not find module 'libsecp256k1-1.dll' (or one of its dependencies). Try using the full path with constructor syntax."), FileNotFoundError("Could not find
module 'libsecp256k1-0.dll' (or one of its dependencies). Try using the full path with constructor syntax.")]
Traceback (most recent call last):
File "C:\python3\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\python3\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "C:\python3\scripts\pyinstaller.exe\__main__.py", line 7, in <module>
File "C:\python3\lib\site-packages\PyInstaller\__main__.py", line 194, in _console_script_run
run()
File "C:\python3\lib\site-packages\PyInstaller\__main__.py", line 180, in run
run_build(pyi_config, spec_file, **vars(args))
File "C:\python3\lib\site-packages\PyInstaller\__main__.py", line 61, in run_build
PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
File "C:\python3\lib\site-packages\PyInstaller\building\build_main.py", line 1006, in main
build(specfile, distpath, workpath, clean_build)
File "C:\python3\lib\site-packages\PyInstaller\building\build_main.py", line 928, in build
exec(code, spec_namespace)
File "deterministic.spec", line 55, in <module>
a = Analysis([home+'run_electrum',
File "C:\python3\lib\site-packages\PyInstaller\building\build_main.py", line 428, in __init__
self.__postinit__()
File "C:\python3\lib\site-packages\PyInstaller\building\datastruct.py", line 184, in __postinit__
self.assemble()
File "C:\python3\lib\site-packages\PyInstaller\building\build_main.py", line 736, in assemble
isolated.call(find_binary_dependencies, self.binaries, self.binding_redirects, collected_packages)
File "C:\python3\lib\site-packages\PyInstaller\isolated\_parent.py", line 372, in call
return isolated.call(function, *args, **kwargs)
File "C:\python3\lib\site-packages\PyInstaller\isolated\_parent.py", line 302, in call
raise RuntimeError(f"Child process call to {function.__name__}() failed with:\n" + output)
RuntimeError: Child process call to find_binary_dependencies() failed with:
File "C:\python3\lib\site-packages\PyInstaller\isolated\_child.py", line 63, in run_next_command
output = function(*args, **kwargs)
File "C:\python3\lib\site-packages\PyInstaller\building\build_main.py", line 177, in find_binary_dependencies
__import__(package)
File "C:\python3\lib\site-packages\electrum\__init__.py", line 20, in <module>
from .wallet import Wallet
File "C:\python3\lib\site-packages\electrum\wallet.py", line 53, in <module>
from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_strpath_to_intpath
File "C:\python3\lib\site-packages\electrum\bip32.py", line 11, in <module>
from . import constants
File "C:\python3\lib\site-packages\electrum\constants.py", line 30, in <module>
from . import bitcoin
File "C:\python3\lib\site-packages\electrum\bitcoin.py", line 35, in <module>
from . import ecc
File "C:\python3\lib\site-packages\electrum\ecc.py", line 39, in <module>
from .ecc_fast import _libsecp256k1, SECP256K1_EC_UNCOMPRESSED
File "C:\python3\lib\site-packages\electrum\ecc_fast.py", line 151, in <module>
sys.exit(f"Error: Failed to load libsecp256k1.")
SystemExit: Error: Failed to load libsecp256k1.
🗯 ERROR: build-electrum-git failed
```
Also, the -OO flag is removed from wine python, for similar reasons:
pyinstaller imports electrum, and in electrum/__init__.py, we raise
if -O is used: 9b1fb0e5fe/electrum/__init__.py (L40)
Windows binaries
✓ These binaries should be reproducible, meaning you should be able to generate binaries that match the official releases.
This assumes an Ubuntu (x86_64) host, but it should not be too hard to adapt to another similar system.
-
Install Docker
See
contrib/docker_notes.md.Note: older versions of Docker might not work well (see #6971). If having problems, try to upgrade to at least
docker 20.10. -
Build Windows binaries
$ ./build.shIf you want reproducibility, try instead e.g.:
$ ELECBUILD_COMMIT=HEAD ELECBUILD_NOCACHE=1 ./build.sh -
The generated binaries are in
./contrib/build-wine/dist.
Code Signing
Electrum Windows builds are signed with a Microsoft Authenticode™ code signing certificate in addition to the GPG-based signatures.
The advantage of using Authenticode is that Electrum users won't receive a Windows SmartScreen warning when starting it.
The release signing procedure involves a signer (the holder of the certificate/key) and one or multiple trusted verifiers:
| Signer | Verifier |
|---|---|
Build .exe files using make_win.sh |
|
Sign .exe with ./sign.sh |
|
| Upload signed files to download server | |
Build .exe files using make_win.sh |
|
Compare files using unsign.sh |
|
Sign .exe file using gpg -b |
| Signer and verifiers: |
|---|
Upload signatures to 'electrum-signatures' repo, as $version/$filename.$builder.asc |
Verify Integrity of signed binary
Every user can verify that the official binary was created from the source code in this repository. To do so, the Authenticode signature needs to be stripped since the signature is not reproducible.
This procedure removes the differences between the signed and unsigned binary:
- Remove the signature from the signed binary using osslsigncode or signtool.
- Set the COFF image checksum for the signed binary to 0x0. This is necessary because pyinstaller doesn't generate a checksum.
- Append null bytes to the unsigned binary until the byte count is a multiple of 8.
The script unsign.sh performs these steps.
FAQ
How to investigate diff between binaries if reproducibility fails?
pyi-archive_viewer is needed, for that run $ pip install pyinstaller.
As a first pass overview, run:
pyi-archive_viewer -l electrum-*.exe1 > f1
pyi-archive_viewer -l electrum-*.exe2 > f2
diff f1 f2 > d
cat d
Then investigate manually:
$ pyi-archive_viewer electrum-*.exe1
? help