Skip to content

Commit

Permalink
feat: add support for Stellar Soroban
Browse files Browse the repository at this point in the history
  • Loading branch information
overcat committed Jan 3, 2024
1 parent c09f9e8 commit a7fa60a
Show file tree
Hide file tree
Showing 32 changed files with 7,079 additions and 287 deletions.
197 changes: 197 additions & 0 deletions common/protob/messages-stellar.proto
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,200 @@ message StellarSignedTx {
required bytes public_key = 1; // public key for the private key used to sign data
required bytes signature = 2; // signature suitable for sending to the Stellar network
}

/**
* @embed
*/
message StellarSCVal {
required StellarSCValType type = 1;
optional bool b = 2; // SCV_VOID
reserved 3; // SCV_ERROR, not supported yet
optional uint32 u32 = 4; // SCV_U32
optional sint32 i32 = 5; // SCV_I32
optional uint64 u64 = 6; // SCV_U64
optional sint64 i64 = 7; // SCV_I64
optional uint64 timepoint = 8; // SCV_TIMEPOINT
optional uint64 duration = 9; // SCV_DURATION
optional StellarUInt128Parts u128 = 10; // SCV_U128
optional StellarInt128Parts i128 = 11; // SCV_I128
optional StellarUInt256Parts u256 = 12; // SCV_U256
optional StellarInt256Parts i256 = 13; // SCV_I256
optional bytes bytes = 14; // SCV_BYTES
optional string string = 15; // SCV_STRING, We do not allow sending binary data here, which is the same as how memo_text is handled, see: https://github.com/trezor/trezor-firmware/issues/610
optional string symbol = 16; // SCV_SYMBOL
repeated StellarSCVal vec = 17; // SCV_VEC
repeated StellarSCValMapEntry map = 18; // SCV_MAP
optional StellarSCAddress address = 19; // SCV_ADDRESS
optional sint64 nonce_key = 20; // SCV_LEDGER_KEY_NONCE
reserved 21; // SCV_CONTRACT_INSTANCE, not supported yet

message StellarUInt128Parts {
required uint64 hi = 1;
required uint64 lo = 2;
}

message StellarInt128Parts {
required sint64 hi = 1;
required uint64 lo = 2;
}

message StellarUInt256Parts {
required uint64 hi_hi = 1;
required uint64 hi_lo = 2;
required uint64 lo_hi = 3;
required uint64 lo_lo = 4;
}

message StellarInt256Parts {
required sint64 hi_hi = 1;
required uint64 hi_lo = 2;
required uint64 lo_hi = 3;
required uint64 lo_lo = 4;
}

message StellarSCAddress {
required StellarSCAddressType type = 1;
required string address = 2; // stellar account address (G...) or contract address (C...), TODO: bytes?

enum StellarSCAddressType
{
SC_ADDRESS_TYPE_ACCOUNT = 0;
SC_ADDRESS_TYPE_CONTRACT = 1;
};
}

message StellarSCValMapEntry {
optional StellarSCVal key = 1;
optional StellarSCVal value = 2;
}

enum StellarSCValType {
SCV_BOOL = 0;
SCV_VOID = 1;
SCV_ERROR = 2;
SCV_U32 = 3;
SCV_I32 = 4;
SCV_U64 = 5;
SCV_I64 = 6;
SCV_TIMEPOINT = 7;
SCV_DURATION = 8;
SCV_U128 = 9;
SCV_I128 = 10;
SCV_U256 = 11;
SCV_I256 = 12;
SCV_BYTES = 13;
SCV_STRING = 14;
SCV_SYMBOL = 15;
SCV_VEC = 16;
SCV_MAP = 17;
SCV_ADDRESS = 18;
SCV_CONTRACT_INSTANCE = 19;
SCV_LEDGER_KEY_CONTRACT_INSTANCE = 20;
SCV_LEDGER_KEY_NONCE = 21;
}
}

/**
* @embed
*/
message StellarInvokeContractArgs {
required StellarSCVal.StellarSCAddress contract_address = 1;
required string function_name = 2;
repeated StellarSCVal args = 3;
}

