Merge remote-tracking branch 'agent/benma-agent/scroll-payment-request-memo-name'
What changed, and why it matters
This commit tweaks how the BitBox02 hardware wallet displays a payment-request memo on screen. It changes the label from 'Memo from\n\nMerchant' to 'Memo from: Merchant' and makes the screen scrollable so long merchant names don't get cut off. It also updates test snapshots and bumps the firmware version to 9.27.2. There is no indication this fixes a security vulnerability; it appears to be a user-interface improvement.
No security action required; treat as routine UI improvement. Reviewers may optionally verify that scrollable confirm screens do not alter the user's ability to read the full recipient name before signing.
Security signals we found
No security-relevant signal in commit message or diff
UI/UX change only: text formatting and scrollability
No memory-safety, cryptographic, or authorization changes observed
No vendor disclosure of security relevance present
Evidence from the diff
The change in payment_request.rs updates the ConfirmParams body format for payment-request recipient-name display and sets scrollable: true. The test vectors are version-locked so older firmware behavior is preserved for client integration tests. A new unit test verifies that a 200+ character recipient name renders as a scrollable confirm screen. versions.json is bumped from v9.27.1 to v9.27.2.
Changed components
src/rust/bitbox02-rust/src/hww/api/payment_request.rssrc/rust/bitbox02-rust/src/hal/testing/ui.rssrc/rust/bitbox-test-vectors/src/btc_transaction/cases/screens.rssrc/rust/bitbox-test-vectors/testdata/btc-transaction-test-vectors.jsonversions.jsonInspect captured patch +115 / −6
### src/rust/bitbox-test-vectors/README.md
@@ -12,6 +12,13 @@ express.
Version expectations and the previous-transaction requirement live on the vector.
+Client libraries use these vectors for integration tests against all supported and historical
+firmware versions. When firmware changes an observable outcome or screen transcript, preserve the
+old `VersionExpectation` by closing its range with `max_version_exclusive`, and add a new
+expectation whose `min_version` is the first firmware containing the change. Do not mutate an
+existing open-ended range in a way that changes the expected behavior of historical firmware.
+After changing the Rust constructors, regenerate the canonical JSON as described below.
+
Explicit derivation paths use canonical `m/...` strings rather than protocol-level integer arrays.
Confirm and transaction-fee screens include their `longtouch` requirement. Client simulators whose
stdout protocol omits that flag compare the remaining observable screen fields.
### src/rust/bitbox-test-vectors/src/btc_transaction/cases/screens.rs
@@ -798,7 +798,12 @@ pub fn payment_request_owned_output() -> Vec<VersionExpectation> {
success(
Some("9.24.0"),
Some("9.26.3"),
- payment_request_screens("0.30000000 TBTC", "0.10000000 TBTC", 50),
+ payment_request_screens(
+ "0.30000000 TBTC",
+ "0.10000000 TBTC",
+ 50,
+ "Memo from\n\nTest Merchant",
+ ),
),
invalid_input(Some("9.26.3"), None),
]
@@ -855,12 +860,13 @@ fn payment_request_screens(
total: &str,
transaction_fee: &str,
high_fee_percent: u32,
+ memo_from_body: &str,
) -> Vec<Screen> {
vec![
address("0.20000000 TBTC", "Test Merchant"),
Screen::Confirm {
title: "".into(),
- body: "Memo from\n\nTest Merchant".into(),
+ body: memo_from_body.into(),
longtouch: false,
},
Screen::Confirm {
@@ -884,8 +890,23 @@ pub fn payment_request() -> Vec<VersionExpectation> {
invalid_input_before("9.24.0"),
success(
Some("9.24.0"),
+ Some("9.27.2"),
+ payment_request_screens(
+ "0.50000000 TBTC",
+ "0.30000000 TBTC",
+ 150,
+ "Memo from\n\nTest Merchant",
+ ),
+ ),
+ success(
+ Some("9.27.2"),
None,
- payment_request_screens("0.50000000 TBTC", "0.30000000 TBTC", 150),
+ payment_request_screens(
+ "0.50000000 TBTC",
+ "0.30000000 TBTC",
+ 150,
+ "Memo from: Test Merchant",
+ ),
),
]
}
### src/rust/bitbox-test-vectors/testdata/btc-transaction-test-vectors.json
@@ -1335,6 +1335,7 @@
},
{
"min_version": "9.24.0",
+ "max_version_exclusive": "9.27.2",
"outcome": "success",
"screens": [
{
@@ -1378,6 +1379,52 @@
"body": "confirmed"
}
]
+ },
+ {
+ "min_version": "9.27.2",
+ "outcome": "success",
+ "screens": [
+ {
+ "type": "transaction_address",
+ "amount": "0.20000000 TBTC",
+ "address": "Test Merchant"
+ },
+ {
+ "type": "confirm",
+ "title": "",
+ "body": "Memo from: Test Merchant",
+ "longtouch": false
+ },
+ {
+ "type": "confirm",
+ "title": "Memo 1/2",
+ "body": "TextMemo line1",
+ "longtouch": false
+ },
+ {
+ "type": "confirm",
+ "title": "Memo 2/2",
+ "body": "TextMemo line2",
+ "longtouch": false
+ },
+ {
+ "type": "transaction_fee",
+ "amount": "0.50000000 TBTC",
+ "fee": "0.30000000 TBTC",
+ "longtouch": false
+ },
+ {
+ "type": "confirm",
+ "title": "High fee",
+ "body": "The fee is 150.0%\nthe send amount.\nProceed?",
+ "longtouch": true
+ },
+ {
+ "type": "status",
+ "title": "Transaction",
+ "body": "confirmed"
+ }
+ ]
}
],
"expected_signatures": [
### src/rust/bitbox02-rust/src/hal/testing/ui.rs
@@ -72,6 +72,7 @@ pub struct TestingUi<'a> {
_abort_nth: Option<usize>,
pub screens: Vec<Screen>,
pub confirm_display_sizes: Vec<usize>,
+ pub confirm_scrollable: Vec<bool>,
progress_screens: Rc<RefCell<Vec<ProgressScreen>>>,
_enter_string: Option<EnterStringCb<'a>>,
_menu: Option<MenuCb<'a>>,
@@ -133,6 +134,7 @@ impl Ui for TestingUi<'_> {
async fn confirm(&mut self, params: &ConfirmParams<'_>) -> Result<(), UserAbort> {
self.confirm_display_sizes.push(params.display_size);
+ self.confirm_scrollable.push(params.scrollable);
self.screens.push(Screen::Confirm {
title: params.title.into(),
body: params.body.into(),
@@ -301,6 +303,7 @@ impl<'a> TestingUi<'a> {
Self {
screens: vec![],
confirm_display_sizes: vec![],
+ confirm_scrollable: vec![],
progress_screens: Rc::new(RefCell::new(vec![])),
_abort_nth: None,
_enter_string: None,
### src/rust/bitbox02-rust/src/hww/api/payment_request.rs
@@ -152,7 +152,8 @@ pub async fn user_verify(
hal.ui()
.confirm(&ConfirmParams {
title: "",
- body: &format!("Memo from\n\n{}", payment_request.recipient_name),
+ body: &format!("Memo from: {}", payment_request.recipient_name),
+ scrollable: true,
accept_is_nextarrow: true,
..Default::default()
})
@@ -1552,7 +1553,7 @@ mod tests {
},
Screen::Confirm {
title: "".into(),
- body: "Memo from\n\nPOCKET".into(),
+ body: "Memo from: POCKET".into(),
longtouch: false,
},
Screen::Confirm {
@@ -1562,6 +1563,36 @@ mod tests {
},
]
);
+ assert_eq!(mock_hal.ui.confirm_scrollable, vec![true, true]);
+ }
+
+ #[async_test::test]
+ async fn test_user_verify_long_recipient_name_scrollable() {
+ let recipient_name = format!("SWAPKIT ({})", "a".repeat(200));
+ let mut mock_hal = TestingHal::new();
+ user_verify(
+ &mut mock_hal,
+ &pb::BtcPaymentRequestRequest {
+ recipient_name: recipient_name.clone(),
+ memos: vec![make_text_memo("Swap memo")],
+ nonce: vec![],
+ total_amount: 1234567890,
+ signature: vec![],
+ },
+ "12.34567890 BTC",
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(
+ mock_hal.ui.screens[1],
+ Screen::Confirm {
+ title: "".into(),
+ body: format!("Memo from: {recipient_name}"),
+ longtouch: false,
+ }
+ );
+ assert!(mock_hal.ui.confirm_scrollable[0]);
}
#[cfg(feature = "app-ethereum")]
### versions.json
@@ -1,5 +1,5 @@
{
- "firmware": "v9.27.1",
+ "firmware": "v9.27.2",
"bootloader": "v1.2.2",
"stage0": 1
}Why this scored 19/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.