feat(core): add `miniscript` feature flag
What changed, and why it matters
This commit adds a new build-time feature flag called 'miniscript' to the Trezor firmware. It does not implement any actual Miniscript functionality yet; it only creates the scaffolding (a new MicroPython module placeholder, build options, and CI test configurations) so future Miniscript work can be developed without affecting production firmware builds. There is no security-relevant code change here.
No security action required. Treat as normal feature-flag infrastructure. Monitor future commits that populate the trezorminiscript module and any Miniscript parsing/signing code for security review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit introduces a Cargo/xtask feature flag ‘miniscript’ and a corresponding USE_MINISCRIPT C preprocessor define. When enabled, it registers an empty MicroPython module named ‘trezorminiscript’ and exposes a USE_MINISCRIPT boolean in trezorutils. The flag is wired into CI for emulator and firmware builds on model T3T1 with btconly/debuglink configurations only, explicitly excluded from production/universal builds. No parsing, signing, or cryptographic logic is added.
Changed components
core/embed/rust/src/micropython/miniscript.rscore/embed/upymod/modtrezorutils/modtrezorutils.ccore/embed/upymod/rustmods.ccore/embed/xtask/src/options.rs.github/workflows/core.ymlInspect captured patch +79 / −9
### .github/workflows/core.yml
@@ -48,7 +48,7 @@ jobs:
cat $GITHUB_OUTPUT
core_firmware:
- name: Build firmware (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.type }}${{matrix.n1w1 && ', n1w1' || '' }})
+ name: Build firmware (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.type }}${{ matrix.miniscript && ', miniscript' || '' }}${{matrix.n1w1 && ', n1w1' || '' }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
@@ -57,6 +57,7 @@ jobs:
model: ${{ fromJSON(github.event_name == 'push' && '["T2B1", "T2T1", "T3B1", "T3T1", "T3W1"]' || '["T2T1", "T3B1", "T3T1", "T3W1"]') }}
coins: [universal, btconly]
type: ${{ fromJSON(github.event_name == 'schedule' && '["normal", "debuglink", "production"]' || '["normal", "debuglink"]') }}
+ miniscript: [false]
n1w1: [false]
include:
- model: D001
@@ -68,6 +69,10 @@ jobs:
- model: T2B1
coins: btconly
type: normal
+ - model: T3T1
+ coins: btconly
+ type: debuglink
+ miniscript: true # currently Miniscript is not supported for universal builds
- model: T3W1
coins: universal
type: debuglink
@@ -79,6 +84,7 @@ jobs:
TREZOR_MODEL: ${{ matrix.model }}
BITCOIN_ONLY: ${{ matrix.coins == 'universal' && '0' || '1' }}
PYOPT: ${{ matrix.type == 'debuglink' && '0' || '1' }}
+ MINISCRIPT: ${{ matrix.miniscript && '1' || '0' }}
N1W1: ${{ matrix.n1w1 && '1' || '0' }}
PRODUCTION: ${{ matrix.type == 'production' && '1' || '0' }}
BOOTLOADER_DEVEL: ${{ matrix.model == 'T3W1' && '1' || '0' }}
@@ -106,15 +112,15 @@ jobs:
if: matrix.coins == 'btconly' && matrix.type != 'debuglink'
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # actions/upload-artifact@v7.0.0
with:
- name: core-firmware-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}${{ matrix.n1w1 && '-n1w1' || '' }}
+ name: core-firmware-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}${{ matrix.miniscript && '-miniscript' || '' }}${{ matrix.n1w1 && '-n1w1' || '' }}
path: |
core/build-xtask/artifacts/latest/*.bin
core/build-xtask/artifacts/latest/*.elf
core/build-xtask/artifacts/pub/*
retention-days: 7
core_emu:
- name: Build emu (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.type }}, ${{ matrix.asan }}${{matrix.n1w1 && ', n1w1' || '' }})
+ name: Build emu (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.type }}, ${{ matrix.asan }}${{ matrix.miniscript && ', miniscript' || '' }}${{matrix.n1w1 && ', n1w1' || '' }})
runs-on: ubuntu-latest
timeout-minutes: 15
needs: param
@@ -126,11 +132,17 @@ jobs:
# type: [normal, debuglink]
type: ${{ fromJSON(github.event_name == 'schedule' && '["normal", "debuglink"]' || '["debuglink"]') }}
asan: ${{ fromJSON(needs.param.outputs.asan) }}
+ miniscript: [false]
n1w1: [false]
exclude:
- type: normal
asan: asan
include:
+ - model: T3T1
+ coins: btconly
+ type: debuglink
+ asan: noasan
+ miniscript: true # currently Miniscript is not supported for universal builds
- model: T3W1
coins: universal
type: debuglink
@@ -140,6 +152,7 @@ jobs:
TREZOR_MODEL: ${{ matrix.model }}
BITCOIN_ONLY: ${{ matrix.coins == 'universal' && '0' || '1' }}
PYOPT: ${{ matrix.type == 'debuglink' && '0' || '1' }}
+ MINISCRIPT: ${{ matrix.miniscript && '1' || '0' }}
N1W1: ${{ matrix.n1w1 && '1' || '0' }}
ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
LSAN_OPTIONS: "suppressions=../../asan_suppressions.txt"
@@ -153,10 +166,10 @@ jobs:
- run: nix-shell --run "uv run make -C core build_unix_frozen"
- run: nix-shell --run "uv run ./tools/check-insecure-prng.py --present --model $TREZOR_MODEL core/build-xtask/artifacts/latest/firmware-emu"
- run: nix-shell --run "uv run make -C core test_emu_sanity"
- - run: cp core/build-xtask/artifacts/latest/firmware-emu core/build-xtask/artifacts/latest/firmware-emu-${{ matrix.model }}-${{ matrix.coins }}${{ matrix.n1w1 && '-n1w1' || '' }}
+ - run: cp core/build-xtask/artifacts/latest/firmware-emu core/build-xtask/artifacts/latest/firmware-emu-${{ matrix.model }}-${{ matrix.coins }}${{ matrix.miniscript && '-miniscript' || '' }}${{ matrix.n1w1 && '-n1w1' || '' }}
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # actions/upload-artifact@v7.0.0
with:
- name: core-emu-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}-${{ matrix.asan }}${{ matrix.n1w1 && '-n1w1' || '' }}
+ name: core-emu-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}-${{ matrix.asan }}${{ matrix.miniscript && '-miniscript' || '' }}${{ matrix.n1w1 && '-n1w1' || '' }}
path: |
core/build-xtask/artifacts/latest/firmware-emu*
core/build-xtask/artifacts/latest/bootloader-emu
@@ -320,7 +333,7 @@ jobs:
# See artifacts for a comprehensive report of UI.
# See [docs/tests/ui-tests](../tests/ui-tests.md) for more info.
core_device_test:
- name: Device tests (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.asan }}, ${{ matrix.lang }}${{ matrix.n1w1 && ', n1w1' || '' }})
+ name: Device tests (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.asan }}, ${{ matrix.lang }}${{ matrix.miniscript && ', miniscript' || '' }}${{ matrix.n1w1 && ', n1w1' || '' }})
runs-on: ubuntu-latest
needs:
- param
@@ -332,8 +345,14 @@ jobs:
coins: [universal, btconly]
asan: ${{ fromJSON(needs.param.outputs.asan) }}
lang: ${{ fromJSON(needs.param.outputs.test_lang) }}
+ miniscript: [false]
n1w1: [false]
include:
+ - model: T3T1
+ coins: btconly
+ asan: noasan
+ lang: en
+ miniscript: true
- model: T3W1
coins: universal
asan: noasan
@@ -345,7 +364,7 @@ jobs:
ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
PYTEST_TIMEOUT: ${{ matrix.asan == 'asan' && 600 || 400 }}
ACTIONS_DO_UI_TEST: ${{ matrix.coins == 'universal' && matrix.asan == 'noasan' }}
- STABLE_FEATURES: ${{ !matrix.n1w1 }} # TODO(N1W1): stabilize n1w1
+ STABLE_FEATURES: ${{ !(matrix.miniscript || matrix.n1w1) }} # TODO: remove after stabilization
TEST_LANG: ${{ matrix.lang }}
TESTOPTS: "--durations 10 --session-timeout ${{ matrix.model == 'T3W1' && '3000' || '1800' }}" # pytest global timeout
timeout-minutes: ${{ matrix.model == 'T3W1' && 60 || 40 }} # CI job timeout
@@ -355,7 +374,7 @@ jobs:
submodules: recursive
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # actions/download-artifact@v8.0.0
with:
- name: core-emu-${{ matrix.model }}-${{ matrix.coins }}-debuglink-${{ matrix.asan }}${{ matrix.n1w1 && '-n1w1' || '' }}
+ name: core-emu-${{ matrix.model }}-${{ matrix.coins }}-debuglink-${{ matrix.asan }}${{ matrix.miniscript && '-miniscript' || '' }}${{ matrix.n1w1 && '-n1w1' || '' }}
path: core/build-xtask/artifacts/latest
- run: chmod +x core/build-xtask/artifacts/latest/firmware-emu*
- uses: ./.github/actions/environment
@@ -369,7 +388,7 @@ jobs:
if: failure()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # actions/upload-artifact@v7.0.0
with:
- name: core-test-device-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.lang }}-${{ matrix.asan }}${{ matrix.n1w1 && '-n1w1' || '' }}
+ name: core-test-device-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.lang }}-${{ matrix.asan }}${{ matrix.miniscript && '-miniscript' || '' }}${{ matrix.n1w1 && '-n1w1' || '' }}
path: tests/trezor*.log
retention-days: 7
if: always()
### core/Makefile
@@ -67,6 +67,9 @@ endif
ifeq ($(BOOTLOADER_DEVEL),1)
XTASK_BUILD_OPTS += --bootloader-devel
endif
+ifeq ($(MINISCRIPT),1)
+XTASK_BUILD_OPTS += --miniscript
+endif
ifeq ($(N1W1),1)
XTASK_BUILD_OPTS += --n1w1
endif
### core/embed/projects/firmware/Cargo.toml
@@ -38,6 +38,7 @@ force_bootloader_upgrade = []
frozen = ["upymod/frozen"]
log_stack_usage = ["upymod/log_stack_usage"]
micropy_enable_source_lines = ["upymod/enable_source_lines"]
+miniscript = ["trezor_lib/miniscript", "upymod/miniscript"]
n1w1 = ["upymod/n1w1"]
optiga_testing = ["sec/optiga_testing"]
production = ["io/production"]
### core/embed/projects/firmware/project.toml
@@ -69,6 +69,7 @@ force-bootloader-upgrade = { true = ["force_bootloader_upgrade"] }
frozen = { true = ["frozen"] }
log-stack-usage = { true = ["log_stack_usage"] }
mem-perf = { true = ["memperf"] }
+miniscript = { true = ["miniscript"] }
n1w1 = { true = ["n1w1"] } # TODO: move back to `uses` section (after stabilization)
perf-overlay = { true = ["ui_performance_overlay"] }
production = { true = ["production"], false = ["dev_keys"] }
### core/embed/projects/unix/Cargo.toml
@@ -36,6 +36,7 @@ frozen = ["upymod/frozen"]
log_stack_usage = ["upymod/log_stack_usage"]
memperf = ["upymod/memperf"]
micropy_enable_source_lines = ["upymod/enable_source_lines"]
+miniscript = ["trezor_lib/miniscript", "upymod/miniscript"]
n1w1 = ["upymod/n1w1"]
nfc = ["upymod/nfc"]
optiga_testing = ["sec/optiga_testing"]
### core/embed/rust/Cargo.toml
@@ -76,6 +76,7 @@ layout_caesar = []
layout_delizia = []
layout_eckhart = []
micropython = ["dep:upymod"]
+miniscript = []
n1w1 = []
nrf = []
optiga = []
### core/embed/rust/librust_qstr.h
@@ -937,6 +937,7 @@ static void _librust_qstrs(void) {
MP_QSTR_trace;
MP_QSTR_trezorble;
MP_QSTR_trezorlog;
+ MP_QSTR_trezorminiscript;
MP_QSTR_trezorproto;
MP_QSTR_trezorthp;
MP_QSTR_trezorui_api;
### core/embed/rust/src/micropython/miniscript.rs
@@ -0,0 +1,7 @@
+use crate::micropython::module::Module;
+use crate::micropython::qstr::Qstr;
+
+#[no_mangle]
+pub static mp_module_trezorminiscript: Module = obj_module! {
+ Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorminiscript.to_obj()
+};
### core/embed/rust/src/micropython/mod.rs
@@ -25,6 +25,9 @@ pub mod util;
pub use error::Error;
pub use obj::Obj;
+#[cfg(feature = "miniscript")]
+mod miniscript;
+
#[cfg(feature = "dbg_console")]
pub mod logging;
### core/embed/upymod/Cargo.toml
@@ -51,6 +51,7 @@ layout_eckhart = []
log_stack_usage = []
mcu_attestation = ["sec/mcu_attestation"]
memperf = []
+miniscript = []
n1w1 = ["nfc"]
nfc = ["io/nfc"]
nem = []
### core/embed/upymod/build.rs
@@ -68,6 +68,10 @@ fn main() -> Result<()> {
lib.add_define("MICROPY_TREZOR_MEMPERF", Some("1"));
}
+ if cfg!(feature = "miniscript") {
+ lib.add_define("USE_MINISCRIPT", Some("1"));
+ }
+
if cfg!(feature = "n1w1") {
lib.add_define("USE_N1W1", Some("1"));
}
@@ -944,6 +948,7 @@ impl<'a> MpyBuilder<'a> {
let emulator = py_bool(cfg!(feature = "emulator"));
let haptic = py_bool(cfg!(feature = "haptic"));
let mcu_attestation = py_bool(cfg!(feature = "mcu_attestation"));
+ let miniscript = py_bool(cfg!(feature = "miniscript"));
let n1w1 = py_bool(cfg!(feature = "n1w1"));
let optiga = py_bool(cfg!(feature = "optiga"));
let power_manager = py_bool(cfg!(feature = "power_manager"));
@@ -969,6 +974,7 @@ impl<'a> MpyBuilder<'a> {
format!(r"s/utils\.USE_BUTTON/{button}/g"),
format!(r"s/utils\.USE_HAPTIC/{haptic}/g"),
format!(r"s/utils\.USE_N1W1/{n1w1}/g"),
+ format!(r"s/utils\.USE_MINISCRIPT/{miniscript}/g"),
format!(r"s/utils\.USE_MCU_ATTESTATION/{mcu_attestation}/g"),
format!(r"s/utils\.USE_OPTIGA/{optiga}/g"),
format!(r"s/utils\.USE_POWER_MANAGER/{power_manager}/g"),
### core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -876,6 +876,8 @@ static const mp_obj_tuple_t mod_trezorutils_version_obj = {
/// """Whether the firmware supports loading 3rd-party applications."""
/// USE_TELEMETRY: bool
/// """Whether a telemetry is supported."""
+/// USE_MINISCRIPT: bool
+/// """Whether Miniscript is supported."""
/// USE_N1W1: bool
/// """Whether N1W1 is supported."""
/// MODEL: str
@@ -1000,6 +1002,11 @@ static const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
#else
{MP_ROM_QSTR(MP_QSTR_USE_SERIAL_NUMBER), mp_const_false},
#endif
+#if USE_MINISCRIPT
+ {MP_ROM_QSTR(MP_QSTR_USE_MINISCRIPT), mp_const_true},
+#else
+ {MP_ROM_QSTR(MP_QSTR_USE_MINISCRIPT), mp_const_false},
+#endif
#if USE_N1W1
{MP_ROM_QSTR(MP_QSTR_USE_N1W1), mp_const_true},
#else
### core/embed/upymod/rustmods.c
@@ -51,3 +51,7 @@ MP_REGISTER_MODULE(MP_QSTR_coveragedata, mp_module_coveragedata);
#if defined(USE_DBG_CONSOLE)
MP_REGISTER_MODULE(MP_QSTR_trezorlog, mp_module_trezorlog);
#endif
+
+#ifdef USE_MINISCRIPT
+MP_REGISTER_MODULE(MP_QSTR_trezorminiscript, mp_module_trezorminiscript);
+#endif
### core/embed/xtask/src/options.rs
@@ -202,6 +202,10 @@ build_options! {
#[arg(long, num_args = 0..=1, default_missing_value = "true")]
map n1w1: bool,
+ /// Experimental miniscript support.
+ #[arg(long, num_args = 0..=1, default_missing_value = "true")]
+ map miniscript: bool,
+
/// Disable UI animations
#[arg(long, num_args = 0..=1, default_missing_value = "true")]
map disable_animation: bool,
### core/embed/xtask/tbench/manifest.yaml
@@ -161,6 +161,13 @@ options:
when: component(firmware)
type: checkbox
+ - id: miniscript
+ name: Miniscript Support
+ description: Enables experimental Miniscript support.
+ group: Features
+ when: component(firmware)
+ type: checkbox
+
- id: debug
name: Debug Build
description: Enables debug symbols and disables some optimizations.
### core/mocks/generated/trezorutils.pyi
@@ -288,6 +288,8 @@ USE_APP_LOADING: bool
"""Whether the firmware supports loading 3rd-party applications."""
USE_TELEMETRY: bool
"""Whether a telemetry is supported."""
+USE_MINISCRIPT: bool
+"""Whether Miniscript is supported."""
USE_N1W1: bool
"""Whether N1W1 is supported."""
MODEL: str
### core/src/trezor/utils.py
@@ -28,6 +28,7 @@
USE_DBG_CONSOLE,
USE_HAPTIC,
USE_MCU_ATTESTATION,
+ USE_MINISCRIPT,
USE_N1W1,
USE_NFC,
USE_NRF,
### docs/core/build/xtask.md
@@ -93,6 +93,7 @@ cannot be used bare:
- `--xbuild-trace` — log build script progress (executed commands and timings).
- `--apps` — enable external app loading.
- `--n1w1` — enable N1W1 support.
+- `--miniscript` — enable experimental Miniscript support.
- `--unsafe-fw` — enable unsafe firmware features.
- `--storage-insecure-testing-mode` — insecure storage test mode (forbidden with
`--production`).Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.