You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/solana/transaction/instruction.py

158 lines
4.7 KiB

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any, TypeGuard
from typing_extensions import Self
from ..types import (
Account,
AccountTemplate,
InstructionData,
PropertyTemplate,
UIProperty,
)
class Instruction:
program_id: str
instruction_id: int | None
property_templates: list[PropertyTemplate]
accounts_template: list[AccountTemplate]
ui_name: str
ui_properties: list[UIProperty]
parsed_data: dict[str, Any]
parsed_accounts: dict[str, Account]
is_program_supported: bool
is_instruction_supported: bool
is_ui_hidden: bool
instruction_data: InstructionData
accounts: list[Account]
multisig_signers: list[Account]
is_deprecated_warning: str | None = None
@staticmethod
def parse_instruction_data(
instruction_data: InstructionData, property_templates: list[PropertyTemplate]
):
from trezor.utils import BufferReader
from trezor.wire import DataError
reader = BufferReader(instruction_data)
parsed_data = {}
for property_template in property_templates:
is_included = True
if property_template.is_optional:
is_included = True if reader.get() == 1 else False
parsed_data[property_template.name] = (
property_template.parse(reader) if is_included else None
)
if reader.remaining_count() != 0:
raise DataError("Invalid transaction data")
return parsed_data
@staticmethod
def parse_instruction_accounts(
accounts: list[Account], accounts_template: list[AccountTemplate]
):
parsed_account = {}
for i, account_template in enumerate(accounts_template):
if i >= len(accounts):
if account_template.optional:
continue
else:
raise ValueError # "Account is missing
parsed_account[account_template.name] = accounts[i]
return parsed_account
def __init__(
self,
instruction_data: InstructionData,
program_id: str,
accounts: list[Account],
instruction_id: int | None,
property_templates: list[PropertyTemplate],
accounts_template: list[AccountTemplate],
ui_properties: list[UIProperty],
ui_name: str,
is_program_supported: bool = True,
is_instruction_supported: bool = True,
is_ui_hidden: bool = False,
supports_multisig: bool = False,
is_deprecated_warning: str | None = None,
) -> None:
self.program_id = program_id
self.instruction_id = instruction_id
self.property_templates = property_templates
self.accounts_template = accounts_template
self.ui_name = ui_name
self.ui_properties = ui_properties
self.is_ui_hidden = is_ui_hidden
self.is_program_supported = is_program_supported
self.is_instruction_supported = is_instruction_supported
self.is_deprecated_warning = is_deprecated_warning
self.instruction_data = instruction_data
self.accounts = accounts
if self.is_instruction_supported:
self.parsed_data = self.parse_instruction_data(
instruction_data, property_templates
)
self.parsed_accounts = self.parse_instruction_accounts(
accounts, accounts_template
)
self.multisig_signers = accounts[len(accounts_template) :]
if self.multisig_signers and not supports_multisig:
raise ValueError # Multisig not supported
else:
self.parsed_data = {}
self.parsed_accounts = {}
self.multisig_signers = []
def __getattr__(self, attr: str) -> Any:
if attr in self.parsed_data:
return self.parsed_data[attr]
if attr in self.parsed_accounts:
return self.parsed_accounts[attr]
raise AttributeError # Attribute not found
def get_property_template(self, property: str) -> PropertyTemplate:
for property_template in self.property_templates:
if property_template.name == property:
return property_template
raise ValueError # Property not found
def get_account_template(self, account_name: str) -> AccountTemplate:
for account_template in self.accounts_template:
if account_template.name == account_name:
return account_template
raise ValueError # Account not found
@classmethod
def is_type_of(cls, ins: Any) -> TypeGuard[Self]:
# gets overridden in `instructions.py` `FakeClass`
raise NotImplementedError