Skip to content

Commit

Permalink
Add SignTx tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cedelavergne-ledger committed Oct 25, 2024
1 parent a61255e commit de9eaaf
Show file tree
Hide file tree
Showing 4 changed files with 737 additions and 0 deletions.
230 changes: 230 additions & 0 deletions tests/application_client/command_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from input_files.cvote import MAX_CIP36_PAYLOAD_SIZE, CVoteTestCase
from input_files.signOpCert import OpCertTestCase
from input_files.signMsg import SignMsgTestCase, MessageAddressFieldType
from input_files.signTx import SignTxTestCase, TxInput, TxOutput
from input_files.signTx import TxOutputBabbage, ThirdPartyAddressParams

from application_client.app_def import InsType, AddressType, StakingDataSourceType

Expand All @@ -27,6 +29,35 @@ class P1Type(IntEnum):
P1_CHUNK = 0x02
P1_CONFIRM = 0x03
P1_WITNESS = 0x04
P1_INPUTS = 0x02
P1_OUTPUTS = 0x03
P1_FEE = 0x04
P1_TTL = 0x05
P1_CERTIFICATES = 0x06
P1_WITHDRAWALS = 0x07
P1_AUX_DATA = 0x08
P1_VALIDITY_INTERVAL_START = 0x09
P1_TX_CONFIRM = 0x0a
P1_MINT = 0x0b
P1_SCRIPT_DATA_HASH = 0x0c
P1_COLLATERAL_INPUTS = 0x0d
P1_REQUIRED_SIGNERS = 0x0e
P1_TX_WITNESSES = 0x0f
P1_TOTAL_COLLATERAL = 0x10
P1_REFERENCE_INPUTS = 0x11
P1_COLLATERAL_OUTPUT = 0x12
P1_VOTING_PROCEDURES = 0x13
P1_TREASURY = 0x15
P1_DONATION = 0x16


class P2Type(IntEnum):
P2_BASIC_DATA = 0x30
P2_DATUM = 0x34
P2_DATUM_CHUNK = 0x35
P2_SCRIPT = 0x36
P2_SCRIPT_CHUNK = 0x37
P2_CONFIRM = 0x33


class CommandBuilder:
Expand Down Expand Up @@ -270,6 +301,205 @@ def sign_msg_confirm(self) -> bytes:
return self._serialize(InsType.SIGN_MSG, P1Type.P1_CONFIRM, 0x00)


def sign_tx_init(self, testCase: SignTxTestCase) -> bytes:
"""APDU Builder for Sign TX - INIT step
Args:
testCase (SignTxTestCase): Test parameters
Returns:
Serial data APDU
"""

# Serialization format:
# Options (1B)
# NetworkId (1B)
# ProtocolMagic (4B)
# TTL option flag (1B)
# auxiliary Data option flag (1B)
# validityIntervalStart option flag (1B)
# mint
# scriptDataHash
# includeNetworkId
# collateralOutput
# totalCollateral
# treasury
# donation
# signingMode
# TX inputs length (4B)
# TX outputs length (4B)
# TX certificates length (4B)
# TX withdrawals length (4B)
# witnessLegacy
# collateralInputs
# requiredSigners
# referenceInputs
# votingProcedures
# witnessBabbage

