Skip to content

Commit b690884

Browse files
committed
Add ledger tests to CI
1 parent 0cb2d36 commit b690884

5 files changed

Lines changed: 53 additions & 63 deletions

File tree

.github/workflows/build.yml

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ jobs:
246246
with:
247247
submodules: recursive
248248
path: ./mintlayer-core
249+
- name: Update local dependency repositories
250+
run: sudo apt-get update
251+
- name: Install build dependencies
252+
run: sudo apt-get install -yqq --no-install-recommends build-essential pkg-config libdbus-1-dev libusb-1.0-0-dev
249253
- name: Install rust
250254
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
251255
- name: Install cargo-nextest
@@ -283,28 +287,31 @@ jobs:
283287
path: ./mintlayer-core
284288
- name: Install cargo-nextest
285289
uses: taiki-e/install-action@nextest
286-
- name: Build Ledger app, run emulator and execute tests
290+
- name: Build Ledger app in container
287291
run: |
288292
sudo docker run --rm \
289-
-v "$(realpath ./mintlayer-ledger-app):/app" \
290-
-v "$(realpath ./mintlayer-core):/mintlayer_core" \
291-
ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest \
292-
sh -c '
293-
set -e
294-
echo "--- Building Ledger app ---"
295-
cd /app
296-
cargo ledger build nanosplus
293+
-v "$(realpath ./mintlayer-ledger-app):/app" \
294+
ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest \
295+
sh -c 'cd /app && cargo ledger build nanosplus'
296+
- name: Run Ledger emulator and execute tests
297+
run: |
298+
set -e
297299
298-
echo "--- Starting Speculos Emulator in the background ---"
299-
speculos --apdu-port 9999 --api-port 5001 --display headless --model nanosp target/nanosplus/release/mintlayer-app &
300-
SPECULOS_PID=$!
301-
sleep 5
300+
sudo docker run -d --rm --name ledger-emulator \
301+
-v "$(realpath ./mintlayer-ledger-app):/app" \
302+
--publish 5001:5001 --publish 9999:9999 \
303+
ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest \
304+
sh -c 'cd /app && speculos --apdu-port 9999 --api-port 5001 --display headless --model nanosp -s "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" target/nanosplus/release/mintlayer-app'
302305
303-
echo "--- Running Ledger device tests ---"
304-
cd /mintlayer_core
305-
cargo-nextest nextest run --archive-file ledger-tests.tar.zst -j1
306+
echo "--- Waiting for emulator to initialize ---"
307+
sleep 15
306308
307-
echo "--- Stopping Speculos Emulator ---"
308-
kill $SPECULOS_PID
309-
'
310-
timeout-minutes: 10
309+
# Set up a trap to ensure the container is stopped even if tests fail or the job is cancelled
310+
trap "echo '--- Dumping Ledger emulator logs ---'; sudo docker logs ledger-emulator; echo '--- Stopping Ledger emulator ---'; sudo docker stop ledger-emulator" EXIT
311+
312+
echo "--- Running Ledger device tests on the host ---"
313+
cd ./mintlayer-core
314+
cargo-nextest nextest run --archive-file ledger-tests.tar.zst -j1 ledger_signer || test_exit_code=$?
315+
316+
exit $test_exit_code
317+
timeout-minutes: 15

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ rev = "510bb3ca30639af4bdb12a918b6bbbdb75fa5f52"
273273
[workspace.dependencies.mintlayer-ledger-messages]
274274
git = "https://github.com/mintlayer/mintlayer-ledger-app"
275275
# The commit "Fix comments"
276-
rev = "2fa1c33e536f94ba3cac94b97054902674580686"
276+
rev = "78bd2e2514be3a6db83e7493eaaa03c40acc5409"
277277
package = "messages"
278278

279279
[workspace.dependencies.trezor-client]

wallet/src/signer/ledger_signer/ledger_messages.rs

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,28 @@ use common::{
2222
};
2323
use crypto::key::{
2424
extended::ExtendedPublicKey,
25-
hdkd::{
26-
chain_code::{ChainCode, CHAINCODE_LENGTH},
27-
derivation_path::DerivationPath,
28-
},
25+
hdkd::{chain_code::ChainCode, derivation_path::DerivationPath},
2926
secp256k1::{extended_keys::Secp256k1ExtendedPublicKey, Secp256k1PublicKey},
3027
};
31-
use serialization::{Decode, DecodeAll, Encode};
28+
use serialization::Encode;
3229
use utils::ensure;
3330
use wallet_types::hw_data::LedgerFullInfo;
3431