/**
* @embed
*/
message StellarSorobanAuthorizedFunction {
required StellarSorobanAuthorizedFunctionType type = 1;
optional StellarInvokeContractArgs contract_fn = 2;

enum StellarSorobanAuthorizedFunctionType {
SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN = 0;
SOROBAN_AUTHORIZED_FUNCTION_TYPE_CREATE_CONTRACT_HOST_FN = 1; // Creating a contract can be done using any address without security issues, so we will not provide support for it at this time.
}
}

/**
* @embed
*/
message StellarSorobanAuthorizedInvocation {
required StellarSorobanAuthorizedFunction function = 1;
repeated StellarSorobanAuthorizedInvocation sub_invocations = 2;
}

/**
* @embed
*/
message StellarHostFunction {
required StellarHostFunctionType type = 1;
optional StellarInvokeContractArgs invoke_contract = 2;

enum StellarHostFunctionType
{
HOST_FUNCTION_TYPE_INVOKE_CONTRACT = 0;
HOST_FUNCTION_TYPE_CREATE_CONTRACT = 1;
HOST_FUNCTION_TYPE_UPLOAD_CONTRACT_WASM = 2;
}
}

/**
* @embed
*/
message StellarSorobanAddressCredentials {
required StellarSCVal.StellarSCAddress address = 1;
required sint64 nonce = 2;
required uint32 signature_expiration_ledger = 3;
required StellarSCVal signature = 4;
}

/**
* @embed
*/
message StellarSorobanCredentials {
required StellarSorobanCredentialsType type = 1;
optional StellarSorobanAddressCredentials address = 2;

enum StellarSorobanCredentialsType
{
SOROBAN_CREDENTIALS_SOURCE_ACCOUNT = 0;
SOROBAN_CREDENTIALS_ADDRESS = 1;
};
}

/**
* @embed
*/
message StellarSorobanAuthorizationEntry {
required StellarSorobanCredentials credentials = 1;
required StellarSorobanAuthorizedInvocation root_invocation = 2;
}

message StellarInvokeHostFunctionOp {
optional string source_account = 1; // (optional) source account address
required StellarHostFunction function = 2;
repeated StellarSorobanAuthorizationEntry auth = 3;
}

message StellarTxExtRequest {
}

message StellarTxExt {
// For soroban transactions, v = 1
required sint32 v = 1;
optional bytes soroban_data = 2; // (optional) soroban data, exists only if v == 1
}