data = bytes()
data += testCase.options.to_bytes(8, "big")
data += testCase.tx.network.networkId.to_bytes(1, "big")
data += testCase.tx.network.protocol.to_bytes(4, "big")
data += self._serializeOptionFlags(testCase.tx.ttl is not None)
data += self._serializeOptionFlags(testCase.tx.auxiliaryData is not None)
data += self._serializeOptionFlags(testCase.tx.validityIntervalStart is not None)
data += self._serializeOptionFlags(len(testCase.tx.mint) > 0)
data += self._serializeOptionFlags(testCase.tx.scriptDataHash is not None)
data += self._serializeOptionFlags(testCase.tx.includeNetworkId is not None)
data += self._serializeOptionFlags(testCase.tx.collateralOutput is not None)
data += self._serializeOptionFlags(testCase.tx.totalCollateral is not None)
data += self._serializeOptionFlags(testCase.tx.treasury is not None)
data += self._serializeOptionFlags(testCase.tx.donation is not None)
data += testCase.signingMode.to_bytes(1, "big")
data += len(testCase.tx.inputs).to_bytes(4, "big")
data += len(testCase.tx.outputs).to_bytes(4, "big")
data += len(testCase.tx.certificates).to_bytes(4, "big")
data += len(testCase.tx.withdrawals).to_bytes(4, "big")
data += len(testCase.tx.collateralInputs).to_bytes(4, "big")
data += len(testCase.tx.requiredSigners).to_bytes(4, "big")
data += len(testCase.tx.referenceInputs).to_bytes(4, "big")
data += len(testCase.tx.votingProcedures).to_bytes(4, "big")
# Compute the number of Babbage Witnesses Buffer
nbPaths = sum(1 for input in testCase.tx.inputs if input.path)
data += nbPaths.to_bytes(4, "big") # number of Babbage Witnesses Buffer
return self._serialize(InsType.SIGN_TX, P1Type.P1_INIT, 0x00, data)


def sign_tx_inputs(self, txInput: TxInput) -> bytes:
"""APDU Builder for Sign TX - INPUTS step
Args:
txInput (TxInput): Test parameters
Returns:
Serial data APDU
"""

# Serialization format:
data = bytes()
data += bytes.fromhex(txInput.txHashHex)
data += txInput.outputIndex.to_bytes(4, "big")
return self._serialize(InsType.SIGN_TX, P1Type.P1_INPUTS, 0x00, data)


def sign_tx_outputs_basic(self, txOutput: TxOutput) -> bytes:
"""APDU Builder for Sign TX - OUTPUTS step - BASIC DATA level
Args:
txOutput (TxOutput): Test parameters
Returns:
Serial data APDU
"""

# Serialization format:
# Format (1B)
# Tx Output destination
# Coin (8B)
# TokenBundle Length (4B)
# datum option flag (1B)
# referenceScriptHex option flag (1B)

data = bytes()
data += txOutput.format.to_bytes(1, "big")
data += txOutput.destinationType.to_bytes(1, "big")
if isinstance(txOutput.destination, ThirdPartyAddressParams):
data += int(len(txOutput.destination.addressHex) / 2).to_bytes(4, "big")
data += bytes.fromhex(txOutput.destination.addressHex)
else:
data += self._serializeAddressParams(txOutput.destination)
data += txOutput.amount.to_bytes(8, "big")
data += len(txOutput.tokenBundle).to_bytes(4, "big")
data += self._serializeOptionFlags(txOutput.datumHashHex is not None)
if isinstance(txOutput, TxOutputBabbage):
data += self._serializeOptionFlags(txOutput.referenceScriptHex is not None)
return self._serialize(InsType.SIGN_TX, P1Type.P1_OUTPUTS, P2Type.P2_BASIC_DATA, data)


def sign_tx_outputs_confirm(self) -> bytes:
"""APDU Builder for Sign TX - OUTPUTS step - CONFIRM level
Returns:
Serial data APDU
"""

return self._serialize(InsType.SIGN_TX, P1Type.P1_OUTPUTS, P2Type.P2_CONFIRM)


def sign_tx_fee(self, testCase: SignTxTestCase) -> bytes:
"""APDU Builder for Sign TX - FEE step
Args:
testCase (SignTxTestCase): Test parameters
Returns:
Serial data APDU
"""

# Serialization format:
data = bytes()
data += testCase.tx.fee.to_bytes(8, "big")
return self._serialize(InsType.SIGN_TX, P1Type.P1_FEE, 0x00, data)


def sign_tx_ttl(self, testCase: SignTxTestCase) -> bytes:
"""APDU Builder for Sign TX - TTL step
Args:
testCase (SignTxTestCase): Test parameters
Returns:
Serial data APDU
"""

# Serialization format:
data = bytes()
assert testCase.tx.ttl is not None
data += testCase.tx.ttl.to_bytes(8, "big")
return self._serialize(InsType.SIGN_TX, P1Type.P1_TTL, 0x00, data)


