// Copyright 2022 Hcnet Development Foundation and contributors. Licensed // under the Apache License, Version 2.0. See the COPYING file at the root // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 % #include "xdr/Hcnet-types.h" namespace hcnet { // We fix a maximum of 128 value types in the system for two reasons: we want to // keep the codes relatively small (<= 8 bits) when bit-packing values into a // u64 at the environment interface level, so that we keep many bits for // payloads (small strings, small numeric values, object handles); and then we // actually want to go one step further and ensure (for code-size) that our // codes fit in a single ULEB128-code byte, which means we can only use 7 bits. // // We also reserve several type codes from this space because we want to _reuse_ // the SCValType codes at the environment interface level (or at least not // exceed its number-space) but there are more types at that level, assigned to // optimizations/special case representations of values abstract at this level. enum SCValType { SCV_BOOL = 0, SCV_VOID = 1, SCV_ERROR = 2, // 32 bits is the smallest type in WASM or XDR; no need for u8/u16. SCV_U32 = 3, SCV_I32 = 4, // 64 bits is naturally supported by both WASM and XDR also. SCV_U64 = 5, SCV_I64 = 6, // Time-related u64 subtypes with their own functions and formatting. SCV_TIMEPOINT = 7, SCV_DURATION = 8, // 128 bits is naturally supported by Rust and we use it for Soroban // fixed-point arithmetic prices / balances / similar "quantities". These // are represented in XDR as a pair of 2 u64s. SCV_U128 = 9, SCV_I128 = 10, // 256 bits is the size of sha256 output, ed25519 keys, and the EVM machine // word, so for interop use we include this even though it requires a small // amount of Rust guest and/or host library code. SCV_U256 = 11, SCV_I256 = 12, // Bytes come in 3 flavors, 2 of which have meaningfully different // formatting and validity-checking / domain-restriction. SCV_BYTES = 13, SCV_STRING = 14, SCV_SYMBOL = 15, // Vecs and maps are just polymorphic containers of other ScVals. SCV_VEC = 16, SCV_MAP = 17, // Address is the universal identifier for contracts and classic // accounts. SCV_ADDRESS = 18, // The following are the internal SCVal variants that are not // exposed to the contracts. SCV_CONTRACT_INSTANCE = 19, // SCV_LEDGER_KEY_CONTRACT_INSTANCE and SCV_LEDGER_KEY_NONCE are unique // symbolic SCVals used as the key for ledger entries for a contract's // instance and an address' nonce, respectively. SCV_LEDGER_KEY_CONTRACT_INSTANCE = 20, SCV_LEDGER_KEY_NONCE = 21 }; enum SCErrorType { SCE_CONTRACT = 0, // Contract-specific, user-defined codes. SCE_WASM_VM = 1, // Errors while interpreting WASM bytecode. SCE_CONTEXT = 2, // Errors in the contract's host context. SCE_STORAGE = 3, // Errors accessing host storage. SCE_OBJECT = 4, // Errors working with host objects. SCE_CRYPTO = 5, // Errors in cryptographic operations. SCE_EVENTS = 6, // Errors while emitting events. SCE_BUDGET = 7, // Errors relating to budget limits. SCE_VALUE = 8, // Errors working with host values or SCVals. SCE_AUTH = 9 // Errors from the authentication subsystem. }; enum SCErrorCode { SCEC_ARITH_DOMAIN = 0, // Some arithmetic was undefined (overflow, divide-by-zero). SCEC_INDEX_BOUNDS = 1, // Something was indexed beyond its bounds. SCEC_INVALID_INPUT = 2, // User provided some otherwise-bad data. SCEC_MISSING_VALUE = 3, // Some value was required but not provided. SCEC_EXISTING_VALUE = 4, // Some value was provided where not allowed. SCEC_EXCEEDED_LIMIT = 5, // Some arbitrary limit -- gas or otherwise -- was hit. SCEC_INVALID_ACTION = 6, // Data was valid but action requested was not. SCEC_INTERNAL_ERROR = 7, // The host detected an error in its own logic. SCEC_UNEXPECTED_TYPE = 8, // Some type wasn't as expected. SCEC_UNEXPECTED_SIZE = 9 // Something's size wasn't as expected. }; // Smart contract errors are split into a type (SCErrorType) and a code. When an // error is of type SCE_CONTRACT it carries a user-defined uint32 code that // Soroban assigns no specific meaning to. In all other cases, the type // specifies a subsystem of the Soroban host where the error originated, and the // accompanying code is an SCErrorCode, each of which specifies a slightly more // precise class of errors within that subsystem. // // Error types and codes are not maximally precise; there is a tradeoff between // precision and flexibility in the implementation, and the granularity here is // chosen to be adequate for most purposes while not placing a burden on future // system evolution and maintenance. When additional precision is needed for // debugging, Soroban can be run with diagnostic events enabled. union SCError switch (SCErrorType type) { case SCE_CONTRACT: uint32 contractCode; case SCE_WASM_VM: case SCE_CONTEXT: case SCE_STORAGE: case SCE_OBJECT: case SCE_CRYPTO: case SCE_EVENTS: case SCE_BUDGET: case SCE_VALUE: case SCE_AUTH: SCErrorCode code; }; struct UInt128Parts { uint64 hi; uint64 lo; }; // A signed int128 has a high sign bit and 127 value bits. We break it into a // signed high int64 (that carries the sign bit and the high 63 value bits) and // a low unsigned uint64 that carries the low 64 bits. This will sort in // generated code in the same order the underlying int128 sorts. struct Int128Parts { int64 hi; uint64 lo; }; struct UInt256Parts { uint64 hi_hi; uint64 hi_lo; uint64 lo_hi; uint64 lo_lo; }; // A signed int256 has a high sign bit and 255 value bits. We break it into a // signed high int64 (that carries the sign bit and the high 63 value bits) and // three low unsigned `uint64`s that carry the lower bits. This will sort in // generated code in the same order the underlying int256 sorts. struct Int256Parts { int64 hi_hi; uint64 hi_lo; uint64 lo_hi; uint64 lo_lo; }; enum ContractExecutableType { CONTRACT_EXECUTABLE_WASM = 0, CONTRACT_EXECUTABLE_HCNET_ASSET = 1 }; union ContractExecutable switch (ContractExecutableType type) { case CONTRACT_EXECUTABLE_WASM: Hash wasm_hash; case CONTRACT_EXECUTABLE_HCNET_ASSET: void; }; enum SCAddressType { SC_ADDRESS_TYPE_ACCOUNT = 0, SC_ADDRESS_TYPE_CONTRACT = 1 }; union SCAddress switch (SCAddressType type) { case SC_ADDRESS_TYPE_ACCOUNT: AccountID accountId; case SC_ADDRESS_TYPE_CONTRACT: Hash contractId; }; %struct SCVal; %struct SCMapEntry; const SCSYMBOL_LIMIT = 32; typedef SCVal SCVec<>; typedef SCMapEntry SCMap<>; typedef opaque SCBytes<>; typedef string SCString<>; typedef string SCSymbol; struct SCNonceKey { int64 nonce; }; struct SCContractInstance { ContractExecutable executable; SCMap* storage; }; union SCVal switch (SCValType type) { case SCV_BOOL: bool b; case SCV_VOID: void; case SCV_ERROR: SCError error; case SCV_U32: uint32 u32; case SCV_I32: int32 i32; case SCV_U64: uint64 u64; case SCV_I64: int64 i64; case SCV_TIMEPOINT: TimePoint timepoint; case SCV_DURATION: Duration duration; case SCV_U128: UInt128Parts u128; case SCV_I128: Int128Parts i128; case SCV_U256: UInt256Parts u256; case SCV_I256: Int256Parts i256; case SCV_BYTES: SCBytes bytes; case SCV_STRING: SCString str; case SCV_SYMBOL: SCSymbol sym; // Vec and Map are recursive so need to live // behind an option, due to xdrpp limitations. case SCV_VEC: SCVec *vec; case SCV_MAP: SCMap *map; case SCV_ADDRESS: SCAddress address; // Special SCVals reserved for system-constructed contract-data // ledger keys, not generally usable elsewhere. case SCV_LEDGER_KEY_CONTRACT_INSTANCE: void; case SCV_LEDGER_KEY_NONCE: SCNonceKey nonce_key; case SCV_CONTRACT_INSTANCE: SCContractInstance instance; }; struct SCMapEntry { SCVal key; SCVal val; }; }