Skip to content

Commit

Permalink
Merge branch 'dev' into fix/change-confirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
r4mmer authored Oct 3, 2024
2 parents 0445f6c + 9e217cd commit 563466a
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 31 deletions.
48 changes: 34 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,34 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2021-08-02
## [Unreleased]

### Added

- Inital commit with the brand new Hathor application
- Sign TX command
- Get XPUB command
- Confirm address command
- HTR + Version command
- Support for authority outputs
- Support for mint/melt/delegate/destroy operations

## [1.0.1] - 2021-09-22
## Changed

- Signal bits and version parsed separetely, version is now uint8_t instead of uint16_t.
- In sign tx command we check that change output index is valid (inside the output array) and not repeated.

## Fixed

- Auto confirmation of unreceived change outputs would fail tx signing
- Some parsing errors would not clean the global context, it could fail the next command.

## [1.1.1] - 2024-03-12

### Added

- Support for Nano X
- CodeQL security check in CI

### Security

- Code improvements to prevent null pointer exception and buffer overflow.
- Update deprecated OS calls
- Clean dead code

## [1.1.0] - 2022-02-4

Expand All @@ -34,14 +47,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Sign TX command now supports transactions with custom tokens

## [1.1.1] - 2024-03-12
## [1.0.1] - 2021-09-22

### Added

- CodeQL security check
- Support for Nano X

## [1.0.0] - 2021-08-02

### Added

- Inital commit with the brand new Hathor application
- Sign TX command
- Get XPUB command
- Confirm address command
- HTR + Version command


### Changed

- Code improvements to prevent null pointer exception and buffer overflow.
- Update deprecated OS calls
- Clean dead code
15 changes: 12 additions & 3 deletions doc/BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,29 @@ All dev commands are configured on a separate `.dev.Makefile`.
## Dev-env