def sign_tx_confirm(self) -> bytes:
"""APDU Builder for Sign TX - CONFIRM step
Returns:
Serial data APDU
"""

# Serialization format:
return self._serialize(InsType.SIGN_TX, P1Type.P1_TX_CONFIRM)


def sign_tx_witness(self, path: str) -> bytes:
"""APDU Builder for Sign TX - WITNESS step
Args:
path (str): Input Test path
Returns:
Serial data APDU
"""

# Serialization format:
data = bytes()
data += pack_derivation_path(path)
return self._serialize(InsType.SIGN_TX, P1Type.P1_TX_WITNESSES, 0x00, data)


def _serializeOptionFlags(self, included: bool) -> bytes:
"""Serialize Flag option value
Args:
included (boolean): Flag value
Returns:
Serial data APDU
"""
value = 0x02 if included else 0x01
return value.to_bytes(1, "big")


def _serializeAddressParams(self, testCase: DeriveAddressTestCase) -> bytes:
"""Serialize address parameters
Expand Down
107 changes: 107 additions & 0 deletions tests/application_client/command_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from input_files.cvote import CVoteTestCase
from input_files.signOpCert import OpCertTestCase
from input_files.signMsg import SignMsgTestCase
from input_files.signTx import SignTxTestCase, TxInput, TxOutput

from application_client.command_builder import CommandBuilder, P1Type
from application_client.app_def import Errors
Expand Down Expand Up @@ -280,3 +281,109 @@ def sign_msg_confirm(self) -> Generator[None, None, None]:

with self._exchange_async(self._cmd_builder.sign_msg_confirm()):
yield


@contextmanager
def sign_tx_init(self, testCase: SignTxTestCase) -> Generator[None, None, None]:
"""APDU Sign TX - INIT step
Args:
testCase (SignTxTestCase): Test parameters
Returns:
Generator
"""

with self._exchange_async(self._cmd_builder.sign_tx_init(testCase)):
yield


def sign_tx_inputs(self, txInput: TxInput) -> RAPDU:
"""APDU Sign TX - INPUTS step
Args:
txInput (TxInput): Test parameters
Returns:
Response APDU
"""

return self._exchange(self._cmd_builder.sign_tx_inputs(txInput))


@contextmanager
def sign_tx_outputs_basic(self, txOutput: TxOutput) -> Generator[None, None, None]:
"""APDU Sign TX - OUTPUTS step - BASIC DATA level
Args:
txOutput (TxOutput): Test parameters
Returns:
Generator
"""

with self._exchange_async(self._cmd_builder.sign_tx_outputs_basic(txOutput)):
yield


def sign_tx_outputs_confirm(self) -> RAPDU:
"""APDU Sign TX - OUTPUTS step -CONFIRM level
Returns:
Response APDU
"""

return self._exchange(self._cmd_builder.sign_tx_outputs_confirm())


@contextmanager
def sign_tx_fee(self, testCase: SignTxTestCase) -> Generator[None, None, None]:
"""APDU Sign TX - FEE step
Args:
testCase (SignTxTestCase): Test parameters
Returns:
Generator
"""

with self._exchange_async(self._cmd_builder.sign_tx_fee(testCase)):
yield


def sign_tx_ttl(self, testCase: SignTxTestCase) -> RAPDU:
"""APDU Sign TX - TTL step
Args:
testCase (SignTxTestCase): Test parameters
Returns:
Response APDU
"""

return self._exchange(self._cmd_builder.sign_tx_ttl(testCase))


@contextmanager
def sign_tx_confirm(self) -> Generator[None, None, None]:
"""APDU Sign TX - CONFIRM step
Returns:
Generator
"""

with self._exchange_async(self._cmd_builder.sign_tx_confirm()):
yield


def sign_tx_witness(self, path: str) -> RAPDU:
"""APDU Sign TX - CONFIRM step
Args:
path (str): Input Test path
Returns:
Response APDU
"""

return self._exchange(self._cmd_builder.sign_tx_witness(path))
Loading

0 comments on commit de9eaaf

Please sign in to comment.