diff --git a/app/src/ui_stream.h b/app/src/ui_stream.h index b15886bbe..ee66fd5b5 100644 --- a/app/src/ui_stream.h +++ b/app/src/ui_stream.h @@ -36,6 +36,10 @@ `tz_ui_stream_push`, calling `tz_ui_stream_close`, and launching with a `refill` callback set to NULL. */ +#ifdef HAVE_NBGL +#include +#endif + #include #define TZ_UI_STREAM_HISTORY_SCREENS 8 @@ -90,6 +94,15 @@ typedef struct { char body[TZ_UI_STREAM_CONTENTS_LINES][TZ_UI_STREAM_CONTENTS_WIDTH + 1]; } tz_ui_stream_screen_t; +#ifdef HAVE_NBGL +typedef struct { + char title[TZ_UI_STREAM_TITLE_WIDTH + 1]; + char body[TZ_UI_STREAM_CONTENTS_WIDTH + 1]; + nbgl_layoutTagValue_t pair; + nbgl_layoutTagValueList_t list; +} tz_ui_stream_display_t; +#endif // HAVE_NBGL + typedef struct { void (*cb)(tz_ui_cb_type_t); tz_ui_stream_screen_t screens[TZ_UI_STREAM_HISTORY_SCREENS]; @@ -99,6 +112,9 @@ typedef struct { // FIXME: workaround for issue with non-local control flow. Remove once // fixed see !66 bool pressed_right; +#ifdef HAVE_NBGL + tz_ui_stream_display_t current_screen; +#endif // HAVE_NBGL } tz_ui_stream_t; void tz_ui_stream_init(void (*)(tz_ui_cb_type_t)); diff --git a/app/src/ui_stream_nbgl.c b/app/src/ui_stream_nbgl.c index 03ecd1f87..219635b4a 100644 --- a/app/src/ui_stream_nbgl.c +++ b/app/src/ui_stream_nbgl.c @@ -22,6 +22,8 @@ #include "globals.h" +bool tz_ui_nav_cb(uint8_t, nbgl_pageContent_t *); + void tz_reject(void) { @@ -45,11 +47,41 @@ tz_reject_ui(void) nbgl_useCaseStatus("Rejected", false, tz_reject); } +void +tz_choice_ui(bool accept) +{ + FUNC_ENTER(("accept=%d", accept)); + + if (accept) { + // FIXME: implement accept workflow + } else { + tz_reject_ui(); + } + + FUNC_LEAVE(); +} + +void +tz_ui_continue(void) +{ + FUNC_ENTER(("void")); + + tz_ui_stream_t *s = &global.stream; + + if (!s->full) + s->cb(TZ_UI_STREAM_CB_REFILL); + + FUNC_LEAVE(); + return; +} + void tz_ui_start(void) { FUNC_ENTER(("void")); + nbgl_useCaseForwardOnlyReview("Reject", NULL, tz_ui_nav_cb, tz_choice_ui); + FUNC_LEAVE(); return; } @@ -73,4 +105,80 @@ tz_ui_stream_init(void (*cb)(uint8_t)) FUNC_LEAVE(); } +static nbgl_layoutTagValue_t * +tz_ui_current_screen(__attribute__((unused)) uint8_t pairIndex) +{ + FUNC_ENTER(("pairIndex=%d", pairIndex)); + + tz_ui_stream_t *s = &global.stream; + tz_ui_stream_display_t *c = &s->current_screen; + + PRINTF("[DEBUG] pressed_right=%d\n", s->pressed_right); + + if (s->current < s->total && s->pressed_right) { + s->current++; + s->pressed_right = false; + } + + c->title[0] = 0; + c->body[0] = 0; + + size_t bucket = s->current % TZ_UI_STREAM_HISTORY_SCREENS; + STRLCPY(c->title, s->screens[bucket].title); + STRLCPY(c->body, s->screens[bucket].body[0]); + + c->pair.item = c->title; + c->pair.value = c->body; + + PRINTF("show title=%s, body=%s from bucket=%d\n", c->title, c->body, + bucket); + FUNC_LEAVE(); + return &c->pair; +} + +bool +tz_ui_nav_cb(uint8_t page, nbgl_pageContent_t *content) +{ + FUNC_ENTER(("page=%d, content=%p", page, content)); + + tz_ui_stream_t *s = &global.stream; + tz_ui_stream_display_t *c = &s->current_screen; + + if (s->total < 0) { + return false; + } + + if (page > 0 && !s->pressed_right) { + s->pressed_right = true; + } + + PRINTF("pressed_right=%d, current=%d, total=%d, full=%d\n", + s->pressed_right, s->current, s->total, s->full); + + if ((s->current == s->total) && !s->full) { + tz_ui_continue(); + } + + if (!s->full) { + c->list.pairs = NULL; + c->list.callback = tz_ui_current_screen; + c->list.startIndex = 0; + c->list.nbPairs = 1; + c->list.smallCaseForValue = false; + c->list.wrapping = false; + + content->type = TAG_VALUE_LIST; + content->tagValueList = c->list; + } else { + content->type = INFO_LONG_PRESS; + content->infoLongPress.icon = &C_tezos; + content->infoLongPress.text = "Sign"; + content->infoLongPress.longPressText = "Sign"; + } + + FUNC_LEAVE(); + + return true; +} + #endif diff --git a/tests/integration/stax/snapshots/operation_0_transaction.png b/tests/integration/stax/snapshots/operation_0_transaction.png new file mode 100644 index 000000000..8a9dd89d1 Binary files /dev/null and b/tests/integration/stax/snapshots/operation_0_transaction.png differ diff --git a/tests/integration/stax/test_reject_transfer.py b/tests/integration/stax/test_reject_transfer.py new file mode 100755 index 000000000..c7deb0e3d --- /dev/null +++ b/tests/integration/stax/test_reject_transfer.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# Copyright 2023 Trilitech + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from utils import * + +# full input: 0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316 +# full output: CAR +# signer: tz1dyX3B1CFYa2DfdFLyPtiJCfQRUgPVME6E +# path: m/44'/1729'/0'/0' + +if __name__ == "__main__": + app = stax_app() + + app.assert_screen(SCREEN_HOME_DEFAULT) + + app.send_apdu("800f000011048000002c800006c18000000080000000") + app.expect_apdu_return("9000") + + app.assert_screen("review_request_sign_operation") + app.review.tap() + + # Ensure we don't advance to a blank screen + app.assert_screen("review_request_sign_operation") + + app.send_apdu("800f81005e0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316"); + + app.review.tap() + app.assert_screen("operation_0_transaction") + + app.review_reject_signing() + app.expect_apdu_failure("6985") + + app.assert_screen(SCREEN_HOME_DEFAULT) + app.welcome.quit()