This command will start a docker container with the configured dev-env where you can run any commands on the `Makefile` or the SDK, like [loading](https://developers.ledger.com/docs/nano-app/load/) the app on Nano S.
`make -f .dev.Makefile builder`


```bash
make -f .dev.Makefile builder
```

## Compilation

`make -f .dev.Makefile build`
```bash
make -f .dev.Makefile build
```

You can add the flag `DEBUG=1` at the end to compile on debug mode.

To remove all files generated from the build process run:
`make -f .dev.Makefile clean`
```bash
make -f .dev.Makefile clean
```

## Linter

Build linter with `make -f .dev.Makefile lint-build` and you can use the same linter (and linter configurations) as the CI using the commands:

- `make -f .dev.Makefile lint`
- `make -f .dev.Makefile lint-fix`

Expand Down
1 change: 1 addition & 0 deletions src/token/token_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ int8_t find_token_registry_index(uint8_t *uid) {
for (uint8_t i = 0; i < G_token_symbols.len; i++) {
if (memcmp(uid, G_token_symbols.tokens[i].uid, TOKEN_UID_LEN) == 0) return i;
}
PRINTF("[*] Registry length = %d\n", G_token_symbols.len);
return -1;
}
34 changes: 28 additions & 6 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,40 @@ poetry install
make install
```

### Launch with Speculos
### Run with Speculos

First start your application with Speculos
First you need to build and run the application with speculos.

On the root directory of the project run:

```bash
make -f .dev.Makefile build DEBUG=1
```
./path/to/speculos.py /path/to/app.elf --ontop --sdk 2.0

```bash
make -f .dev.Makefile speculos
```

then in the `tests` folder run
### Running tests

```
In the `tests` folder run:

```bash
make test
# or
poetry run pytest
```

### Running qa

You need to open the speculos web ui, the actual address will be shown in the speculos stdout.

In the `tests` folder run:

```bash
make qa
# or
make test
poetry run pytest qa.py
```

Obs: you need to restart speculos if the tests ran before since they configure automation rules which need to be turned off during qa.
21 changes: 21 additions & 0 deletions tests/app_client/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(self, transport: ApduTransport, debug: bool = False) -> None:
self.transport = transport

def get_app_and_version(self) -> Tuple[str, str]:
print("[+] sending: get_app_and_version request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.get_app_and_version()
) # type: int, bytes
Expand Down Expand Up @@ -44,6 +45,7 @@ def get_app_and_version(self) -> Tuple[str, str]:
return app_name, version

def get_version(self) -> Tuple[bytes, int, int, int]:
print("[+] sending: get_version request")
sw, response = self.transport.exchange_apdu_raw(self.builder.get_version())

if sw != 0x9000:
Expand All @@ -62,6 +64,7 @@ def get_version(self) -> Tuple[bytes, int, int, int]:

def get_address(self, bip32_path: str) -> str:

print("[+] sending: get_address request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.get_address(bip32_path)
)
Expand All @@ -72,6 +75,7 @@ def get_address(self, bip32_path: str) -> str:
return

def get_xpub(self, bip32_path: str) -> Tuple[bytes, bytes]:
print("[+] sending: get_xpub request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.get_xpub(bip32_path=bip32_path)
)
Expand Down Expand Up @@ -108,20 +112,28 @@ def sign_tx(
response: bytes = b""

signatures: List[bytes] = []

index = 0
for chunk in self.builder.sign_tx_send_data(
transaction=transaction,
change_list=change_list,
use_old_protocol=use_old_protocol,
):
print("[+] sending: sign_tx send for chunk {}\n".format(index))
index += 1
sw, response = self.transport.exchange_apdu_raw(chunk)
print("\n", "ledger_resp:", sw, response)

if sw != 0x9000:
raise DeviceException(error_code=sw, ins=InsType.INS_SIGN_TX)

time.sleep(0.1)

index = 0
# ask for signatures
for chunk in self.builder.sign_tx_signatures(transaction):
print("[+] sending: sign_tx sign for chunk {}\n".format(index))
index += 1
sw, response = self.transport.exchange_apdu_raw(chunk)
print("\n", "ledger_resp:", sw, response)

Expand All @@ -138,6 +150,7 @@ def sign_tx(
return signatures

def sign_token_data(self, token: Token) -> bytes:
print("[+] sending: sign_token_data request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.sign_token_data(token)
)
Expand All @@ -148,6 +161,7 @@ def sign_token_data(self, token: Token) -> bytes:
return response

def send_token_data(self, token: Token, signature: bytes, num: int = 0):
print("[+] sending: send_token_data request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.send_token_data(token, signature, num=num)
)
Expand All @@ -158,9 +172,15 @@ def send_token_data(self, token: Token, signature: bytes, num: int = 0):
def send_token_data_list(self, tokens: List[Token], signatures: List[bytes]):
assert len(tokens) == len(signatures)
for i, token in enumerate(tokens):
print(
"[+] sending: send_token_data_list request for token {} - {}".format(
i, token
)
)
self.send_token_data(token, signatures[i], num=i)

def verify_token_signature(self, token: Token, signature: bytes):
print("[+] sending: verify_token_signature request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.verify_token_signature(token, signature)
)
Expand All @@ -169,6 +189,7 @@ def verify_token_signature(self, token: Token, signature: bytes):
raise DeviceException(error_code=sw, ins=InsType.INS_GET_ADDRESS)

def reset_token_signatures(self):
print("[+] sending: reset_token_signatures request")
sw, response = self.transport.exchange_apdu_raw(
self.builder.reset_token_signatures()
)
Expand Down
45 changes: 39 additions & 6 deletions tests/qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

def test_qa_version(cmd):
print("QA::version")
assert cmd.get_version() == (b'HTR', 1, 0, 0)
assert cmd.get_version() == (b'HTR', 1, 1, 1)


def test_qa_address(cmd):
Expand Down Expand Up @@ -48,7 +48,39 @@ def test_qa_reset_token_signatures(cmd):
cmd.reset_token_signatures()


def test_qa_sign_tx_with_token(cmd):
def test_qa_sign_tx_custom_tokens(cmd):
path = "m/44'/280'/0'/0/10"
token1 = fake_token()
token2 = fake_token()
inputs = [
TxInput(fake.sha256(True), 1, path),
TxInput(fake.sha256(True), 0, path),
]
outputs = [
TxOutput(fake.pyint(1), fake_script(), 1),
TxOutput(fake.pyint(1), fake_script(), 2),
TxOutput(fake.pyint(1), fake_script()),
]
tx = fake_tx(inputs=inputs, outputs=outputs, tokens=[token1.uid, token2.uid])
print("QA::sign_tx_custom_tokens::token1:", str(token1))
print("QA::sign_tx_custom_tokens::token2:", str(token2))
print("QA::sign_tx_custom_tokens::tx:", str(tx))

print("QA::sign_tx_custom_tokens::sign::token1")
sig1 = cmd.sign_token_data(token1)
print("QA::sign_tx_custom_tokens::sign::token1")
sig2 = cmd.sign_token_data(token2)

print("QA::sign_tx_custom_tokens::send::token1")
cmd.send_token_data(token1, sig1, 0)
print("QA::sign_tx_custom_tokens::send::token2")
cmd.send_token_data(token2, sig2, 1)

print("QA::sign_tx_custom_tokens::sign::tx")
signatures = cmd.sign_tx(tx)


def test_qa_sign_tx_with_authority(cmd):
path = "m/44'/280'/0'/0/10"
# sign_token
token = fake_token()
Expand All @@ -57,12 +89,13 @@ def test_qa_sign_tx_with_token(cmd):
TxInput(fake.sha256(True), 0, path),
]
outputs = [
TxOutput(fake.pyint(1), fake_script(), 1),
TxOutput(1, fake_script(), 1, True),
TxOutput(2, fake_script(), 1, True),
TxOutput(fake.pyint(1), fake_script()),
]
tx = Transaction(1, [token.uid], inputs, outputs)
print("QA::sign_tx_with_token::token:", str(token))
print("QA::sign_tx_with_token::tx:", str(tx))
tx = Transaction(0, 1, [token.uid], inputs, outputs)
print("QA::sign_tx_with_authority::token:", str(token))
print("QA::sign_tx_with_authority::tx:", str(tx))
sig = cmd.sign_token_data(token)
# send_token_data
cmd.send_token_data(token, sig)
Expand Down
4 changes: 2 additions & 2 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def fake_tx(
outputs: Optional[TxOutput] = None,
tokens: Optional[bytes] = None,
) -> Transaction:
tx_inps = inputs or [fake_input() for _ in range(fake.pyint(1, 10))]
tx_outps = outputs or [fake_output() for _ in range(fake.pyint(1, 10))]
tx_inps = inputs or [fake_input() for _ in range(fake.pyint(1, 5))]
tx_outps = outputs or [fake_output() for _ in range(fake.pyint(1, 5))]
tkns = (
[fake.sha256(True) for _ in range(fake.pyint(1, 10))]
if tokens is None
Expand Down

0 comments on commit 563466a

Please sign in to comment.