3532
use ledger_lib::Exchange;
3633
use mintlayer_ledger_messages::{
3734
decode_all as ledger_decode_all, encode as ledger_encode, AddrType, Amount as LAmount,
38-
Bip32Path as LedgerBip32Path, CoinType, InputAdditionalInfoReq, Ins,
39-
OutputValue as LOutputValue, P1SignTx, PubKeyP1, PublicKeyReq, SignMessageReq, SignTxReq,
40-
TxInput as LTxInput, TxInputReq, TxMetadataReq, TxOutput as LTxOutput, TxOutputReq, APDU_CLASS,
41-
H256 as LH256, P1_APP_NAME, P1_GET_VERSION, P1_SIGN_NEXT, P1_SIGN_START, P2_DONE, P2_SIGN_MORE,
35+
Bip32Path as LedgerBip32Path, CoinType, GetPublicKeyRespones, GetVersionRespones,
36+
InputAdditionalInfoReq, Ins, MsgSignature, OutputValue as LOutputValue, P1SignTx, PubKeyP1,
37+
PublicKeyReq, SignMessageReq, SignTxReq, Signature as LedgerSignature, TxInput as LTxInput,
38+
TxInputReq, TxMetadataReq, TxOutput as LTxOutput, TxOutputReq, APDU_CLASS, H256 as LH256,
39+
P1_APP_NAME, P1_GET_VERSION, P1_SIGN_NEXT, P1_SIGN_START, P2_DONE, P2_SIGN_MORE,
4240
};
4341

4442
const MAX_ADPU_LEN: usize = (u8::MAX - 5) as usize; // 4 bytes for the header + 1 for len
4543
const TIMEOUT_DUR: Duration = Duration::from_secs(100);
4644
const OK_RESPONSE: u16 = 0x9000;
4745
const TX_VERSION: u8 = 1;
4846

