Skip to content

Commit

Permalink
Add initial set of performance tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bigspider committed Jun 18, 2024
1 parent 41f5254 commit 75fa3e8
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 0 deletions.
41 changes: 41 additions & 0 deletions tests_perf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Benchmarks

The tests in this folder are meant to measure the performance of various app operations.

These tests are implemented in Python and can be executed either using the [Speculos](https://github.com/LedgerHQ/speculos) emulator or a Ledger Nano S+, Nano X, or Stax.

Python dependencies are listed in [requirements.txt](requirements.txt), install them using [pip](https://pypi.org/project/pip/)

```
pip install -r requirements.txt
```

## Build

The app must be built with the `AUTOAPPROVE_FOR_PERF_TESTS=1` parameter when calling `make`. This flag compiles the testnet app in a mode that requires no user interaction at all.

## Launch with Speculos

Performance measured in speculos is not a good proxy of the performance on a real device.

Simply run:

```
pytest
```

## Launch with your device

Compile and install the app on your device as normal.

To run the tests on your Ledger device, you also need to install an optional dependency

```
pip install ledgercomm[hid]
```

Be sure to have you device connected through USB and open on the bitcoin testnet app, sideloaded from the build above.

```
pytest --hid
```
Empty file added tests_perf/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions tests_perf/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

from pathlib import Path
from test_utils.fixtures import *
import random
import sys
import os

absolute_path = os.path.dirname(os.path.abspath(__file__))
relative_bitcoin_path = ('../bitcoin_client')
absolute_bitcoin_client_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), '../')
sys.path.append(os.path.join(absolute_path, relative_bitcoin_path))

from ledger_bitcoin import Chain # noqa: E402

TESTS_ROOT_DIR = Path(__file__).parent

random.seed(0) # make sure tests are repeatable
6 changes: 6 additions & 0 deletions tests_perf/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bip32>=3.4,<4.0
embit>=0.8.0,<0.9.0
ledgercomm>=1.2.1,<2.0.0
pytest>=8.2.2,<9.0.0
pytest-benchmark>=4.0.0,<5.0.0
typing-extensions>=3.7,<4.0
20 changes: 20 additions & 0 deletions tests_perf/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[tool:pytest]
addopts = --strict-markers

[pylint]
disable = C0114, # missing-module-docstring
C0115, # missing-class-docstring
C0116, # missing-function-docstring
C0103, # invalid-name
R0801, # duplicate-code
R0913 # too-many-arguments
extension-pkg-whitelist=hid

[pycodestyle]
max-line-length = 120

[mypy-hid.*]
ignore_missing_imports = True

[mypy-pytest.*]
ignore_missing_imports = True
77 changes: 77 additions & 0 deletions tests_perf/test_perf_sign_psbt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

from pathlib import Path

import pytest

from ledger_bitcoin import WalletPolicy, Client
from ledger_bitcoin.psbt import PSBT

from test_utils import txmaker

tests_root: Path = Path(__file__).parent


def make_psbt(wallet_policy: WalletPolicy, n_inputs: int, n_outputs: int) -> PSBT:
in_amounts = [10000 + 10000 * i for i in range(n_inputs)]
total_in = sum(in_amounts)
out_amounts = [total_in // n_outputs - i for i in range(n_outputs)]

change_index = 1

psbt = txmaker.createPsbt(
wallet_policy,
in_amounts,
out_amounts,
[i == change_index for i in range(n_outputs)]
)

sum_in = sum(in_amounts)
sum_out = sum(out_amounts)

assert sum_out < sum_in

return psbt


@pytest.mark.parametrize("n_inputs", [1, 3, 10])
def test_perf_sign_psbt_singlesig_pkh(client: Client, n_inputs: int, benchmark):
# PSBT for a legacy 2-output spend (1 change address)

wallet = WalletPolicy(
"",
"pkh(@0/**)",
[
"[f5acc2fd/44'/1'/0']tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT"
],
)

psbt = make_psbt(wallet, n_inputs, 2)

def sign_tx():
result = client.sign_psbt(psbt, wallet, None)

assert len(result) == n_inputs

benchmark.pedantic(sign_tx, rounds=1)


@pytest.mark.parametrize("n_inputs", [1, 3, 10])
def test_perf_sign_psbt_singlesig_wpkh(client: Client, n_inputs: int, benchmark):
# PSBT for a segwit 2-output spend (1 change address)

wallet = WalletPolicy(
"",
"wpkh(@0/**)",
[
"[f5acc2fd/84'/1'/0']tpubDCtKfsNyRhULjZ9XMS4VKKtVcPdVDi8MKUbcSD9MJDyjRu1A2ND5MiipozyyspBT9bg8upEp7a8EAgFxNxXn1d7QkdbL52Ty5jiSLcxPt1P"
],
)

psbt = make_psbt(wallet, n_inputs, 2)

def sign_tx():
result = client.sign_psbt(psbt, wallet, None)

assert len(result) == n_inputs

benchmark.pedantic(sign_tx, rounds=1)

0 comments on commit 75fa3e8

Please sign in to comment.