/**
* Request: ask device to sign Stellar Soroban authorization
* @start
* @next StellarSignedTx
*/
message StellarSignSorobanAuthorization {
repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/148'/index'
required string network_passphrase = 2; // passphrase for signing messages on the destination network
required sint64 nonce = 3; // nonce for the authorization
required uint32 signature_expiration_ledger = 4; // ledger number after which the signature expires
required StellarSorobanAuthorizedInvocation invocation = 5; // the invocation to authorize
}
14 changes: 14 additions & 0 deletions common/protob/messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,21 @@ enum MessageType {
MessageType_StellarPathPaymentStrictSendOp = 223 [(wire_in) = true];
reserved 224; // omitted: StellarCreateClaimableBalanceOp
MessageType_StellarClaimClaimableBalanceOp = 225 [(wire_in) = true];
reserved 226; // omitted: StellarBeginSponsoringFutureReservesOp
reserved 227; // omitted: StellarEndSponsoringFutureReservesOp
reserved 228; // omitted: StellarRevokeSponsorshipOp
reserved 229; // omitted: StellarClawbackOp
MessageType_StellarSignedTx = 230 [(wire_out) = true];
reserved 231; // omitted: StellarClawbackClaimableBalanceOp
reserved 232; // omitted: StellarSetTrustLineFlagsOp
reserved 233; // omitted: StellarLiquidityPoolDepositOp
reserved 234; // omitted: StellarLiquidityPoolWithdrawOp
MessageType_StellarInvokeHostFunctionOp = 235 [(wire_in) = true];
reserved 236; // omitted: StellarExtendFootprintTtl
reserved 237; // omitted: StellarRestoreFootprint
MessageType_StellarSignSorobanAuthorization = 238 [(wire_out) = true];
MessageType_StellarTxExtRequest = 239 [(wire_out) = true];
MessageType_StellarTxExt = 240 [(wire_in) = true];

// Cardano
// dropped Sign/VerifyMessage ids 300-302
Expand Down
12 changes: 12 additions & 0 deletions core/src/all_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,20 @@
import trezor.enums.NEMSupplyChangeType
trezor.enums.StellarAssetType
import trezor.enums.StellarAssetType
trezor.enums.StellarHostFunctionType
import trezor.enums.StellarHostFunctionType
trezor.enums.StellarMemoType
import trezor.enums.StellarMemoType
trezor.enums.StellarSCAddressType
import trezor.enums.StellarSCAddressType
trezor.enums.StellarSCValType
import trezor.enums.StellarSCValType
trezor.enums.StellarSignerType
import trezor.enums.StellarSignerType
trezor.enums.StellarSorobanAuthorizedFunctionType
import trezor.enums.StellarSorobanAuthorizedFunctionType
trezor.enums.StellarSorobanCredentialsType
import trezor.enums.StellarSorobanCredentialsType
trezor.enums.TezosBallotType
import trezor.enums.TezosBallotType
trezor.enums.TezosContractType
Expand Down Expand Up @@ -725,6 +735,8 @@
import apps.stellar.operations.layout
apps.stellar.operations.serialize
import apps.stellar.operations.serialize
apps.stellar.sign_soroban_auth
import apps.stellar.sign_soroban_auth
apps.stellar.sign_tx
import apps.stellar.sign_tx
apps.stellar.writers
Expand Down
3 changes: 3 additions & 0 deletions core/src/apps/stellar/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
StellarClaimClaimableBalanceOp,
StellarCreateAccountOp,
StellarCreatePassiveSellOfferOp,
StellarInvokeHostFunctionOp,
StellarManageBuyOfferOp,
StellarManageDataOp,
StellarManageSellOfferOp,
Expand All @@ -37,6 +38,7 @@
| StellarPaymentOp
| StellarSetOptionsOp
| StellarClaimClaimableBalanceOp
| StellarInvokeHostFunctionOp
)


Expand All @@ -59,6 +61,7 @@
MessageType.StellarPaymentOp: 1,
MessageType.StellarSetOptionsOp: 5,
MessageType.StellarClaimClaimableBalanceOp: 15,
MessageType.StellarInvokeHostFunctionOp: 24,
}


Expand Down
34 changes: 34 additions & 0 deletions core/src/apps/stellar/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ def address_from_public_key(pubkey: bytes) -> str:
return base32.encode(address)


def encode_contract(raw_key: bytes) -> str:
"""Returns the base32-encoded version of contract address bytes (C...)"""
address = bytearray()
address.append(2 << 3) # version -> 'C'
address.extend(raw_key)
address.extend(_crc16_checksum(bytes(address))) # checksum

return base32.encode(address)


def decode_contract(contract: str) -> bytes:
"""Extracts raw key from a contract address
Contract address is in format:
<1-byte version> <32-bytes contract key> <2-bytes CRC-16 checksum>
"""
from trezor.wire import ProcessError

b = base32.decode(contract)
# verify checksum - function deleted as it saved 50 bytes from the binary
if _crc16_checksum(b[:-2]) != b[-2:]:
raise ProcessError("Invalid address checksum")
return b[1:-2]


def _crc16_checksum(data: bytes) -> bytes:
"""Returns the CRC-16 checksum of bytearray bytes
Expand All @@ -46,3 +70,13 @@ def _crc16_checksum(data: bytes) -> bytes:
crc ^= polynomial

return ustruct.pack("<H", crc & 0xFFFF)


def bytes_to_int(data: bytes, signed: bool = False) -> int:
"""Converts bytes to int (big endian)"""
num = 0
for byte in data:
num = (num << 8) + byte
if signed and (data[0] & 0x80):
num -= 1 << (8 * len(data))
return num
Loading

0 comments on commit a7fa60a

Please sign in to comment.