Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .github/workflows/build_and_functional_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ name: Build and run functional tests using ragger through reusable workflow
# resulting binaries.
# It then calls another reusable workflow to run the Ragger tests on the compiled application binary.
#
# While this workflow is optional, having functional testing on your application is mandatory and this workflow and
# The build part of this workflow is mandatory, this ensures that the app will be deployable in the Ledger App Store.
# While the test part of this workflow is optional, having functional testing on your application is mandatory and this workflow and
# tooling environment is meant to be easy to use and adapt after forking your application

on:
workflow_dispatch:
inputs:
golden_run:
type: choice
required: true
default: 'Raise an error (default)'
description: CI behavior if the test snapshots are different than expected.
options:
- 'Raise an error (default)'
- 'Open a PR'
push:
branches:
- master
Expand All @@ -30,5 +40,4 @@ jobs:
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_ragger_tests.yml@v1
with:
download_app_binaries_artifact: "compiled_app_binaries"
test_dir: tests
run_for_devices: '["nanos", "nanox", "nanosp", "stax"]'
regenerate_snapshots: ${{ inputs.golden_run == 'Open a PR' }}
19 changes: 8 additions & 11 deletions .github/workflows/codeql_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,21 @@ jobs:
analyse:
name: Analyse
strategy:
fail-fast: false
matrix:
sdk: [ "$NANOS_SDK", "$NANOX_SDK", "$NANOSP_SDK" ]
#'cpp' covers C and C++
language: [ 'cpp' ]
sdk: ["$NANOX_SDK", "$NANOSP_SDK", "$STAX_SDK", "$FLEX_SDK"]
# 'cpp' covers C and C++
language: ['cpp']
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest

steps:
- name: Clone
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality
Expand All @@ -45,4 +42,4 @@ jobs:
make BOLOS_SDK=${{ matrix.sdk }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
1 change: 0 additions & 1 deletion .github/workflows/coding_style_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ jobs:
with:
source: './src'
extensions: 'h,c'
version: 11
4 changes: 2 additions & 2 deletions .github/workflows/documentation_generation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ jobs:

steps:
- name: Clone
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: HTML documentation
run: doxygen .doxygen/Doxyfile

- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: documentation
path: doc/html
37 changes: 24 additions & 13 deletions .github/workflows/misspellings_checks.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Misspellings checks
name: Checks on the Python client

# This workflow performs some misspelling checks on the repository
# This workflow performs some checks on the Python client used by the ragger tests
# It is there to help us maintain a level of quality in our codebase and does not have to be kept on forked
# applications.

Expand All @@ -14,17 +14,28 @@ on:
pull_request:

jobs:
misspell:
name: Check misspellings
lint:
name: Client linting
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v3

- name: Check misspellings
uses: codespell-project/actions-codespell@v1
with:
builtin: clear,rare
check_filenames: true
skip: ./.git,./unit-tests/mock_includes
- name: Clone
uses: actions/checkout@v4
- name: Installing PIP dependencies
run: |
pip install pylint
pip install -r tests/requirements.txt
- name: Lint Python code
run: pylint --rc tests/setup.cfg tests/application_client/

mypy:
name: Type checking
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v4
- name: Installing PIP dependencies
run: |
pip install mypy
pip install -r tests/requirements.txt
- name: Mypy type checking
run: mypy tests/application_client/
18 changes: 18 additions & 0 deletions .github/workflows/swap_ci_workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Swap functional tests

on:
workflow_dispatch:
push:
branches:
- master
- main
- develop
pull_request:

jobs:
job_functional_tests:
uses: LedgerHQ/app-exchange/.github/workflows/reusable_swap_functional_tests.yml@develop
with:
branch_for_kas: ${{ github.ref }}
repo_for_kas: ${{ github.repository }}
test_filter: '"KAS or Kaspa or kaspa"'
21 changes: 16 additions & 5 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ jobs:

steps:
- name: Clone
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Clone SDK
uses: actions/checkout@v4
with:
repository: ledgerHQ/ledger-secure-sdk
path: sdk

- name: Build unit tests
run: |
cd unit-tests/
export BOLOS_SDK=../sdk
cmake -Bbuild -H. && make -C build && make -C build test

- name: Generate code coverage
Expand All @@ -31,18 +38,22 @@ jobs:
lcov --directory . -b "$(realpath build/)" --capture --initial -o coverage.base && \
lcov --rc lcov_branch_coverage=1 --directory . -b "$(realpath build/)" --capture -o coverage.capture && \
lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info && \
lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' --remove coverage.info 'lib_standard_app' --remove coverage.info '*/src/import/*' -o coverage.info && \
lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' -o coverage.info && \
genhtml coverage.info -o coverage

- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: code-coverage
path: unit-tests/coverage

- name: Install codecov dependencies
run: apt install --no-install-recommends -y curl gpg

- name: Upload to codecov.io
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./unit-tests/coverage.info
flags: unittests
name: codecov-app-boilerplate
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ APPNAME = "Kaspa"

# Application version
APPVERSION_M = 1
APPVERSION_N = 0
APPVERSION_P = 3
APPVERSION_N = 1
APPVERSION_P = 0
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

ifeq ($(TARGET_NAME),TARGET_NANOS)
Expand Down Expand Up @@ -95,6 +95,7 @@ VARIANT_VALUES = KAS
# Application communication interfaces #
########################################
ENABLE_BLUETOOTH = 1
ENABLE_SWAP = 1
#ENABLE_NFC = 1

########################################
Expand Down
2 changes: 1 addition & 1 deletion ledger_app.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[app]
build_directory = "./"
sdk = "C"
devices = ["flex", "nanos", "nanox", "nanos+", "stax"]
devices = ["flex", "nanox", "nanos+", "stax"]

[tests]
unit_directory = "./unit-tests/"
Expand Down
8 changes: 7 additions & 1 deletion src/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,13 @@ bool address_from_pubkey(const uint8_t public_key[static 64],
memmove(address, hrp, sizeof(hrp));
address[5] = ':';

cashaddr_encode(compressed_public_key, compressed_pub_size, address + 6, address_len, version);
if (cashaddr_encode(compressed_public_key,
compressed_pub_size,
address + 6,
address_len,
version) == 0) {
return false;
}

memmove(out, address, address_len);

Expand Down
18 changes: 13 additions & 5 deletions src/app_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*****************************************************************************/

#include <stdint.h> // uint*_t
#include <string.h> // memset, explicit_bzero

#include "os.h"
#include "ux.h"
#include "swap.h"

#include "types.h"
#include "globals.h"
#include "io.h"
#include "sw.h"
#include "ui/menu.h"
#include "parser.h"
#include "apdu/dispatcher.h"
#include "menu.h"
#include "dispatcher.h"

global_ctx_t G_context;

Expand All @@ -48,7 +49,14 @@ void app_main() {

io_init();

ui_menu_main();
#ifdef HAVE_SWAP
// When called in swap context as a library, we don't want to show the menu
if (!G_called_from_swap) {
#endif
ui_menu_main();
#ifdef HAVE_SWAP
}
#endif

// Reset context
explicit_bzero(&G_context, sizeof(G_context));
Expand Down Expand Up @@ -82,4 +90,4 @@ void app_main() {
return;
}
}
}
}
2 changes: 1 addition & 1 deletion src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ int crypto_sign_transaction(void) {
G_context.bip32_path[0] = 0x8000002C;
G_context.bip32_path[1] = 0x8001b207;
G_context.bip32_path[2] = G_context.tx_info.transaction.account;
G_context.bip32_path[3] = (uint32_t)(txin->address_type);
G_context.bip32_path[3] = (uint32_t) (txin->address_type);
G_context.bip32_path[4] = txin->address_index;

G_context.bip32_path_len = 5;
Expand Down
2 changes: 1 addition & 1 deletion src/handler/sign_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ int handler_sign_msg(buffer_t *cdata) {
G_context.bip32_path[0] = 0x8000002C;
G_context.bip32_path[1] = 0x8001b207;
G_context.bip32_path[2] = G_context.msg_info.account;
G_context.bip32_path[3] = (uint32_t)(G_context.msg_info.address_type);
G_context.bip32_path[3] = (uint32_t) (G_context.msg_info.address_type);
G_context.bip32_path[4] = G_context.msg_info.address_index;

G_context.bip32_path_len = 5;
Expand Down
62 changes: 56 additions & 6 deletions src/handler/sign_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,61 @@

#include "os.h"
#include "cx.h"
#include "buffer.h"
#include "swap.h" // G_swap_response_ready, G_called_from_swap

#include "sign_tx.h"
#include "sw.h"
#include "globals.h"
#include "display.h"
#include "constants.h"
#include "../apdu/dispatcher.h"
#include "../sw.h"
#include "../globals.h"
#include "../crypto.h"
#include "../ui/display.h"
#include "buffer.h"
#include "dispatcher.h"

#include "crypto.h"
#include "../transaction/types.h"
#include "../transaction/deserialize.h"
#include "../transaction/tx_validate.h"
#include "../transaction/utils.h"
#include "../helper/send_response.h"
#include "handle_swap.h"
#include "validate.h"

#ifdef HAVE_SWAP
static int check_and_sign_swap_tx(transaction_t *tx) {
if (G_swap_response_ready) {
// Safety against trying to make the app sign multiple TX
// This code should never be triggered as the app is supposed to exit after
// sending the signed transaction
PRINTF("Safety against double signing triggered\n");
os_sched_exit(-1);
} else {
// We will quit the app after this transaction, whether it succeeds or fails
PRINTF("Swap response is ready, the app will quit after the next send\n");
// This boolean will make the io_send_sw family instant reply +
// return to exchange
G_swap_response_ready = true;
}
uint64_t value = tx->tx_outputs[0].value;
uint64_t fees = calc_fees(tx->tx_inputs, tx->tx_input_len, tx->tx_outputs, tx->tx_output_len);
uint8_t to[ECDSA_ADDRESS_LEN + 1] = {0};
if (!script_public_key_to_address(to,
sizeof(to),
tx->tx_outputs[0].script_public_key,
sizeof(tx->tx_outputs[0].script_public_key))) {
// No need to exit here early since `to` would be all zeros if this failed
// and will never match any valid kaspa address
PRINTF("Failed to convert script public key to address\n");
}

if (swap_check_validity(value, fees, to)) {
PRINTF("Swap response validated\n");
validate_transaction(true);
}
// Unreachable because swap_check_validity() returns an error to exchange app OR
// validate_transaction() returns a success to exchange
return 0;
}
#endif // HAVE_SWAP

static int sign_input_and_send() {
int error = crypto_sign_transaction();
Expand Down Expand Up @@ -139,6 +181,14 @@ int handler_sign_tx(buffer_t *cdata, uint8_t type, bool more) {
// last APDU for this transaction, let's parse, display and request a sign confirmation
G_context.state = STATE_PARSED;

#ifdef HAVE_SWAP
// If we are in swap context, do not redisplay the message data
// Instead, ensure they are identical with what was previously displayed
if (G_called_from_swap) {
return check_and_sign_swap_tx(&G_context.tx_info.transaction);
}
#endif // HAVE_SWAP

return ui_display_transaction();
}
} else if (type == 3) {
Expand Down
Loading
Loading