49-
#[derive(Decode)]
50-
pub struct LedgerSignature {
51-
pub signature: [u8; 64],
52-
pub multisig_idx: Option<u32>,
53-
}
54-
5547
struct SignatureResult {
5648
sig: LedgerSignature,
5749
input_idx: usize,
@@ -129,10 +121,9 @@ pub async fn sign_challenge<L: Exchange>(
129121

130122
let resp = send_chunked(ledger, Ins::SIGN_MSG, P1_SIGN_NEXT, message).await?;
131123

132-
let sig_len = *resp.first().ok_or(LedgerError::InvalidResponse)? as usize;
133-
let sig = resp.as_slice().get(1..1 + sig_len).ok_or(LedgerError::InvalidResponse)?;
124+
let sig: MsgSignature = ledger_decode_all(&resp).ok_or(LedgerError::InvalidResponse)?;
134125

135-
Ok(sig.to_vec())
126+
Ok(sig.signature.to_vec())
136127
}
137128

138129
pub async fn get_app_name<L: Exchange>(ledger: &mut L) -> Result<Vec<u8>, ledger_lib::Error> {
@@ -161,13 +152,12 @@ pub async fn check_current_app<L: Exchange>(ledger: &mut L) -> SignerResult<Ledg
161152
.await
162153
.map_err(|err| LedgerError::DeviceError(err.to_string()))?;
163154
let ver = ok_response(resp)?;
164-
let app_version = match ver.as_slice() {
165-
[major, minor, patch] => common::primitives::semver::SemVer {
166-
major: *major,
167-
minor: *minor,
168-
patch: *patch as u16,
169-
},
170-
_ => return Err(SignerError::LedgerError(LedgerError::InvalidResponse)),
155+
let app_version_resp: GetVersionRespones =
156+
ledger_decode_all(&ver).ok_or(LedgerError::InvalidResponse)?;
157+
let app_version = common::primitives::semver::SemVer {
158+
major: app_version_resp.major,
159+
minor: app_version_resp.minor,
160+
patch: app_version_resp.patch as u16,
171161
};
172162

173163
Ok(LedgerFullInfo { app_version })
@@ -191,20 +181,13 @@ pub async fn get_extended_public_key<L: Exchange>(
191181
)
192182
.await?;
193183

194-
let pk_len = *resp.first().ok_or(LedgerError::InvalidResponse)? as usize;
195-
let public_key = resp.as_slice().get(1..1 + pk_len).ok_or(LedgerError::InvalidResponse)?;
196-
let chain_code_len = *resp.get(1 + pk_len).ok_or(LedgerError::InvalidResponse)? as usize;
197-
let chain_code: [_; CHAINCODE_LENGTH] = resp
198-
.as_slice()
199-
.get(2 + pk_len..2 + pk_len + chain_code_len)
200-
.ok_or(LedgerError::InvalidResponse)?
201-
.try_into()
202-
.map_err(|_| LedgerError::InvalidKey)?;
184+
let resp: GetPublicKeyRespones =
185+
ledger_decode_all(&resp).ok_or(LedgerError::InvalidResponse)?;
203186

204187
let extended_public_key = Secp256k1ExtendedPublicKey::new_unchecked(
205188
derivation_path,
206-
ChainCode::from(chain_code),
207-
Secp256k1PublicKey::from_bytes(public_key).map_err(|_| LedgerError::InvalidKey)?,
189+
ChainCode::from(resp.chain_code),
190+
Secp256k1PublicKey::from_bytes(&resp.public_key).map_err(|_| LedgerError::InvalidKey)?,
208191
);
209192

210193
Ok(ExtendedPublicKey::new(extended_public_key))
@@ -286,8 +269,8 @@ fn decode_signature_response(resp: &[u8]) -> Result<SignatureResult, LedgerError
286269
let input_idx = *resp.first().ok_or(LedgerError::InvalidResponse)? as usize;
287270
let has_more_signatures = *resp.last().ok_or(LedgerError::InvalidResponse)? == P2_SIGN_MORE;
288271

289-
let sig = LedgerSignature::decode_all(&mut &resp[..resp.len() - 1][1..])
290-
.map_err(|_| LedgerError::InvalidResponse)?;
272+
let sig: LedgerSignature =
273+
ledger_decode_all(&resp[..resp.len() - 1][1..]).ok_or(LedgerError::InvalidResponse)?;
291274

292275
Ok(SignatureResult {
293276
sig,

wallet/src/signer/ledger_signer/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use crate::{
2323
ledger_signer::ledger_messages::{
2424
check_current_app, get_app_name, get_extended_public_key, sign_challenge, sign_tx,
2525
to_ledger_amount, to_ledger_output_value, to_ledger_tx_input, to_ledger_tx_output,
26-
LedgerSignature,
2726
},
2827
utils::{is_htlc_utxo, produce_uniparty_signature_for_input},
2928
Signer, SignerError, SignerProvider, SignerResult,
@@ -81,7 +80,8 @@ use itertools::{izip, Itertools};
8180
use ledger_lib::{Exchange, Filters, LedgerHandle, LedgerProvider, Transport};
8281
use mintlayer_ledger_messages::{
8382
AddrType, Bip32Path as LedgerBip32Path, CoinType, InputAdditionalInfoReq,
84-
InputAddressPath as LedgerInputAddressPath, TxInputReq, TxOutputReq,
83+
InputAddressPath as LedgerInputAddressPath, Signature as LedgerSignature, TxInputReq,
84+
TxOutputReq,
8585
};
8686
use randomness::make_true_rng;
8787
use tokio::sync::Mutex;
@@ -669,8 +669,8 @@ where
669669
(Some(destination), None) => {
670670
let standalone = match standalone_inputs.get(&(input_index as u32)).map(|x| x.as_slice()) {
671671
Some([standalone]) => standalone,
672+
Some([]) | None => return Ok((None, SignatureStatus::NotSigned, SignatureStatus::NotSigned)),
672673
Some(_) => return Err(LedgerError::MultisigSignatureReturned.into()),
673-
None => return Ok((None, SignatureStatus::NotSigned, SignatureStatus::NotSigned))
674674
};
675675

676676
let sig = produce_uniparty_signature_for_input(

0 commit comments

Comments
 (0)