├── .github ├── pull_request_template.md └── workflows │ ├── build.yml │ ├── telegram.yml │ └── tests.yml ├── .gitignore ├── .gitmodules ├── LICENSE.md ├── README.md ├── docker-compose.yml ├── docker ├── Dockerfile └── setup.sh ├── docs ├── code_structure.md ├── debug_crash.md ├── developer_tips.md ├── dice_verification.md ├── electrum.md ├── feature_roadmap.md ├── img │ ├── Mini_Pill_Main_Photo.jpg │ ├── Open_Pill_Mini_Models.JPG │ ├── Open_Pill_Mini_w_Faceplate.JPG │ ├── Open_Pill_Models.JPG │ ├── Open_Pill_Star.JPG │ ├── Open_Pill_w_Comfort_Joystick.png │ ├── OrangePillMini_Thumb.jpg │ ├── Orange_Pill.JPG │ ├── Orange_Pill_Models.JPG │ ├── Rugged_Pill_Thumb.jpg │ ├── Simple_Pill_Models.JPG │ ├── dice_entr.png │ ├── dice_pic1.png │ ├── dice_pic2.png │ ├── dice_pic3.png │ ├── dice_type.png │ ├── dicedoc │ │ ├── coleman_addresses.png │ │ ├── coleman_change_addresses_1.png │ │ ├── coleman_change_addresses_2.png │ │ ├── coleman_entropy.png │ │ ├── coleman_verify.png │ │ ├── coleman_zpub.png │ │ ├── seedtool_1.png │ │ ├── seedtool_2.png │ │ ├── seedtool_3.png │ │ ├── seedtool_addresses.png │ │ ├── seedtool_change_addresses_1.png │ │ ├── seedtool_change_addresses_2.png │ │ ├── seedtool_fingerprint.png │ │ ├── seedtool_zpub.png │ │ ├── sesi_dice_1.png │ │ ├── sesi_dice_2.png │ │ ├── sesi_export_xpub_1.png │ │ ├── sesi_export_xpub_2.png │ │ ├── sesi_finger_print.png │ │ ├── sesi_seed_1.png │ │ ├── sesi_seed_2.png │ │ ├── sesi_tools_dice_seed.png │ │ ├── sparrow_wallet_1.png │ │ ├── sparrow_wallet_2.png │ │ └── sparrow_zpub.png │ ├── legacy_hardware_remapped_pins.jpg │ ├── legacy_hardware_remapped_pins_photo.jpg │ ├── logo.svg │ ├── smartcard_pn532_headerconnection.jpg │ ├── smartcard_usb_readers.png │ ├── usb_relay_01.png │ ├── usb_relay_linux_01.png │ ├── usb_relay_linux_02.png │ ├── usb_relay_mac_01.png │ ├── usb_relay_mac_02.png │ ├── usb_relay_mac_03.png │ └── usb_relay_mac_04.png ├── legacy_hardware.md ├── qr_formats.md ├── raspberry_pi_os_build_instructions.md ├── recovery.md ├── seed_qr │ ├── README.md │ ├── img │ │ ├── compact_12word.png │ │ ├── compact_24word.png │ │ ├── handmade_qr.jpg │ │ ├── phone_screenshot_compact.jpg │ │ ├── phone_screenshot_standard.jpg │ │ ├── qrcode_capacity.png │ │ ├── seedqr_plate.jpg │ │ ├── standard_12word.png │ │ ├── standard_24word.png │ │ ├── standard_24word_with_logo.png │ │ ├── vector1_compact_24word.png │ │ ├── vector1_standard_24word.png │ │ ├── vector2_compact_24word.png │ │ ├── vector2_standard_24word.png │ │ ├── vector3_compact_24word.png │ │ ├── vector3_standard_24word.png │ │ ├── vector4_compact_12word.png │ │ ├── vector4_standard_12word.png │ │ ├── vector5_compact_12word.png │ │ ├── vector5_standard_12word.png │ │ ├── vector6_compact_12word.png │ │ ├── vector6_standard_12word.png │ │ ├── vector7_compact_12word.png │ │ ├── vector8_compact_12word.png │ │ ├── vector9_compact_12word.png │ │ └── zxing_screenshot.png │ └── printable_templates │ │ ├── 21x21_A4_trading_card_2sided.pdf │ │ ├── 21x21_letter_trading_card_2sided.pdf │ │ ├── 25x25_A4_trading_card_2sided.pdf │ │ ├── 25x25_letter_trading_card_2sided.pdf │ │ ├── 29x29_A4_trading_card_2sided.pdf │ │ ├── 29x29_letter_trading_card_2sided.pdf │ │ ├── dots_21x21.pdf │ │ ├── dots_25x25.pdf │ │ ├── dots_29x29.pdf │ │ ├── grid_21x21.pdf │ │ ├── grid_25x25.pdf │ │ ├── grid_29x29.pdf │ │ ├── grid_wfingerprint_21x21.pdf │ │ ├── grid_wfingerprint_21x21_4-up.pdf │ │ ├── grid_wfingerprint_25x25.pdf │ │ ├── grid_wfingerprint_29x29.pdf │ │ ├── mnemonic_12words_wfingerprint.pdf │ │ ├── mnemonic_12words_wfingerprint_4up.pdf │ │ ├── mnemonic_24word_wfingerprint.pdf │ │ ├── mnemonic_24word_wfingerprint_4-up.pdf │ │ ├── trading_card_21x21_w12words.pdf │ │ ├── trading_card_25x25_w24words.pdf │ │ ├── trading_card_29x29_w24words.pdf │ │ └── xpub_wfingerprint.pdf ├── smartcard_support_installation.md └── usb_relay.md ├── enclosures ├── look_screws_pill │ ├── Bottom.stl │ ├── ButtonsConnected.stl │ ├── README.md │ ├── Top.stl │ └── pressPad.stl ├── open_pill │ ├── README.md │ ├── design_file │ │ └── open_pill.f3d │ └── print_file │ │ └── open_pill.stl ├── open_pill_mini │ ├── Main_Chassis.stl │ └── README.md ├── open_pill_mini_w_coverplate │ ├── Buttons.stl │ ├── Lid.stl │ ├── Main_Chassis.stl │ ├── README.md │ ├── Thumbstick_PLA.stl │ └── Thumbstick_TPU.stl ├── orange_pill │ ├── README.md │ ├── design_files │ │ ├── OP_Lower.f3d │ │ ├── OP_Upper.f3d │ │ ├── button.f3d │ │ └── joystiick.f3d │ └── print_files │ │ ├── OP_Lower.stl │ │ ├── OP_Upper.stl │ │ ├── button.stl │ │ └── joystick.stl ├── orange_pill_mini │ ├── OrangePillMini_Bottom.3mf │ ├── OrangePillMini_Buttons.stl │ ├── OrangePillMini_Top.stl │ ├── README.md │ ├── ThumbStick_small.stl │ └── Trackball.stl ├── pushcase │ ├── 3mf │ │ ├── Bottom - Push Case - SeedSigner.3mf │ │ ├── Buttons - Push Case - SeedSigner.3mf │ │ ├── Middle - Push Case - SeedSigner.3mf │ │ ├── Soldering Support - Push Case - SeedSigner.3mf │ │ └── Top - Push Case - SeedSigner.3mf │ ├── README.md │ ├── images │ │ ├── back1.png │ │ ├── back2.png │ │ ├── camera1.png │ │ ├── camera2.png │ │ ├── camera3.png │ │ ├── camera4.png │ │ ├── camera5.png │ │ ├── components.png │ │ ├── front1.png │ │ ├── front2.png │ │ ├── front3.png │ │ ├── front4.png │ │ ├── pushcase.png │ │ ├── solder1.png │ │ ├── solder2.png │ │ ├── solder3.png │ │ ├── stack1.png │ │ ├── stack2.png │ │ ├── stack3.png │ │ └── stack4.png │ └── stl │ │ ├── Bottom - Push Case - SeedSigner.stl │ │ ├── Button - Push Case - SeedSigner.stl │ │ ├── Middle - Push Case - SeedSigner.stl │ │ ├── Soldering Support - Push Case - SeedSigner.stl │ │ ├── Thumbstick - Push Case - SeedSigner.stl │ │ └── Top - Push Case - SeedSigner.stl ├── rugged_pill │ ├── Multimaterial │ │ ├── RuggedPIll_Bottom_MMU.3mf │ │ └── RuggedPill_Top_MMU.3mf │ ├── README.md │ ├── RuggedPillBottom_4SeedHammer.stl │ ├── RuggedPill_Bottom.stl │ ├── RuggedPill_Buttons.stl │ ├── RuggedPill_Thumbstick.stl │ ├── RuggedPill_Top.stl │ └── RuggedPill_Trackball.stl └── simple_pill │ ├── README.md │ ├── protective_case │ ├── sp_protective_case_lower.stl │ └── sp_protective_case_upper.stl │ ├── sp_button.stl │ ├── sp_dpad.stl │ ├── sp_lower.stl │ └── sp_upper.stl ├── l10n ├── README.md ├── messages.pot └── requirements-l10n.txt ├── pyproject.toml ├── requirements-raspi.txt ├── requirements.txt ├── seedsigner_pubkey.gpg ├── setup.cfg ├── setup.py ├── src ├── main.py └── seedsigner │ ├── __init__.py │ ├── controller.py │ ├── gui │ ├── __init__.py │ ├── components.py │ ├── keyboard.py │ ├── renderer.py │ ├── screens │ │ ├── __init__.py │ │ ├── psbt_screens.py │ │ ├── scan_screens.py │ │ ├── screen.py │ │ ├── seed_screens.py │ │ ├── settings_screens.py │ │ └── tools_screens.py │ └── toast.py │ ├── hardware │ ├── ST7789.py │ ├── __init__.py │ ├── buttons.py │ ├── camera.py │ ├── microsd.py │ └── pivideostream.py │ ├── helpers │ ├── __init__.py │ ├── embit_utils.py │ ├── l10n.py │ ├── mnemonic_generation.py │ ├── qr.py │ ├── seedkeeper_utils.py │ └── ur2 │ │ ├── LICENSE │ │ ├── __init__.py │ │ ├── bytewords.py │ │ ├── cbor_lite.py │ │ ├── constants.py │ │ ├── crc32.py │ │ ├── fountain_decoder.py │ │ ├── fountain_encoder.py │ │ ├── fountain_utils.py │ │ ├── random_sampler.py │ │ ├── ur.py │ │ ├── ur_decoder.py │ │ ├── ur_encoder.py │ │ ├── utils.py │ │ └── xoshiro256.py │ ├── models │ ├── __init__.py │ ├── decode_qr.py │ ├── encode_qr.py │ ├── encryptedqr.py │ ├── encryption.py │ ├── psbt_parser.py │ ├── qr_type.py │ ├── seed.py │ ├── seed_storage.py │ ├── settings.py │ ├── settings_definition.py │ ├── singleton.py │ └── threads.py │ ├── resources │ ├── fonts │ │ ├── Font_Awesome_6_Free-Solid-900.otf │ │ ├── Inconsolata-Regular.ttf │ │ ├── Inconsolata-SemiBold.ttf │ │ ├── OpenSans-Regular.ttf │ │ ├── OpenSans-SemiBold.ttf │ │ ├── PlemolJPConsole-Regular-S.ttf │ │ ├── PlemolJPConsole-SemiBold-S.ttf │ │ ├── RobotoCondensed-Bold.ttf │ │ ├── RobotoCondensed-Regular.ttf │ │ └── seedsigner-icons.otf │ ├── icons │ │ ├── arrow-down.png │ │ ├── arrow-down_selected.png │ │ ├── arrow-up.png │ │ ├── arrow-up_selected.png │ │ ├── back.png │ │ ├── back_selected.png │ │ ├── btc_logo.png │ │ ├── btc_logo_30x30.png │ │ ├── btc_logo_bw.png │ │ ├── dire_warning.png │ │ └── warning.png │ └── img │ │ ├── btc_logo_60x60.png │ │ ├── logo_black_240.png │ │ └── partners │ │ └── hrf_logo.png │ └── views │ ├── __init__.py │ ├── psbt_views.py │ ├── scan_views.py │ ├── screensaver.py │ ├── seed_views.py │ ├── settings_views.py │ ├── tools_views.py │ └── view.py ├── tests ├── README.md ├── base.py ├── requirements.txt ├── run_full_coverage.sh ├── screenshot_generator │ ├── README.md │ ├── __init__.py │ ├── conftest.py │ ├── generator.py │ ├── template.md │ └── utils.py ├── test_bip85.py ├── test_controller.py ├── test_decodepsbtqr.py ├── test_embit_utils.py ├── test_encodepsbtqr.py ├── test_flows.py ├── test_flows_l10n.py ├── test_flows_psbt.py ├── test_flows_seed.py ├── test_flows_settings.py ├── test_flows_tools.py ├── test_flows_view.py ├── test_l10n.py ├── test_main.py ├── test_mnemonic_generation.py ├── test_psbt_parser.py ├── test_seed.py ├── test_seedqr.py ├── test_settings.py └── test_settingsqr_decoder.py └── tools ├── javacard-build.xml.manual ├── javacard-build.xml.seedsigneros ├── mnemonic.py └── seed_phrase_to_qr.py /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | _Describe the change simply. Provide a reason for the change._ 4 | 5 | _Include screenshots of any new or modified screens (or at least explain why they were omitted)_ 6 | 7 | This pull request is categorized as a: 8 | 9 | - [ ] New feature 10 | - [ ] Bug fix 11 | - [ ] Code refactor 12 | - [ ] Documentation 13 | - [ ] Other 14 | 15 | ## Checklist 16 | 17 | - [ ] I’ve run `pytest` and made sure all unit tests pass before sumbitting the PR 18 | 19 | If you modified or added functionality/workflow, did you add new unit tests? 20 | 21 | - [ ] No, I’m a fool 22 | - [ ] Yes 23 | - [ ] N/A 24 | 25 | I have tested this PR on the following platforms/os: 26 | 27 | - [ ] Raspberry Pi OS [Manual Build](https://github.com/SeedSigner/seedsigner/blob/dev/docs/manual_installation.md) 28 | - [ ] [SeedSigner OS](https://github.com/SeedSigner/seedsigner-os) on a Pi0/Pi0W board 29 | - [ ] Other 30 | 31 | 32 | Note: Keep your changes limited in scope; if you uncover other issues or improvements along the way, ideally submit those as a separate PR. The more complicated the PR the harder to review, test, and merge. 33 | -------------------------------------------------------------------------------- /.github/workflows/telegram.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Notify on Telegram 2 | on: 3 | pull_request_target: 4 | branches: 5 | - dev 6 | types: 7 | - closed 8 | 9 | jobs: 10 | if_merged: 11 | if: github.event.pull_request.merged == true 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Notify on Telegram 15 | uses: EverythingSuckz/github-telegram-notify@main 16 | with: 17 | bot_token: '${{ secrets.BOT_TOKEN }}' 18 | chat_id: '${{ secrets.CHAT_ID }}' 19 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | - main 8 | pull_request: 9 | 10 | concurrency: 11 | # Concurrency group that uses the workflow name and PR number if available 12 | # or commit SHA as a fallback. If a new build is triggered under that 13 | # concurrency group while a previous build is running it will be canceled. 14 | # Repeated pushes to a PR will cancel all previous builds, while multiple 15 | # merges to main will not cancel. 16 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | test: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | matrix: 24 | # 3.10: currently used by Seedsigner 25 | # 3.12: latest stable Python as upper test bound 26 | python-version: ["3.10", "3.12"] 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | # Needs to also pull the seedsigner-translations repo 32 | submodules: recursive 33 | - name: Set up Python ${{ matrix.python-version }} 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: ${{ matrix.python-version }} 37 | - name: Install dependencies 38 | run: | 39 | sudo apt-get install libzbar0 40 | python -m pip install --upgrade pip 41 | pip install -r requirements.txt -r tests/requirements.txt 42 | pip install . 43 | - name: Test with pytest 44 | run: | 45 | mkdir artifacts 46 | python -m pytest \ 47 | --color=yes \ 48 | --cov=seedsigner \ 49 | --cov-append \ 50 | --cov-branch \ 51 | --durations 5 \ 52 | -vv 53 | - name: Generate screenshots 54 | run: | 55 | python -m pytest tests/screenshot_generator/generator.py \ 56 | --color=yes \ 57 | --cov=seedsigner \ 58 | --cov-append \ 59 | --cov-branch \ 60 | --cov-report html:./artifacts/cov_html \ 61 | -vv 62 | cp -r ./seedsigner-screenshots ./artifacts/ 63 | - name: Coverage report 64 | run: coverage report 65 | - name: Archive CI Artifacts 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: ci-artifacts-${{ matrix.python-version }} 69 | path: artifacts/** 70 | retention-days: 10 71 | # Upload also when tests fail. The workflow result (red/green) will 72 | # be not effected by this. 73 | if: always() 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __pycache__/ 3 | src/seedsigner.egg-info/ 4 | .nova 5 | .vscode 6 | src/seedsigner/models/settings_definition.json 7 | .idea 8 | .coverage* 9 | 10 | *.po 11 | *.mo -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/seedsigner/resources/seedsigner-translations"] 2 | path = src/seedsigner/resources/seedsigner-translations 3 | url = https://github.com/SeedSigner/seedsigner-translations.git 4 | branch = dev 5 | [submodule "seedsigner-screenshots"] 6 | path = seedsigner-screenshots 7 | url = https://github.com/SeedSigner/seedsigner-screenshots.git 8 | branch = dev 9 | update = none 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SeedSigner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | seedsigner-dev: 5 | build: 6 | context: . 7 | dockerfile: docker/Dockerfile 8 | command: sh -c 'bash -c "docker/setup.sh"' 9 | volumes: 10 | - ../seedsigner:/seedsigner -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-bullseye 2 | 3 | # install zbar dependencyy 4 | RUN apt-get -qq update 5 | RUN apt-get -y -qq install zbar-tools 6 | 7 | # temp copy requirements files to local repo to do pip3 install 8 | COPY ../requirements.txt /requirements.txt 9 | COPY ../tests/requirements.txt /tests-requirements.txt 10 | 11 | WORKDIR / 12 | RUN pip3 install -r requirements.txt 13 | RUN pip3 install -r tests-requirements.txt 14 | 15 | # clean up copied files 16 | RUN rm /requirements.txt 17 | RUN rm /tests-requirements.txt 18 | 19 | # set working dir 20 | WORKDIR /seedsigner -------------------------------------------------------------------------------- /docker/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pip3 install -e . 4 | tail -f /dev/null -------------------------------------------------------------------------------- /docs/code_structure.md: -------------------------------------------------------------------------------- 1 | # Code Structure 2 | 3 | SeedSigner roughly follows a Model-View-Controller approach. Like in a typical web app (e.g. Flask) the `View`s can be called as needed like individual web urls. After completing display and interaction with the user, the `View` then decides where to route the user next, analogous to a web app returning a `response.redirect(url)`. 4 | 5 | The `Controller` then ends up being quite stripped down. For example, there's no need for a web app's `urls.py` since there are no mappings from url to `View` to maintain since we're not actually using a url/http routing approach. 6 | 7 | `View`s have to handle user interaction so there are `while True` loops that cycle between waiting for user input, gathering data, and then updating the UI components accordingly. You wouldn't find this kind of cycle in a web app because this sort of interactive user input is handled in the browser at the html/css/js level. 8 | 9 | 10 | 11 | * `Model`s: Store the persistent settings, the in-memory seeds, current wallet information, etc. 12 | * `Controller`: Manages the state of the world and controls access to global resources. 13 | * `View`s: Implementation of each screen. Prepares relevant data for display. Must also instantiate the display objects that will actually render the UI. 14 | * `gui.screens`: Re-usable formatted UI renderers. 15 | * `gui.components`: Basic individual UI elements that are used by the `templates` such as the top nav, buttons, button lists, text displays. 16 | 17 | In an typical webserver context the `View` would send data to an html template (e.g. Jinja) which would then dynamically populate the page with html elements like ``, `