diff --git a/.github/release.yml b/.github/release.yml index 481646c565..7c2ce51c56 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,8 +1,12 @@ changelog: categories: - - title: 💥 Breaking Changes + - title: 💥 Language Breaking Changes labels: - - Breaking Change + - Language Breaking Change + - Storage Breaking Change + - title: 💥 Go API Breaking Chance + labels: + - Go API Breaking Change - title: ⭐ Features labels: - Feature diff --git a/docs/language/accounts.mdx b/docs/language/accounts.mdx index b70c84a58b..f5c45963ce 100644 --- a/docs/language/accounts.mdx +++ b/docs/language/accounts.mdx @@ -10,57 +10,85 @@ Every account can be accessed through two types, `PublicAccount` and `AuthAccoun which represents the publicly available portion of an account. ```cadence -struct PublicAccount { +pub struct PublicAccount { - let address: Address - // The FLOW balance of the default vault of this account - let balance: UFix64 - // The FLOW balance of the default vault of this account that is available to be moved - let availableBalance: UFix64 - // Amount of storage used by the account, in bytes - let storageUsed: UInt64 - // storage capacity of the account, in bytes - let storageCapacity: UInt64 + /// The address of the account. + pub let address: Address - // Contracts deployed to the account - let contracts: PublicAccount.Contracts + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 - // Keys assigned to the account - let keys: PublicAccount.Keys + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 - // The public paths associated with this account - let publicPaths: [PublicPath] + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 - // Storage operations + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 - fun getCapability(_ path: PublicPath): Capability - fun getLinkTarget(_ path: CapabilityPath): Path? + /// The contracts deployed to the account. + pub let contracts: PublicAccount.Contracts - // Storage iteration - fun forEachPublic(_ function: fun(PublicPath, Type): Bool) + /// The keys assigned to the account. + pub let keys: PublicAccount.Keys - struct Contracts { + /// All public paths of this account. + pub let publicPaths: [PublicPath] - let names: [String] + /// Returns the capability at the given public path. + pub fun getCapability(_ path: PublicPath): Capability - fun get(name: String): DeployedContract? + /// Returns the target path of the capability at the given public or private path, + /// or nil if there exists no capability at the given path. + pub fun getLinkTarget(_ path: CapabilityPath): Path? - fun borrow(name: String): T? + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: fun(PublicPath, Type): Bool) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? } - struct Keys { - // Returns the key at the given index, if it exists. - // Revoked keys are always returned, but they have \`isRevoked\` field set to true. - fun get(keyIndex: Int): AccountKey? + pub struct Keys { + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? - // Iterate over all unrevoked keys in this account, - // passing each key in turn to the provided function. - // Iteration is stopped early if the function returns `false`. - // The order of iteration is undefined. - fun forEach(function: fun(AccountKey): Bool): Void + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: fun(AccountKey): Bool) - // The total number of unrevoked keys in this account. - let count: UInt64 + /// The total number of unrevoked keys in this account. + pub let count: UInt64 } } ``` @@ -84,154 +112,309 @@ Only [signed transactions](transactions) can get the `AuthAccount` for an accoun For each signer of the transaction that signs as an authorizer, the corresponding `AuthAccount` object is passed to the `prepare` phase of the transaction. - ```cadence - struct AuthAccount { - - let address: Address - // The FLOW balance of the default vault of this account - let balance: UFix64 - // The FLOW balance of the default vault of this account that is available to be moved - let availableBalance: UFix64 - // Amount of storage used by the account, in bytes - let storageUsed: UInt64 - // storage capacity of the account, in bytes - let storageCapacity: UInt64 - - // Contracts deployed to the account - - let contracts: AuthAccount.Contracts - - // Key management API - - let keys: AuthAccount.Keys - - // Provides a API for bootstrapping (sending and receiving) capabilities - - let inbox: AuthAccount.Inbox - - // All the paths associated with this account - let publicPaths: [PublicPath] - let privatePaths: [PrivatePath] - let storagePaths: [StoragePath] - - // Account storage API (see the section below for documentation) - - fun save(_ value: T, to: StoragePath) - fun type(at path: StoragePath): Type? - fun load(from: StoragePath): T? - fun copy(from: StoragePath): T? - - fun borrow(from: StoragePath): T? - - fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? - fun linkAccount(_ newCapabilityPath: PrivatePath): Capability<&AuthAccount>? - fun getCapability(_ path: CapabilityPath): Capability - fun getLinkTarget(_ path: CapabilityPath): Path? - fun unlink(_ path: CapabilityPath) - - // All the paths associated with this account - - let publicPaths: [PublicPath] - let privatePaths: [PrivatePath] - let storagePaths: [StoragePath] - - // Storage iteration - - fun forEachPublic(_ function: fun(PublicPath, Type): Bool) - fun forEachPrivate(_ function: fun(PrivatePath, Type): Bool) - fun forEachStored(_ function: fun(StoragePath, Type): Bool) - - struct Contracts { - - // The names of each contract deployed to the account - let names: [String] - - fun add( - name: String, - code: [UInt8], - ... contractInitializerArguments - ): DeployedContract - - fun update__experimental(name: String, code: [UInt8]): DeployedContract - - fun get(name: String): DeployedContract? - - fun remove(name: String): DeployedContract? - - fun borrow(name: String): T? - } - - struct Keys { - // Adds a new key with the given hashing algorithm and a weight, and returns the added key. - fun add( - publicKey: PublicKey, - hashAlgorithm: HashAlgorithm, - weight: UFix64 - ): AccountKey - - // Returns the key at the given index, if it exists, or nil otherwise. - // Revoked keys are always returned, but they have `isRevoked` field set to true. - fun get(keyIndex: Int): AccountKey? - - // Marks the key at the given index revoked, but does not delete it. - // Returns the revoked key if it exists, or nil otherwise. - fun revoke(keyIndex: Int): AccountKey? - - // Iterate over all unrevoked keys in this account, - // passing each key in turn to the provided function. - // Iteration is stopped early if the function returns `false`. - // The order of iteration is undefined. - fun forEach(function: fun(AccountKey): Bool): Void - - // The total number of unrevoked keys in this account. - let count: UInt64 - } - - struct Inbox { - // Publishes a new Capability under the given name, to be claimed by the specified recipient - fun publish(_ value: Capability, name: String, recipient: Address) +```cadence +pub struct AuthAccount { + + /// The address of the account. + pub let address: Address + + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 + + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 + + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 + + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 + + /// The contracts deployed to the account. + pub let contracts: AuthAccount.Contracts + + /// The keys assigned to the account. + pub let keys: AuthAccount.Keys + + /// The inbox allows bootstrapping (sending and receiving) capabilities. + pub let inbox: AuthAccount.Inbox + + /// All public paths of this account. + pub let publicPaths: [PublicPath] + + /// All private paths of this account. + pub let privatePaths: [PrivatePath] + + /// All storage paths of this account. + pub let storagePaths: [StoragePath] + + // Account storage API (see the section below for documentation) + + // Provides a API for bootstrapping (sending and receiving) capabilities + + let inbox: AuthAccount.Inbox + + /// Saves the given object into the account's storage at the given path. + /// + /// Resources are moved into storage, and structures are copied. + /// + /// If there is already an object stored under the given path, the program aborts. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun save(_ value: T, to: StoragePath) + + /// Reads the type of an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, the type of the object is returned without modifying the stored object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun type(at: StoragePath): Type? + + /// Loads an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, + /// the stored resource or structure is moved out of storage and returned as an optional. + /// + /// When the function returns, the storage no longer contains an object under the given path. + /// + /// The given type must be a supertype of the type of the loaded object. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the loaded object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun load(from: StoragePath): T? + + /// Returns a copy of a structure stored in account storage under the given path, + /// without removing it from storage, + /// or nil if no object is stored under the given path. + /// + /// If there is a structure stored, it is copied. + /// The structure stays stored in storage after the function returns. + /// + /// The given type must be a supertype of the type of the copied structure. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the copied structure. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun copy(from: StoragePath): T? + + /// Returns a reference to an object in storage without removing it from storage. + /// + /// If no object is stored under the given path, the function returns nil. + /// If there is an object stored, a reference is returned as an optional, + /// provided it can be borrowed using the given type. + /// If the stored object cannot be borrowed using the given type, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the borrowed object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed + pub fun borrow(from: StoragePath): T? + + /// Creates a capability at the given public or private path, + /// which targets the given public, private, or storage path. + /// + /// The target path leads to the object that will provide the functionality defined by this capability. + /// + /// The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. + /// + /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. + /// + /// It is not necessary for the target path to lead to a valid object; the target path could be empty, + /// or could lead to an object which does not provide the necessary type interface: + /// The link function does **not** check if the target path is valid/exists at the time the capability is created + /// and does **not** check if the target value conforms to the given type. + /// + /// The link is latent. + /// + /// The target value might be stored after the link is created, + /// and the target value might be moved out after the link has been created. + pub fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? + + /// Creates a capability at the given public or private path which targets this account. + /// + /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. + pub fun linkAccount(_ newCapabilityPath: PrivatePath): Capability<&AuthAccount>? + + /// Returns the capability at the given private or public path. + pub fun getCapability(_ path: CapabilityPath): Capability + + /// Returns the target path of the capability at the given public or private path, + /// or nil if there exists no capability at the given path. + pub fun getLinkTarget(_ path: CapabilityPath): Path? + + /// Removes the capability at the given public or private path. + pub fun unlink(_ path: CapabilityPath) + + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: ((PublicPath, Type): Bool)) + + /// Iterate over all the private paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPrivate(_ function: ((PrivatePath, Type): Bool)) + + /// Iterate over all the stored paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachStored(_ function: ((StoragePath, Type): Bool)) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Adds the given contract to the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// All additional arguments that are given are passed further to the initializer + /// of the contract that is being deployed. + /// + /// The function fails if a contract/contract interface with the given name already exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract. + pub fun add( + name: String, + code: [UInt8] + ): DeployedContract + + /// **Experimental** + /// + /// Updates the code for the contract/contract interface in the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// Does **not** run the initializer of the contract/contract interface again. + /// The contract instance in the world state stays as is. + /// + /// Fails if no contract/contract interface with the given name exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract for the updated contract. + pub fun update__experimental(name: String, code: [UInt8]): DeployedContract + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Removes the contract/contract interface from the account which has the given name, if any. + /// + /// Returns the removed deployed contract, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun remove(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? + } - // Unpublishes a Capability previously published by this account. - // Returns `nil` if no Capability is published under the given name. - // Errors if the Capability under that name does not match the provided type. - fun unpublish(_ name: String): Capability? + pub struct Keys { + + /// Adds a new key with the given hashing algorithm and a weight. + /// + /// Returns the added key. + pub fun add( + publicKey: PublicKey, + hashAlgorithm: HashAlgorithm, + weight: UFix64 + ): AccountKey + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? + + /// Marks the key at the given index revoked, but does not delete it. + /// + /// Returns the revoked key if it exists, or nil otherwise. + pub fun revoke(keyIndex: Int): AccountKey? + + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: fun(AccountKey): Bool) + + /// The total number of unrevoked keys in this account. + pub let count: UInt64 + } - // Claims a Capability previously published by the specified provider. - // Returns `nil` if no Capability is published under the given name, or if this account is not its intended recipient. - // Errors if the Capability under that name does not match the provided type. - fun claim(_ name: String, provider: Address): Capability? + pub struct Inbox { + + /// Publishes a new Capability under the given name, + /// to be claimed by the specified recipient. + pub fun publish(_ value: Capability, name: String, recipient: Address) + + /// Unpublishes a Capability previously published by this account. + /// + /// Returns `nil` if no Capability is published under the given name. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun unpublish(_ name: String): Capability? + + /// Claims a Capability previously published by the specified provider. + /// + /// Returns `nil` if no Capability is published under the given name, + /// or if this account is not its intended recipient. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun claim(_ name: String, provider: Address): Capability? } - } - - struct DeployedContract { - let name: String - let code: [UInt8] - - // Returns an array of `Type` objects representing all the public type declarations in this contract (e.g. structs, resources, enums) - // - // For example, given a contract - // ``` - // contract Foo { - // pub struct Bar {...} - // pub resource Qux {...} - // } - // ``` - // then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` - - fun publicTypes(): [Type] - } - ``` +} +``` - A script can get the `AuthAccount` for an account address using the built-in `getAuthAccount` function: +A script can get the `AuthAccount` for an account address using the built-in `getAuthAccount` function: - ```cadence - fun getAuthAccount(_ address: Address): AuthAccount - ``` +```cadence +fun getAuthAccount(_ address: Address): AuthAccount +``` - This `AuthAccount` object can perform all operations associated with authorized accounts, - and as such this function is only available in scripts, - which discard their changes upon completion. - Attempting to use this function outside of a script will cause a type error. +This `AuthAccount` object can perform all operations associated with authorized accounts, +and as such this function is only available in scripts, +which discard their changes upon completion. +Attempting to use this function outside of a script will cause a type error. ## Account Creation @@ -484,7 +667,7 @@ let path = PublicPath(identifier: pathID) // is /public/foo Account storage is accessed through the following functions of `AuthAccount`. This means that any code that has access to the authorized account has access to all its stored objects. - + ```cadence fun save(_ value: T, to: StoragePath) ``` @@ -501,7 +684,7 @@ The path must be a storage path, i.e., only the domain `storage` is allowed. ```cadence -fun type(at path: StoragePath): Type? +fun type(at: StoragePath): Type? ``` Reads the type of an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path. diff --git a/docs/language/contracts.mdx b/docs/language/contracts.mdx index 32977536fe..55cbbc5481 100644 --- a/docs/language/contracts.mdx +++ b/docs/language/contracts.mdx @@ -237,9 +237,28 @@ The deployed contracts of an account can be accessed through the `contracts` obj Accounts store "deployed contracts", that is, the code of the contract: ```cadence -struct DeployedContract { - let name: String - let code: [UInt8] +pub struct DeployedContract { + /// The address of the account where the contract is deployed at. + pub let address: Address + + /// The name of the contract. + pub let name: String + + /// The code of the contract. + pub let code: [UInt8] + + /// Returns an array of `Type` objects representing all the public type declarations in this contract + /// (e.g. structs, resources, enums). + /// + /// For example, given a contract + /// ``` + /// contract Foo { + /// pub struct Bar {...} + /// pub resource Qux {...} + /// } + /// ``` + /// then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` + pub fun publicTypes(): [Type] } ``` diff --git a/docs/language/crypto.mdx b/docs/language/crypto.mdx index 13a7a75c1f..b48fc1370f 100644 --- a/docs/language/crypto.mdx +++ b/docs/language/crypto.mdx @@ -157,7 +157,7 @@ The raw key value depends on the supported signature scheme: The raw public key is 64-bytes long. - `BLS_BLS_12_381`: - The public key is a G_2 (curve over the prime field extension) element. + The public key is a G_2 element (on the curve over the prime field extension). The encoding follows the compressed serialization defined in the [IETF draft-irtf-cfrg-pairing-friendly-curves-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-point-serialization-procedu). A public key is 96-bytes long. @@ -173,9 +173,16 @@ The validation of the public key depends on the supported signature scheme: - `BLS_BLS_12_381`: The given key is correctly serialized following the compressed serialization in [IETF draft-irtf-cfrg-pairing-friendly-curves-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-point-serialization-procedu). - The coordinates represent valid prime field extension elemnents. The resulting point is on the curve, and is on the correct subgroup G_2. + The coordinates represent valid prime field extension elements. The resulting point is on the curve, and is on the correct subgroup G_2. + Note that the point at infinity is accepted and yields the identity public key. Such identity key can be useful when aggregating multiple keys. -Since the validation happen only at the time of creation, public keys are immutable. + + +🚧 Status: Accepting the BLS identity key is going to be available in the upcoming release of Cadence on Mainnet. + + + +Since the validation happens only at the time of creation, public keys are immutable. ```cadence publicKey.signatureAlgorithm = SignatureAlgorithm.ECDSA_secp256k1 // Not allowed @@ -225,15 +232,22 @@ ECDSA verification is implemented as defined in ANS X9.62 (also referred by [FIP A valid signature would be generated using the expected `signedData`, `domainSeparationTag` and `hashAlgorithm` used to verify. - BLS (`BLS_BLS_12_381`): - - `signature` expects a G_1 (subgroup of the curve over the prime field) point. + - `signature` expects a G_1 point (on the curve over the prime field). The encoding follows the compressed serialization defined in the [IETF draft-irtf-cfrg-pairing-friendly-curves-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-point-serialization-procedu). A signature is 48-bytes long. - `signedData` is the arbitrary message to verify the signature against. - `domainSeparationTag` is the expected domain tag. All tags are accepted (check [KMAC128 for BLS](#KMAC128-for-BLS)). - `hashAlgorithm` only accepts `KMAC128_BLS_BLS12_381`. It is the algorithm used to hash the message along with the given tag (check [KMAC128 for BLS](#KMAC128-for-BLS)). -BLS verification performs the necessary membership check of the signature while the membership check of the public key is performed at the creation of the `PublicKey` object -and not repeated during the signature verification. +BLS verification performs the necessary membership check on the signature while the membership check of the public key is performed at the creation of the `PublicKey` object +and is not repeated during the signature verification. In order to prevent equivocation issues, a verification under the identity public key always returns `false`. + + + +🚧 Status: Returning `false` when verifying against a BLS identity key is going to be available in the upcoming release of Cadence on Mainnet. +Currently, BLS identity keys can only be constructed as an output of `aggregatePublicKeys`. + + The verificaction uses a hash-to-curve algorithm to hash the `signedData` into a `G_1` point, following the `hash_to_curve` method described in the [draft-irtf-cfrg-hash-to-curve-14](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-3). While KMAC128 is used as a hash-to-field method resulting in two field elements, the mapping to curve is implemented using the [simplified SWU](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-6.6.3). @@ -252,7 +266,7 @@ These tools are defined in the built-in `BLS` contract, which does not need to b ### Proof of Possession (PoP) Multi-signature verification in BLS requires a defense against rogue public-key attacks. Multiple ways are -available to protect BLS verification. The language provides the proof of possession of private key as a defense tool. +available to protect BLS verification. Cadence provides the proof of possession of private key as a defense tool. The proof of possession of private key is a BLS signature over the public key itself. The PoP signature follows the same requirements of a BLS signature (detailed in [Signature verification](#Signature-verification)), except it uses a special domain separation tag. The key expected to be used in KMAC128 is the UTF-8 encoding of `"BLS_POP_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"`. @@ -270,7 +284,7 @@ Signatures could be generated from the same or distinct messages, they could also be the aggregation of other signatures. The order of the signatures in the slice does not matter since the aggregation is commutative. There is no subgroup membership check performed on the input signatures. -If the array is empty or if decoding one of the signature fails, the program aborts +If the array is empty or if decoding one of the signatures fails, the program aborts. The output signature can be verified against an aggregated public key to authenticate multiple signers at once. Since the `verify` method accepts a single data to verify against, it is only possible to @@ -287,11 +301,18 @@ Aggregates multiple BLS public keys into one. The order of the public keys in the slice does not matter since the aggregation is commutative. The input keys are guaranteed to be in the correct subgroup since subgroup membership is checked at the key creation time. -If the array is empty or any of the input keys is not a BLS key, the program aborts +If the array is empty or any of the input keys is not a BLS key, the program aborts. +Note that the identity public key is a valid input to this function and it represents the +identity element of aggregation. The output public key can be used to verify aggregated signatures to authenticate multiple signers at once. Since the `verify` method accepts a single data to verify against, it is only possible to verfiy multiple signatures of the same message. +The identity public key is a possible output of the function, though signature verifications +against identity result in `false`. + +In order to prevent rogue key attacks when verifying aggregated signatures, it is important to verfiy the +PoP of each individual key involved in the aggregation process. ## Crypto Contract diff --git a/docs/language/environment-information.md b/docs/language/environment-information.md index 964f0ecdb9..61db4f71f5 100644 --- a/docs/language/environment-information.md +++ b/docs/language/environment-information.md @@ -24,7 +24,7 @@ To get information about a block, the functions `getCurrentBlock` and `getBlock` - ```cadence - fun getBlock(at height: UInt64): Block? + fun getBlock(at: UInt64): Block? ``` Returns the block at the given height. diff --git a/docs/language/operators.md b/docs/language/operators.md index 081d4a0b80..2949ee2eed 100644 --- a/docs/language/operators.md +++ b/docs/language/operators.md @@ -290,7 +290,7 @@ Logical operators work with the boolean values `true` and `false`. Comparison operators work with boolean and integer values. -- Equality: `==`, is supported for booleans, numbers, addresses, strings, characters, enums, paths, `Type` values, references, and `Void` values (`()`). Variable-sized arrays, fixed-size arrays, and optionals also support equality tests if their inner types do. +- Equality: `==`, is supported for booleans, numbers, addresses, strings, characters, enums, paths, `Type` values, references, and `Void` values (`()`). Variable-sized arrays, fixed-size arrays, dictionaries, and optionals also support equality tests if their inner types do. Both sides of the equality operator may be optional, even of different levels, so it is for example possible to compare a non-optional with a double-optional (`??`). @@ -349,8 +349,19 @@ Comparison operators work with boolean and integer values. xs == ys // is `true` ``` + ```cadence + // Equality tests of dictionaries are possible if the key and value types are equatable. + let d1 = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + d1 == d2 // is `true` + + let d3 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d4 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + d3 == d4 // is `true` + ``` + - Inequality: `!=`, is supported for booleans, numbers, addresses, strings, characters, enums, paths, `Type` values, references, and `Void` values (`()`). - Variable-sized arrays, fixed-size arrays, and optionals also support inequality tests if their inner types do. + Variable-sized arrays, fixed-size arrays, dictionaries, and optionals also support inequality tests if their inner types do. Both sides of the inequality operator may be optional, even of different levels, so it is for example possible to compare a non-optional with a double-optional (`??`). @@ -405,6 +416,17 @@ Comparison operators work with boolean and integer values. xs != ys // is `false` ``` + ```cadence + // Inequality tests of dictionaries are possible if the key and value types are equatable. + let d1 = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 500} + d1 != d2 // is `true` + + let d3 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d4 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + d3 != d4 // is `false` + ``` + - Less than: `<`, for integers ```cadence diff --git a/docs/language/run-time-types.md b/docs/language/run-time-types.md index 45fca64450..9476977b1c 100644 --- a/docs/language/run-time-types.md +++ b/docs/language/run-time-types.md @@ -32,7 +32,7 @@ Type() == Type() Type() != Type() ``` -The method `fun isSubtype(of otherType: Type): Bool` can be used to compare the run-time types of values. +The method `fun isSubtype(of: Type): Bool` can be used to compare the run-time types of values. ```cadence Type().isSubtype(of: Type()) // true diff --git a/docs/language/values-and-types.mdx b/docs/language/values-and-types.mdx index ac00650b3a..8b8e4f3d35 100644 --- a/docs/language/values-and-types.mdx +++ b/docs/language/values-and-types.mdx @@ -1180,7 +1180,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - fun insert(at index: Int, _ element: T): Void + fun insert(at: Int, _ element: T): Void ``` Inserts the new element `element` of type `T` @@ -1211,7 +1211,7 @@ It is invalid to use one of these functions on a fixed-sized array. ``` - ```cadence - fun remove(at index: Int): T + fun remove(at: Int): T ``` Removes the element at the given `index` from the array and returns it. diff --git a/encoding/ccf/bench_test.go b/encoding/ccf/bench_test.go new file mode 100644 index 0000000000..37a81dc72f --- /dev/null +++ b/encoding/ccf/bench_test.go @@ -0,0 +1,211 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/cadence/encoding/json" +) + +var encoded []byte +var val cadence.Value + +var benchmarks = []struct { + name string + value cadence.Value +}{ + {name: "FlowFees.FeesDeducted", value: createFlowFeesFeesDeductedEvent()}, + {name: "FlowFees.TokensWithdrawn", value: createFlowFeesTokensWithdrawnEvent()}, + + {name: "FlowIDTableStaking.DelegatorRewardsPaid", value: createFlowIDTableStakingDelegatorRewardsPaidEvent()}, + {name: "FlowIDTableStaking.EpochTotalRewardsPaid", value: createFlowIDTableStakingEpochTotalRewardsPaidEvent()}, + {name: "FlowIDTableStaking.NewWeeklyPayout", value: createFlowIDTableStakingNewWeeklyPayoutEvent()}, + {name: "FlowIDTableStaking.RewardsPaid", value: createFlowIDTableStakingRewardsPaidEvent()}, + + {name: "FlowToken.TokensDeposited with nil receiver", value: createFlowTokenTokensDepositedEventNoReceiver()}, + {name: "FlowToken.TokensDeposited", value: createFlowTokenTokensDepositedEvent()}, + {name: "FlowToken.TokensMinted", value: createFlowTokenTokensMintedEvent()}, + {name: "FlowToken.TokensWithdrawn", value: createFlowTokenTokensWithdrawnEvent()}, +} + +// Events for transaction 03aa46047cdadfcf7ee23ee86cd53064e05f8b5f8a6f570e9f53b2744eddbee4. +// This transaction was selected from mainnet because of its large number of events (48309). +// - The number of events for each type are real. +// - The types of events are real. +// - To simplify benchmark code, all event values for each event type are the same +// (i.e. the values are from the first event of that event type). +var batchBenchmarks = []struct { + count int + value cadence.Value +}{ + {count: 16102, value: createFlowTokenTokensDepositedEvent()}, + {count: 1, value: createFlowTokenTokensMintedEvent()}, + {count: 16102, value: createFlowTokenTokensWithdrawnEvent()}, + {count: 15783, value: createFlowIDTableStakingDelegatorRewardsPaidEvent()}, + {count: 1, value: createFlowIDTableStakingEpochTotalRewardsPaidEvent()}, + {count: 1, value: createFlowIDTableStakingNewWeeklyPayoutEvent()}, + {count: 317, value: createFlowIDTableStakingRewardsPaidEvent()}, + {count: 1, value: createFlowFeesFeesDeductedEvent()}, + {count: 1, value: createFlowFeesTokensWithdrawnEvent()}, +} + +func BenchmarkEncodeJSON(b *testing.B) { + var err error + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + encoded, err = json.Encode(bm.value) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkDecodeJSON(b *testing.B) { + for _, bm := range benchmarks { + encoded, err := json.Encode(bm.value) + require.NoError(b, err) + + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + val, err = json.Decode(nil, encoded) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkEncodeCCF(b *testing.B) { + var err error + + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + encoded, err = ccf.Encode(bm.value) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkDecodeCCF(b *testing.B) { + for _, bm := range benchmarks { + encoded, err := ccf.Encode(bm.value) + require.NoError(b, err) + + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + val, err = ccf.Decode(nil, encoded) + } + require.NoError(b, err) + }) + } +} + +func BenchmarkEncodeBatchEventsJSON(b *testing.B) { + var err error + var encodedSize int + + for i := 0; i < b.N; i++ { + encodedSize = 0 + for _, bm := range batchBenchmarks { + for i := 0; i < bm.count; i++ { + encoded, err = json.Encode(bm.value) + encodedSize += len(encoded) + } + } + } + require.NoError(b, err) +} + +func BenchmarkDecodeBatchEventsJSON(b *testing.B) { + type encodedBatchEvent struct { + count int + encoded []byte + } + + benchmarks := make([]encodedBatchEvent, len(batchBenchmarks)) + + for i, bm := range batchBenchmarks { + benchmarks[i] = encodedBatchEvent{ + count: bm.count, + encoded: json.MustEncode(bm.value), + } + } + + var err error + for i := 0; i < b.N; i++ { + for _, bm := range benchmarks { + for i := 0; i < bm.count; i++ { + val, err = json.Decode(nil, bm.encoded) + } + } + } + + require.NoError(b, err) +} + +func BenchmarkEncodeBatchEventsCCF(b *testing.B) { + var err error + var encodedSize int + + for i := 0; i < b.N; i++ { + encodedSize = 0 + for _, bm := range batchBenchmarks { + for i := 0; i < bm.count; i++ { + encoded, err = ccf.Encode(bm.value) + encodedSize += len(encoded) + } + } + } + require.NoError(b, err) +} + +func BenchmarkDecodeBatchEventsCCF(b *testing.B) { + type encodedBatchEvent struct { + count int + encoded []byte + } + + benchmarks := make([]encodedBatchEvent, len(batchBenchmarks)) + + for i, bm := range batchBenchmarks { + benchmarks[i] = encodedBatchEvent{ + count: bm.count, + encoded: ccf.MustEncode(bm.value), + } + } + + var err error + for i := 0; i < b.N; i++ { + for _, bm := range benchmarks { + for i := 0; i < bm.count; i++ { + val, err = ccf.Decode(nil, bm.encoded) + } + } + } + + require.NoError(b, err) +} diff --git a/encoding/ccf/ccf.go b/encoding/ccf/ccf.go new file mode 100644 index 0000000000..2f0f54d177 --- /dev/null +++ b/encoding/ccf/ccf.go @@ -0,0 +1,20 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package ccf implements CCF specification +package ccf diff --git a/encoding/ccf/ccf_test.go b/encoding/ccf/ccf_test.go new file mode 100644 index 0000000000..1855fe52a6 --- /dev/null +++ b/encoding/ccf/ccf_test.go @@ -0,0 +1,13641 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf_test + +import ( + "bytes" + "encoding/hex" + "fmt" + "math" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/cadence/runtime" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/tests/checker" + "github.com/onflow/cadence/runtime/tests/utils" +) + +type encodeTest struct { + name string + val cadence.Value + expected []byte + expectedVal cadence.Value +} + +func TestEncodeVoid(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.NewVoid(), + []byte{ // language=json, format=json-cdc + // {"type":"Void"} + // + // language=edn, format=ccf + // 130([137(50), null]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // void type ID (50) + 0x18, 0x32, + // nil + 0xf6, + }, + ) +} + +func TestEncodeOptional(t *testing.T) { + + t.Parallel() + + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, + } + + structType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.NewOptionalType(cadence.NewIntType()), + }, + { + Identifier: "b", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewIntType())), + }, + { + Identifier: "c", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewIntType()))), + }, + }, + } + + structTypeWithOptionalAbstractField := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.NewOptionalType(cadence.NewAnyStructType()), + }, + { + Identifier: "b", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewAnyStructType())), + }, + { + Identifier: "c", + Type: cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewOptionalType(cadence.NewAnyStructType()))), + }, + }, + } + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Optional(nil)", + val: cadence.NewOptional(nil), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Optional","value":null} + // + // language=edn, format=ccf + // 130([138(137(42)), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // never type ID (42) + 0x18, 0x2a, + // nil + 0xf6, + }, + }, + { + name: "Optional(Int)", + val: cadence.NewOptional(cadence.NewInt(42)), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Optional","value":{"type":"Int","value":"42"}} + // + // language=edn, format=ccf + // 130([138(137(4)), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + name: "Optional(Optional(nil))", + val: cadence.NewOptional(cadence.NewOptional(nil)), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"value":null,"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(137(42))), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // never type ID (42) + 0x18, 0x2a, + // nil + 0xf6, + }, + }, + { + name: "Optional(Optional(Int))", + val: cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42))), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(137(4))), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + name: "Optional(Optional(Optional(nil)))", + val: cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(138(137(42)))), null]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // never type ID (42) + 0x18, 0x2a, + // nil + 0xf6, + }, + }, + { + name: "Optional(Optional(Optional(int)))", + val: cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(42)))), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"value":{"value":{"value":"42","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"} + // + // language=edn, format=ccf + // 130([138(138(138(137(4)))), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + name: "struct with nil optional fields", + val: cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(nil), + cadence.NewOptional(cadence.NewOptional(nil)), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + }).WithType(structType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":null,"type":"Optional"},"name":"a"},{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(4))], ["b", 138(138(137(4)))], ["c", 138(138(138(137(4))))]]])], [136(h''), [null, null, null]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", OptionalType(IntType)], ["b", OptionalType(OptionalType(IntType))], ["c", OptionalType(OptionalType(OptionalType(IntType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // nil + 0xf6, + // nil + 0xf6, + // nil + 0xf6, + }, + }, + { + name: "struct with non-nil optional fields", + val: cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(cadence.NewInt(1)), + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(3)))), + }).WithType(structType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":"3","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(4))], ["b", 138(138(137(4)))], ["c", 138(138(138(137(4))))]]])], [136(h''), [1, 2, 3]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", OptionalType(IntType)], ["b", OptionalType(OptionalType(IntType))], ["c", OptionalType(OptionalType(OptionalType(IntType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 2 + 0x02, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 3 + 0x03, + }, + }, + { + name: "struct with nil optional abstract fields", + val: cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(nil), + cadence.NewOptional(cadence.NewOptional(nil)), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(nil))), + }).WithType(structTypeWithOptionalAbstractField), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":null,"type":"Optional"},"name":"a"},{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":null,"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(39))], ["b", 138(138(137(39)))], ["c", 138(138(138(137(39))))]]])], [136(h''), [null, null, null]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", OptionalType(IntType)], ["b", OptionalType(OptionalType(IntType))], ["c", OptionalType(OptionalType(OptionalType(IntType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // nil + 0xf6, + // nil + 0xf6, + // nil + 0xf6, + }, + }, + { + name: "struct with optional Int for optional abstract fields", + val: cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(1))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2)))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewInt(3))))), + }).WithType(structTypeWithOptionalAbstractField), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":{"value":"3","type":"Int"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(39))], ["b", 138(138(137(39)))], ["c", 138(138(138(137(39))))]]])], [136(h''), [130([138(137(4)), 1]), 130([138(137(4)), 2]), 130([138(137(4)), 3])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", OptionalType(AnyStructType)], ["b", OptionalType(OptionalType(AnyStructType))], ["c", OptionalType(OptionalType(OptionalType(AnyStructType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // field 0 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // field 1 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // field 2 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + }, + { + name: "struct with non-nil optional abstract fields", + val: cadence.NewStruct([]cadence.Value{ + cadence.NewOptional(cadence.NewInt(1)), + cadence.NewOptional(cadence.NewOptional(cadence.NewInt(2))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.NewStruct([]cadence.Value{ + cadence.NewInt(3), + }).WithType(simpleStructType)))), + }).WithType(structTypeWithOptionalAbstractField), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.Foo","fields":[{"value":{"value":{"value":"1","type":"Int"},"type":"Optional"},"name":"a"},{"value":{"value":{"value":{"value":"2","type":"Int"},"type":"Optional"},"type":"Optional"},"name":"b"},{"value":{"value":{"value":{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"3","type":"Int"},"name":"bar"}]},"type":"Struct"},"type":"Optional"},"type":"Optional"},"type":"Optional"},"name":"c"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 138(137(39))], ["b", 138(138(137(39)))], ["c", 138(138(138(137(39))))]]]), 160([h'01', "S.test.FooStruct", [["bar", 137(4)]]])], [136(h''), [130([137(4), 1]), 130([137(4), 2]), 130([136(h'01'), [3]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", OptionalType(AnyStructType)], ["b", OptionalType(OptionalType(AnyStructType))], ["c", OptionalType(OptionalType(OptionalType(AnyStructType)))]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // field 2 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // fields: [["bar", IntType]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // field 0 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // field 1 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // field 2 + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + }, + }...) +} + +func TestEncodeBool(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "True", + val: cadence.NewBool(true), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Bool","value":true} + // + // language=edn, format=ccf + // 130([137(0), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + }, + { + name: "False", + val: cadence.NewBool(false), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Bool","value":false} + // + // language=edn, format=ccf + // 130([137(0), false]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // false + 0xf4, + }, + }, + }...) +} + +func TestEncodeCharacter(t *testing.T) { + + t.Parallel() + + a, _ := cadence.NewCharacter("a") + b, _ := cadence.NewCharacter("b") + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "a", + val: a, + expected: []byte{ // language=json, format=json-cdc + // {"type":"Character","value":"a"} + // + // language=edn, format=ccf + // 130([137(2), "a"]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Character type ID (2) + 0x02, + // UTF-8 string, 1 bytes follow + 0x61, + // a + 0x61, + }, + }, + { + name: "b", + val: b, + expected: []byte{ // language=json, format=json-cdc + // {"type":"Character","value":"b"} + // + // language=edn, format=ccf + // 130([137(2), "b"]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Character type ID (2) + 0x02, + // UTF-8 string, 1 bytes follow + 0x61, + // b + 0x62, + }, + }, + }...) +} + +func TestEncodeString(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Empty", + val: cadence.String(""), + expected: []byte{ // language=json, format=json-cdc + // {"type":"String","value":""} + // + // language=edn, format=ccf + // 130([137(1), ""]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // UTF-8 string, 0 bytes follow + 0x60, + }, + }, + { + name: "Non-empty", + val: cadence.String("foo"), + expected: []byte{ // language=json, format=json-cdc + // {"type":"String","value":"foo"} + // + // language=edn, format=ccf + // 130([137(1), "foo"]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // UTF-8 string, 3 bytes follow + 0x63, + // f, o, o + 0x66, 0x6f, 0x6f, + }, + }, + }...) +} + +func TestEncodeAddress(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + []byte{ // language=json, format=json-cdc + // {"type":"Address","value":"0x0000000102030405"} + // + // language=edn, format=ccf + // 130([137(3), h'0000000102030405']) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // bytes, 8 bytes follow + 0x48, + // 0, 0, 0, 1, 2, 3, 4, 5 + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x5, + }, + ) +} + +func TestEncodeInt(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Negative", + val: cadence.NewInt(-42), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int","value":"-42"} + // + // language=edn, format=ccf + // 130([137(4), -42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (negative big number) + 0xc3, + // bytes, 1 byte follow + 0x41, + // -42 + 0x29, + }, + }, + { + name: "Zero", + val: cadence.NewInt(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int","value":"0"} + // + // language=edn, format=ccf + // 130([137(4), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (positive big number) + 0xc2, + // bytes, 0 byte follow + 0x40, + }, + }, + { + name: "Positive", + val: cadence.NewInt(42), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int","value":"42"} + // + // language=edn, format=ccf + // 130([137(4), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (positive big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + }, + { + name: "SmallerThanMinInt256", + val: cadence.NewIntFromBig(new(big.Int).Sub(sema.Int256TypeMinIntBig, big.NewInt(10))), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819978"} + // + // language=edn, format=ccf + // 130([137(4), -57896044618658097711785492504343953926634992332820282019728792003956564819978]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (negative big number) + 0xc3, + // bytes, 32 bytes follow + 0x58, 0x20, + // -57896044618658097711785492504343953926634992332820282019728792003956564819978 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + }, + }, + { + name: "LargerThanMaxUInt256", + val: cadence.NewIntFromBig(new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} + // + // language=edn, format=ccf + // 130([137(4), 115792089237316195423570985008687907853269984665640564039457584007913129639945]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (positive big number) + 0xc2, + // bytes, 33 bytes follow + 0x58, 0x21, + // 115792089237316195423570985008687907853269984665640564039457584007913129639945 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, + }, + }, + }...) +} + +func TestEncodeInt8(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Min", + val: cadence.NewInt8(math.MinInt8), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int8","value":"-128"} + // + // language=edn, format=ccf + // 130([137(5), -128]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // -128 + 0x38, 0x7f, + }, + }, + { + name: "Zero", + val: cadence.NewInt8(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int8","value":"0"} + // + // language=edn, format=ccf + // 130([137(5), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewInt8(math.MaxInt8), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int8","value":"127"} + // + // language=edn, format=ccf + // 130([137(5), 127]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // 127 + 0x18, 0x7f, + }, + }, + }...) +} + +func TestEncodeInt16(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Min", + val: cadence.NewInt16(math.MinInt16), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int16","value":"-32768"} + // + // language=edn, format=ccf + // 130([137(6), -32768]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // -32768 + 0x39, 0x7F, 0xFF, + }, + }, + { + name: "Zero", + val: cadence.NewInt16(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int16","value":"0"} + // + // language=edn, format=ccf + // 130([137(6), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewInt16(math.MaxInt16), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int16","value":"32767"} + // + // language=edn, format=ccf + // 130([137(6), 32767]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // 32767 + 0x19, 0x7F, 0xFF, + }, + }, + }...) +} + +func TestEncodeInt32(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Min", + val: cadence.NewInt32(math.MinInt32), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int32","value":"-2147483648"} + // + // language=edn, format=ccf + // 130([137(7), -2147483648]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // -2147483648 + 0x3a, 0x7f, 0xff, 0xff, 0xff, + }, + }, + { + name: "Zero", + val: cadence.NewInt32(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int32","value":"0"} + // + // language=edn, format=ccf + // 130([137(7), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewInt32(math.MaxInt32), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int32","value":"2147483647"} + // + // language=edn, format=ccf + // 130([137(7), 2147483647]) + // + // language=cbor, format=ccf, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // 2147483647 + 0x1a, 0x7f, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeInt64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Min", + val: cadence.NewInt64(math.MinInt64), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int64","value":"-9223372036854775808"} + // + // language=edn, format=ccf + // 130([137(8), -9223372036854775808]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int64 type ID (8) + 0x08, + // -9223372036854775808 + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + name: "Zero", + val: cadence.NewInt64(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int64","value":"0"} + // + // language=edn, format=ccf + // 130([137(8), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int64 type ID (8) + 0x08, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewInt64(math.MaxInt64), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int64","value":"9223372036854775807"} + // + // language=edn, format=ccf + // 130([137(8), 9223372036854775807]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int64 type ID (8) + 0x08, + // 9223372036854775807 + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeInt128(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Min", + val: cadence.Int128{Value: sema.Int128TypeMinIntBig}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int128","value":"-170141183460469231731687303715884105728"} + // + // language=edn, format=ccf + // 130([137(9), -170141183460469231731687303715884105728]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // tag big num + 0xc3, + // bytes, 16 bytes follow + 0x50, + // -170141183460469231731687303715884105728 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + name: "Zero", + val: cadence.NewInt128(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int128","value":"0"} + // + // language=edn, format=ccf + // 130([137(9), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // tag big num + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + name: "Max", + val: cadence.Int128{Value: sema.Int128TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int128","value":"170141183460469231731687303715884105727"} + // + // language=edn, format=ccf + // 130([137(9), 170141183460469231731687303715884105727]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // tag big num + 0xc2, + // bytes, 16 bytes follow + 0x50, + // 170141183460469231731687303715884105727 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeInt256(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Min", + val: cadence.Int256{Value: sema.Int256TypeMinIntBig}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int256","value":"-57896044618658097711785492504343953926634992332820282019728792003956564819968"} + // + // language=edn, format=ccf + // 130([137(10), -57896044618658097711785492504343953926634992332820282019728792003956564819968]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // tag big num + 0xc3, + // bytes, 32 bytes follow + 0x58, 0x20, + // -57896044618658097711785492504343953926634992332820282019728792003956564819968 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + name: "Zero", + val: cadence.NewInt256(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int256","value":"0"} + // + // language=edn, format=ccf + // 130([137(10), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // tag big num + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + name: "Max", + val: cadence.Int256{Value: sema.Int256TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"Int256","value":"57896044618658097711785492504343953926634992332820282019728792003956564819967"} + // + // language=edn, format=ccf + // 130([137(10), 57896044618658097711785492504343953926634992332820282019728792003956564819967]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int256 type ID (10) + 0x0a, + // tag big num + 0xc2, + // bytes, 32 bytes follow + 0x58, 0x20, + // 57896044618658097711785492504343953926634992332820282019728792003956564819967 + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt","value":"0"} + // + // language=edn, format=ccf + // 130([137(11), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt type ID (11) + 0x0b, + // tag big num + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + name: "Positive", + val: cadence.NewUInt(42), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt","value":"42"} + // + // language=edn, format=ccf + // 130([137(11), 42]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt type ID (11) + 0x0b, + // tag big num + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 42 + 0x2a, + }, + }, + { + name: "LargerThanMaxUInt256", + val: cadence.UInt{Value: new(big.Int).Add(sema.UInt256TypeMaxIntBig, big.NewInt(10))}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt","value":"115792089237316195423570985008687907853269984665640564039457584007913129639945"} + // + // language=edn, format=ccf + // 130([137(11), 115792089237316195423570985008687907853269984665640564039457584007913129639945]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt type ID (11) + 0x0b, + // tag big num + 0xc2, + // bytes, 32 bytes follow + 0x58, 0x21, + // 115792089237316195423570985008687907853269984665640564039457584007913129639945 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, + }, + }, + }...) +} + +func TestEncodeUInt8(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt8(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt8","value":"0"} + // + // language=edn, format=ccf + // 130([137(12), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewUInt8(math.MaxUint8), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt8","value":"255"} + // + // language=edn, format=ccf + // 130([137(12), 255]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + // 255 + 0x18, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt16(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt16(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt16","value":"0"} + // + // language=edn, format=ccf + // 130([137(13), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt16 type ID (13) + 0x0d, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewUInt16(math.MaxUint16), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt16","value":"65535"} + // + // language=edn, format=ccf + // 130([137(13), 65535]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt16 type ID (13) + 0x0d, + // 65535 + 0x19, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt32(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt32(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt32","value":"0"} + // + // language=edn, format=ccf + // 130([137(14), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt32 type ID (14) + 0x0e, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewUInt32(math.MaxUint32), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt32","value":"4294967295"} + // + // language=edn, format=ccf + // 130([137(14), 4294967295]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt32 type ID (14) + 0x0e, + // 4294967295 + 0x1a, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt64(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt64","value":"0"} + // + // language=edn, format=ccf + // 130([137(15), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt64 type ID (15) + 0x0f, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewUInt64(uint64(math.MaxUint64)), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt64","value":"18446744073709551615"} + // + // language=edn, format=ccf + // 130([137(15), 18446744073709551615]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt64 type ID (15) + 0x0f, + // 18446744073709551615 + 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt128(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt128(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt128","value":"0"} + // + // language=edn, format=ccf + // 130([137(16), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt128 type ID (16) + 0x10, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + name: "Max", + val: cadence.UInt128{Value: sema.UInt128TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt128","value":"340282366920938463463374607431768211455"} + // + // language=edn, format=ccf + // 130([137(16), 340282366920938463463374607431768211455]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt128 type ID (16) + 0x10, + // tag (big num) + 0xc2, + // bytes, 16 bytes follow + 0x50, + // 340282366920938463463374607431768211455 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeUInt256(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewUInt256(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt256","value":"0"} + // + // language=edn, format=ccf + // 130([137(17), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt256 type ID (17) + 0x11, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + }, + }, + { + name: "Max", + val: cadence.UInt256{Value: sema.UInt256TypeMaxIntBig}, + expected: []byte{ // language=json, format=json-cdc + // {"type":"UInt256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"} + // + // language=edn, format=ccf + // 130([137(17), 115792089237316195423570985008687907853269984665640564039457584007913129639935]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt256 type ID (17) + 0x11, + // tag (big num) + 0xc2, + // bytes, 32 bytes follow + 0x58, 0x20, + // 115792089237316195423570985008687907853269984665640564039457584007913129639935 + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeWord8(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewWord8(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word8","value":"0"} + // + // language=edn, format=ccf + // 130([137(18), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word8 type ID (18) + 0x12, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewWord8(math.MaxUint8), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word8","value":"255"} + // + // language=edn, format=ccf + // 130([137(18), 255]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word8 type ID (18) + 0x12, + // 255 + 0x18, 0xff, + }, + }, + }...) +} + +func TestEncodeWord16(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewWord16(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word16","value":"0"} + // + // language=edn, format=ccf + // 130([137(19), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word16 type ID (19) + 0x13, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewWord16(math.MaxUint16), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word16","value":"65535"} + // + // language=edn, format=ccf + // 130([137(19), 65535]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word16 type ID (19) + 0x13, + // 65535 + 0x19, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeWord32(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewWord32(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word32","value":"0"} + // + // language=edn, format=ccf + // 130([137(20), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word32 type ID (20) + 0x14, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewWord32(math.MaxUint32), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word32","value":"4294967295"} + // + // language=edn, format=ccf + // 130([137(20), 4294967295]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word32 type ID (20) + 0x14, + // 4294967295 + 0x1a, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeWord64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.NewWord64(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word64","value":"0"} + // + // language=edn, format=ccf + // 130([137(21), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word64 type ID (21) + 0x15, + // 0 + 0x00, + }, + }, + { + name: "Max", + val: cadence.NewWord64(math.MaxUint64), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Word64","value":"18446744073709551615"} + // + // language=edn, format=ccf + // 130([137(21), 18446744073709551615]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Word64 type ID (21) + 0x15, + // 18446744073709551615 + 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }...) +} + +func TestEncodeFix64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.Fix64(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Fix64","value":"0.00000000"} + // + // language=edn, format=ccf + // 130([137(22), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // 0 + 0x00, + }, + }, + { + name: "789.00123010", + val: cadence.Fix64(78_900_123_010), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Fix64","value":"789.00123010"} + // + // language=edn, format=ccf + // 130([137(22), 78900123010]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // 78900123010 + 0x1b, 0x00, 0x00, 0x00, 0x12, 0x5e, 0xd0, 0x55, 0x82, + }, + }, + { + name: "1234.056", + val: cadence.Fix64(123_405_600_000), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Fix64","value":"1234.05600000"} + // + // language=edn, format=ccf + // 130([137(22), 123405600000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // 123405600000 + 0x1b, 0x00, 0x00, 0x00, 0x1c, 0xbb, 0x8c, 0x05, 0x00, + }, + }, + { + name: "-12345.006789", + val: cadence.Fix64(-1_234_500_678_900), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Fix64","value":"-12345.00678900"} + // + // language=edn, format=ccf + // 130([137(22), -1234500678900]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Fix64 type ID (22) + 0x16, + // -1234500678900 + 0x3b, 0x00, 0x00, 0x01, 0x1f, 0x6d, 0xf9, 0x74, 0xf3, + }, + }, + }...) +} + +func TestEncodeUFix64(t *testing.T) { + + t.Parallel() + + testAllEncodeAndDecode(t, []encodeTest{ + { + name: "Zero", + val: cadence.UFix64(0), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UFix64","value":"0.00000000"} + // + // language=edn, format=ccf + // 130([137(23), 0]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // 0 + 0x00, + }, + }, + { + name: "789.00123010", + val: cadence.UFix64(78_900_123_010), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UFix64","value":"789.00123010"} + // + // language=edn, format=ccf + // 130([137(23), 78900123010]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // 78900123010 + 0x1b, 0x00, 0x00, 0x00, 0x12, 0x5e, 0xd0, 0x55, 0x82, + }, + }, + { + name: "1234.056", + val: cadence.UFix64(123_405_600_000), + expected: []byte{ // language=json, format=json-cdc + // {"type":"UFix64","value":"1234.05600000"} + // + // language=edn, format=ccf + // 130([137(23), 123405600000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // 123405600000 + 0x1b, 0x00, 0x00, 0x00, 0x1c, 0xbb, 0x8c, 0x05, 0x00, + }, + }, + }...) +} + +func TestEncodeArray(t *testing.T) { + + t.Parallel() + + // [] + emptyArray := encodeTest{ + name: "Empty", + val: cadence.NewArray( + []cadence.Value{}, + ).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[]} + // + // language=edn, format=ccf + // 130([139(137(4)), []]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []int + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type + // array, 0 items follow + 0x80, + }, + } + + // constant sized array [1, 2, 3] + constantSizedIntArray := encodeTest{ + name: "Constant-sized Integers", + val: cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + cadence.NewInt(3), + }).WithType(cadence.NewConstantSizedArrayType(3, cadence.NewIntType())), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} + // + // language=edn, format=ccf + // 130([140[3, (137(4))], [1, 2, 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type constant-sized [3]int + // tag + 0xd8, ccf.CBORTagConstsizedArrayType, + // array, 2 items follow + 0x82, + // number of elements + 0x03, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // [1, 2, 3] + intArray := encodeTest{ + name: "Integers", + val: cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + cadence.NewInt(3), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"Int","value":"2"},{"type":"Int","value":"3"}]} + // + // language=edn, format=ccf + // 130([139(137(4)), [1, 2, 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []int + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // [[1], [2], [3]] + nestedArray := encodeTest{ + name: "Nested", + val: cadence.NewArray([]cadence.Value{ + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + cadence.NewArray([]cadence.Value{ + cadence.NewInt(2), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + cadence.NewArray([]cadence.Value{ + cadence.NewInt(3), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewIntType())), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewVariableSizedArrayType(cadence.NewIntType()))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":[{"value":"1","type":"Int"}],"type":"Array"},{"value":[{"value":"2","type":"Int"}],"type":"Array"},{"value":[{"value":"3","type":"Int"}],"type":"Array"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(139(137(4))), [[1], [2], [3]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type [[]int] + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 3 items follow + 0x83, + // array, 1 item follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 item follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // array, 1 item follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // [S.test.Foo{1}, S.test.Foo{2}, S.test.Foo{3}] + resourceArray := encodeTest{ + name: "Resources", + val: cadence.NewArray([]cadence.Value{ + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + }).WithType(fooResourceType), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + }).WithType(fooResourceType), + }).WithType(cadence.NewVariableSizedArrayType(fooResourceType)), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}},{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}]} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]])], [139(136(h'')), [[1], [2], [3]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // [S.test.Foo{1}, S.test.Foo{2}, S.test.Foo{3}] + // array, 3 items follow + 0x83, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + s, err := cadence.NewString("a") + require.NoError(t, err) + + resourceWithAbstractFieldArray := encodeTest{ + name: "Resources with abstract field", + val: cadence.NewArray([]cadence.Value{ + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(1), // field is AnyStruct type + }).WithType(foooResourceTypeWithAbstractField), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + s, // field is AnyStruct type + }).WithType(foooResourceTypeWithAbstractField), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + cadence.NewBool(true), // field is AnyStruct type + }).WithType(foooResourceTypeWithAbstractField), + }).WithType(cadence.NewVariableSizedArrayType(foooResourceTypeWithAbstractField)), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}},{"name":"baz","value":{"type":"Int","value":"1"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}},{"name":"baz","value":{"type":"String","value":"a"}}]}},{"type":"Resource","value":{"id":"S.test.Fooo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}},{"name":"baz","value":{"type":"Bool","value":true}}]}}]} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Fooo", [["bar", 137(4)], ["baz", 137(39)]]])], [139(136(h'')), [[1, 130([137(4), 1])], [2, 130([137(1), "a"])], [3, 130([137(0), true])]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Fooo" + // fields: [["bar", int type], ["baz", any type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 11 bytes follow + 0x6b, + // S.test.Fooo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // [S.test.Foo{1, 1}, S.test.Foo{2, "a"}, S.test.Foo{3, true}] + // array, 3 items follow + 0x83, + // element 0 + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text string, 1 byte + 0x61, + // "a" + 0x61, + // element 2 + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + } + + // [1, "a", true] + heterogeneousSimpleTypeArray := encodeTest{ + name: "Heterogenous AnyStruct Array with Simple Values", + val: cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + s, + cadence.NewBool(true), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Array","value":[{"type":"Int","value":"1"},{"type":"String","value":"a"},{"type":"Bool","value":true}]} + // + // language=edn, format=ccf + // 130([139(137(39)), [130([137(4), 1]), 130([137(1), "a"]), 130([137(0), true])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type ([]AnyStruct) + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data with inlined type because static array element type is abstract (AnyStruct) + // array, 3 items follow + 0x83, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text string, length 1 + 0x61, + // "a" + 0x61, + // element 2 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + } + + // [Int8(1), Int16(2), Int32(3)] + heterogeneousNumberTypeArray := encodeTest{ + name: "Heterogeous Number Array", + val: cadence.NewArray([]cadence.Value{ + cadence.NewInt8(1), + cadence.NewInt16(2), + cadence.NewInt32(3), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewNumberType())), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"1","type":"Int8"},{"value":"2","type":"Int16"},{"value":"3","type":"Int32"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(137(43)), [130([137(5), 1]), 130([137(6), 2]), 130([137(7), 3])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type ([]Integer) + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Number type ID (43) + 0x18, 0x2b, + // array data with inlined type because static array element type is abstract (Number) + // array, 3 items follow + 0x83, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int8 type ID (5) + 0x05, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int16 type ID (6) + 0x06, + // 2 + 0x02, + // element 2 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int32 type ID (7) + 0x07, + // 3 + 0x03, + }, + } + + // [1, S.test.Foo{1}] + heterogeneousCompositeTypeArray := encodeTest{ + name: "Heterogenous AnyStruct Array with Composite Value", + val: cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewAnyStructType())), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"1","type":"Int"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]])], [139(137(39)), [130([137(4), 1]), 130([136(h''), [1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // type ([]AnyStruct) + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data with inlined type because static array element type is abstract (AnyStruct) + // array, 2 items follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // S.test.Foo{1} + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + } + + resourceInterfaceType := &cadence.ResourceInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + } + + // [S.test.Foo{1}, S.test.Fooo{2, "a"}] + resourceInterfaceTypeArray := encodeTest{ + name: "Resource Interface Array", + val: cadence.NewArray([]cadence.Value{ + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + s, + }).WithType(foooResourceTypeWithAbstractField), + }).WithType(cadence.NewVariableSizedArrayType(resourceInterfaceType)), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},{"value":{"id":"S.test.Fooo","fields":[{"value":{"value":"2","type":"Int"},"name":"bar"},{"value":{"value":"a","type":"String"},"name":"baz"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[177([h'', "S.test.Bar"]), 161([h'01', "S.test.Foo", [["bar", 137(4)]]]), 161([h'02', "S.test.Fooo", [["bar", 137(4)], ["baz", 137(39)]]])], [139(136(h'')), [130([136(h'01'), [1]]), 130([136(h'02'), [2, 130([137(1), "a"])]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // type definition 0 + // resource interface type: + // id: []byte{} + // cadence-type-id: "S.test.Bar" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Boo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type definition 1 + // resource type: + // id: []byte{1} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // type definition 2: + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Fooo" + // fields: [["bar", int type], ["baz", any type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 11 bytes follow + 0x6b, + // S.test.Fooo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 item follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // S.test.Foo{1} + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // S.test.Fooo{2, "a"} + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text string, 1 byte + 0x61, + // "a" + 0x61, + }, + } + + // [S.test.FooStruct{"a", S.test.Foo{0}}, S.test.FooStruct{"b", S.test.Foo{1}}] + resourceStructArray := encodeTest{ + name: "Resource Struct Array", + val: cadence.NewArray([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.String("a"), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(0), + }).WithType(fooResourceType), + }).WithType(resourceStructType), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }).WithType(resourceStructType), + }).WithType(cadence.NewVariableSizedArrayType(resourceStructType)), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"a","type":"String"},"name":"a"},{"value":{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"0","type":"Int"},"name":"bar"}]},"type":"Resource"},"name":"b"}]},"type":"Struct"},{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"a"},{"value":{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"bar"}]},"type":"Resource"},"name":"b"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 160([h'01', "S.test.FooStruct", [["a", 137(1)], ["b", 136(h'')]]])], [139(136(h'01')), [["a", [0]], ["b", [1]]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // type definition 0 + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // type definition 1 + // struct type: + // id: []byte{1} + // cadence-type-id: "S.test.FooStruct" + // fields: [["a", string type], ["b", foo resource type]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // [S.test.FooStruct{"a", S.test.Foo{0}}, S.test.FooStruct{"b", S.test.Foo{1}}] + // array, 2 item follow + 0x82, + // element 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + // element 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + } + + structInterfaceType := &cadence.StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStructInterface", + } + + structType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + }, + } + + // [S.test.FooStruct{1}, S.test.FooStruct{2}] + structInterfaceTypeArray := encodeTest{ + name: "Struct Interface Array", + val: cadence.NewArray([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.NewInt(1), + }).WithType(structType), + cadence.NewStruct([]cadence.Value{ + cadence.NewInt(2), + }).WithType(structType), + }).WithType(cadence.NewVariableSizedArrayType(structInterfaceType)), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Struct"},{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"2","type":"Int"},"name":"a"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["a", 137(4)]]]), 176([h'01', "S.test.FooStructInterface"])], [139(136(h'01')), [130([136(h''), [1]]), 130([136(h''), [2]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // type definition 0 + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // fields: [["a", int type]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // type definition 1 + // struct interface type: + // id: []byte{1} + // cadence-type-id: "S.test.FooStructInterface" + // tag + 0xd8, ccf.CBORTagStructInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 25 bytes follow + 0x78, 0x19, + // S.test.FooStructInterface + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 item follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + }, + } + + contractInterfaceType := &cadence.ContractInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContractInterface", + } + + contractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + }, + } + + // [S.test.FooContract{1}, S.test.FooContract{2}] + contractInterfaceTypeArray := encodeTest{ + name: "Contract Interface Array", + val: cadence.NewArray([]cadence.Value{ + cadence.NewContract([]cadence.Value{ + cadence.NewInt(1), + }).WithType(contractType), + cadence.NewContract([]cadence.Value{ + cadence.NewInt(2), + }).WithType(contractType), + }).WithType(cadence.NewVariableSizedArrayType(contractInterfaceType)), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Contract"},{"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"2","type":"Int"},"name":"a"}]},"type":"Contract"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[163([h'', "S.test.FooContract", [["a", 137(4)]]]), 178([h'01', "S.test.FooContractInterface"])], [139(136(h'01')), [130([136(h''), [1]]), 130([136(h''), [2]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // type definition 0 + // contract type: + // id: []byte{} + // cadence-type-id: "S.test.FooContract" + // fields: [["a", int type]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // type definition 1 + // constract interface type: + // id: []byte{1} + // cadence-type-id: "S.test.FooContractInterface" + // tag + 0xd8, ccf.CBORTagContractInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 27 bytes follow + 0x78, 0x1b, + // S.test.FooContractInterface + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 item follow + 0x82, + // element 0 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // element 1 (inline type and value) + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + }, + } + + testAllEncodeAndDecode(t, + emptyArray, + constantSizedIntArray, + intArray, + nestedArray, + resourceStructArray, + resourceArray, + resourceWithAbstractFieldArray, + heterogeneousSimpleTypeArray, + heterogeneousNumberTypeArray, + heterogeneousCompositeTypeArray, + resourceInterfaceTypeArray, + structInterfaceTypeArray, + contractInterfaceTypeArray, + ) +} + +func TestEncodeDictionary(t *testing.T) { + + t.Parallel() + + // {} + emptyDict := encodeTest{ + name: "empty", + val: cadence.NewDictionary( + []cadence.KeyValuePair{}, + ).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + expected: []byte{ // language=json, format=json-cdc + // {"value":[],"type":"Dictionary"} + // + // language=edn, format=ccf + // 130([141([137(1), 137(4)]), []]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 6 items follow + 0x80, + }, + } + + // {"c":3, "b":2, "a":1} + simpleDict := encodeTest{ + name: "Simple", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} + // + // language=edn, format=ccf + // 130([141([137(1), 137(4)]), ["a", 1, "b", 2, "c", 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // {"c":{"3:3"}, "b":{"2":2}, "a":{"1":1}} + nestedDict := encodeTest{ + name: "Nested", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("3"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("b"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("2"), + Value: cadence.NewInt(2), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("a"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("1"), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + ), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("1"), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("b"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("2"), + Value: cadence.NewInt(2), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + { + Key: cadence.String("c"), + Value: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("3"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())), + ), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"1"},"value":{"type":"Int","value":"1"}}]}},{"key":{"type":"String","value":"b"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"2"},"value":{"type":"Int","value":"2"}}]}},{"key":{"type":"String","value":"c"},"value":{"type":"Dictionary","value":[{"key":{"type":"String","value":"3"},"value":{"type":"Int","value":"3"}}]}}]} + // + // language=edn, format=ccf + // 130([141([137(1), 141([137(1), 137(4)])]), ["a", ["1", 1], "b", ["2", 2], "c", ["3", 3]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]map[string, int]) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // nested dictionary + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // "1" + 0x31, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // nested dictionary + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // "2" + 0x32, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // nested dictionary + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // "3" + 0x33, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // {"c":foo{3}, "b":foo{2}, "a":foo{1}} + resourceDict := encodeTest{ + name: "Resources", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + }).WithType(fooResourceType), + }, + { + Key: cadence.String("b"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + }).WithType(fooResourceType), + }, + { + Key: cadence.String("a"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + fooResourceType, + )), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(1), + }).WithType(fooResourceType), + }, + + { + Key: cadence.String("b"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(2), + }).WithType(fooResourceType), + }, + { + Key: cadence.String("c"), + Value: cadence.NewResource([]cadence.Value{ + cadence.NewInt(3), + }).WithType(fooResourceType), + }, + }).WithType(cadence.NewDictionaryType( + cadence.NewStringType(), + fooResourceType, + )), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"1"}}]}}},{"key":{"type":"String","value":"b"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"2"}}]}}},{"key":{"type":"String","value":"c"},"value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"3"}}]}}}]} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]])], [141([137(1), 136(h'')]), ["a", [1], "b", [2], "c", [3]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + // {"c":3, 0:1, true:3} + heterogeneousSimpleTypeDict := encodeTest{ + name: "heterogeneous simple type", + val: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.NewInt(0), + Value: cadence.NewInt(1), + }, + { + Key: cadence.NewBool(true), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewAnyStructType(), cadence.NewAnyStructType())), + expectedVal: cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.NewBool(true), + Value: cadence.NewInt(3), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.NewInt(0), + Value: cadence.NewInt(1), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewAnyStructType(), cadence.NewAnyStructType())), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"key":{"value":"c","type":"String"},"value":{"value":"2","type":"Int"}},{"key":{"value":"0","type":"Int"},"value":{"value":"1","type":"Int"}},{"key":{"value":true,"type":"Bool"},"value":{"value":"3","type":"Int"}}],"type":"Dictionary"} + // + // language=edn, format=ccf + // 130([141([137(39), 137(39)]), [130([137(0), true]), 130([137(4), 3]), 130([137(1), "c"]), 130([137(4), 2]), 130([137(4), 0]), 130([137(4), 1])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[AnyStruct]AnyStruct) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // text, 1 byte follows + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 0 bytes follow + 0x40, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + } + + testAllEncodeAndDecode(t, + emptyDict, + simpleDict, + nestedDict, + resourceDict, + heterogeneousSimpleTypeDict, + //heterogeneousNumberTypeDict, + //heterogeneousCompositeTypeDict, + ) +} + +func TestEncodeSortedDictionary(t *testing.T) { + type testcase struct { + name string + val cadence.Value + expectedVal cadence.Value + expectedCBOR []byte + } + + dict := cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())) + + expectedDict := cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + }).WithType(cadence.NewDictionaryType(cadence.NewStringType(), cadence.NewIntType())) + + simpleDict := testcase{ + "Simple", + dict, + expectedDict, + []byte{ // language=json, format=json-cdc + // {"type":"Dictionary","value":[{"key":{"type":"String","value":"a"},"value":{"type":"Int","value":"1"}},{"key":{"type":"String","value":"b"},"value":{"type":"Int","value":"2"}},{"key":{"type":"String","value":"c"},"value":{"type":"Int","value":"3"}}]} + // + // language=edn, format=ccf + // 130([141([137(1), 137(4)]), ["a", 1, "b", 2, "c", 3]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[string]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 6 items follow + 0x86, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 1 bytes follow + 0x61, + // b + 0x62, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // string, 1 bytes follow + 0x61, + // c + 0x63, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 3 + 0x03, + }, + } + + testcases := []testcase{ + simpleDict, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + actualCBOR := testEncode(t, tc.val, tc.expectedCBOR) + testDecode(t, actualCBOR, tc.expectedVal) + }) + } +} + +func exportFromScript(t *testing.T, code string) cadence.Value { + checker, err := checker.ParseAndCheck(t, code) + require.NoError(t, err) + + var uuid uint64 + + inter, err := interpreter.NewInterpreter( + interpreter.ProgramFromChecker(checker), + checker.Location, + &interpreter.Config{ + UUIDHandler: func() (uint64, error) { + uuid++ + return uuid, nil + }, + AtreeStorageValidationEnabled: true, + AtreeValueValidationEnabled: true, + Storage: interpreter.NewInMemoryStorage(nil), + }, + ) + require.NoError(t, err) + + err = inter.Interpret() + require.NoError(t, err) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + exported, err := runtime.ExportValue(result, inter, interpreter.EmptyLocationRange) + require.NoError(t, err) + + return exported +} + +func TestEncodeResource(t *testing.T) { + + t.Parallel() + + t.Run("Simple", func(t *testing.T) { + + t.Parallel() + + actual := exportFromScript(t, ` + resource Foo { + let bar: Int + + init(bar: Int) { + self.bar = bar + } + } + + fun main(): @Foo { + return <- create Foo(bar: 42) + } + `) + + // expectedVal is different from actual because "bar" field is + // encoded before "uuid" field due to deterministic encoding. + expectedVal := cadence.NewResource([]cadence.Value{ + cadence.NewInt(42), + cadence.NewUInt64(1), + }).WithType(cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Foo", + []cadence.Field{ + {Type: cadence.NewIntType(), Identifier: "bar"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + )) + + expectedCBOR := []byte{ + // language=json, format=json-cdc + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"bar","value":{"type":"Int","value":"42"}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)], ["uuid", 137(15)]]])], [136(h''), [42, 1]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 2 fields: [["bar", type(int)], ["uuid", type(uint64)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint type ID (15) + 0x0f, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + // 1 + 0x01, + } + + testEncodeAndDecodeEx(t, actual, expectedCBOR, expectedVal) + }) + + t.Run("With function member", func(t *testing.T) { + + t.Parallel() + + actual := exportFromScript(t, ` + resource Foo { + let bar: Int + + fun foo(): String { + return "foo" + } + + init(bar: Int) { + self.bar = bar + } + } + + fun main(): @Foo { + return <- create Foo(bar: 42) + } + `) + + // expectedVal is different from actual because "bar" field is + // encoded before "uuid" field due to deterministic encoding. + expectedVal := cadence.NewResource([]cadence.Value{ + cadence.NewInt(42), + cadence.NewUInt64(1), + }).WithType(cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Foo", + []cadence.Field{ + {Type: cadence.NewIntType(), Identifier: "bar"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + )) + + // function "foo" should be omitted from resulting CBOR + expectedCBOR := []byte{ // language=json, format=json-cdc + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"bar","value":{"type":"Int","value":"42"}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)], ["uuid", 137(15)]]])], [136(h''), [42, 1]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 2 fields: [["bar", type(int)], ["uuid", type(uint64)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint type ID (15) + 0x0f, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + // 1 + 0x01, + } + + testEncodeAndDecodeEx(t, actual, expectedCBOR, expectedVal) + }) + + t.Run("Nested resource", func(t *testing.T) { + + t.Parallel() + + actual := exportFromScript(t, ` + resource Bar { + let x: Int + + init(x: Int) { + self.x = x + } + } + + resource Foo { + let bar: @Bar + + init(bar: @Bar) { + self.bar <- bar + } + + destroy() { + destroy self.bar + } + } + + fun main(): @Foo { + return <- create Foo(bar: <- create Bar(x: 42)) + } + `) + + // S.test.Foo(uuid: 2, bar: S.test.Bar(uuid: 1, x: 42)) (cadence.Resource) + + // expectedVal is different from actual because "bar" field is + // encoded before "uuid" field due to deterministic encoding. + expectedBarResourceType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Bar", + []cadence.Field{ + {Type: cadence.NewIntType(), Identifier: "x"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + ) + expectedBarResource := cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + cadence.NewUInt64(1), + }, + ).WithType(expectedBarResourceType) + + expectedVal := cadence.NewResource( + []cadence.Value{ + expectedBarResource, + cadence.NewUInt64(2), + }).WithType(cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Foo", + []cadence.Field{ + {Type: expectedBarResourceType, Identifier: "bar"}, + {Type: cadence.NewUInt64Type(), Identifier: "uuid"}, + }, + nil, + )) + + expectedCBOR := []byte{ // language=json, format=json-cdc + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"uuid","value":{"type":"UInt64","value":"2"}},{"name":"bar","value":{"type":"Resource","value":{"id":"S.test.Bar","fields":[{"name":"uuid","value":{"type":"UInt64","value":"1"}},{"name":"x","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Bar", [["x", 137(4)], ["uuid", 137(15)]]]), 161([h'01', "S.test.Foo", [["bar", 136(h'')], ["uuid", 137(15)]]])], [136(h'01'), [[42, 1], 2]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{01} + // cadence-type-id: "S.test.Bar" + // 2 fields: [["x", type(int)], ["uuid", type(uint64)], ] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x61, + // x + 0x78, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint64 type ID (15) + 0x0f, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 2 fields: [["bar", type ref(1)], ["uuid", type(uint64)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagTypeRef, + // type definition ID (0) + // bytes, 0 bytes follow + 0x40, + // field 1 + // array, 2 items follow + 0x82, + // text, 4 bytes follow + 0x64, + // uuid + 0x75, 0x75, 0x69, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Uint64 type ID (15) + 0x0f, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + // 1 + 0x01, + // 2 + 0x02, + } + + testEncodeAndDecodeEx(t, actual, expectedCBOR, expectedVal) + }) +} + +func TestEncodeStruct(t *testing.T) { + + t.Parallel() + + noFieldStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{}, + } + + noFieldStruct := encodeTest{ + name: "no field", + val: cadence.NewStruct( + []cadence.Value{}, + ).WithType(noFieldStructType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooStruct","fields":[]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", []])], [136(h''), []]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 0 fields: [] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // array, 0 item follows + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + }, + } + + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } + + simpleStruct := encodeTest{ + name: "Simple", + val: cadence.NewStruct( + []cadence.Value{ + cadence.NewInt(1), + cadence.String("foo"), + }, + ).WithType(simpleStructType), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + } + + resourceStruct := encodeTest{ + name: "Resources", + val: cadence.NewStruct( + []cadence.Value{ + cadence.String("foo"), + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + }, + ).WithType(fooResourceType), + }, + ).WithType(resourceStructType), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Struct","value":{"id":"S.test.FooStruct","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 160([h'01', "S.test.FooStruct", [["a", 137(1)], ["b", 136(h'')]]])], [136(h'01'), ["foo", [42]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // type reference ID (1) + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 1 items follow + 0x81, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + } + + testAllEncodeAndDecode(t, noFieldStruct, simpleStruct, resourceStruct) +} + +func TestEncodeEvent(t *testing.T) { + + t.Parallel() + + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "c", + Type: cadence.StringType{}, + }, + }, + } + + simpleEventType := &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEvent", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } + + simpleEvent := encodeTest{ + name: "Simple", + val: cadence.NewEvent( + []cadence.Value{ + cadence.NewInt(1), + cadence.String("foo"), + }, + ).WithType(simpleEventType), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} + // + // language=edn, format=ccf + // 129([[162([h'', "S.test.FooEvent", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + } + + abstractEventType := &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEvent", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.AnyStructType{}, + }, + }, + } + + abstractEvent := encodeTest{ + name: "abstract event", + val: cadence.NewEvent( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }, + ).WithType(abstractEventType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooEvent","fields":[{"value":{"value":"1","type":"Int"},"name":"a"},{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"c"}]},"type":"Struct"},"name":"b"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "S.test.FooEvent", [["a", 137(4)], ["b", 137(39)]]]), 160([h'01', "S.test.FooStruct", [["c", 137(1)]]])], [136(h''), [1, 130([136(h'01'), ["b"]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // event type: + // id: []byte{} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(anystruct)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // struct type: + // id: []byte{0x01} + // cadence-type-id: "S.test.FooStruct" + // 1 fields: [["c", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 1 items follow + 0x81, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + }, + } + + resourceEventType := &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEvent", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, + } + + resourceEvent := encodeTest{ + name: "Resources", + val: cadence.NewEvent( + []cadence.Value{ + cadence.String("foo"), + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + }, + ).WithType(fooResourceType), + }, + ).WithType(resourceEventType), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Event","value":{"id":"S.test.FooEvent","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 162([h'01', "S.test.FooEvent", [["a", 137(1)], ["b", 136(h'')]]])], [136(h'01'), ["foo", [42]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // event type: + // id: []byte{0x01} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 1 items follow + 0x81, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + } + + testAllEncodeAndDecode(t, simpleEvent, resourceEvent, abstractEvent) +} + +func TestEncodeContract(t *testing.T) { + + t.Parallel() + + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "c", + Type: cadence.StringType{}, + }, + }, + } + + simpleContractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } + + simpleContract := encodeTest{ + name: "Simple", + val: cadence.NewContract( + []cadence.Value{ + cadence.NewInt(1), + cadence.String("foo"), + }, + ).WithType(simpleContractType), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"Int","value":"1"}},{"name":"b","value":{"type":"String","value":"foo"}}]}} + // + // language=edn, format=ccf + // 129([[163([h'', "S.test.FooContract", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // contract type: + // id: []byte{} + // cadence-type-id: "S.test.FooContract" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 18 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + } + + abstractContractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.AnyStructType{}, + }, + }, + } + + abstractContract := encodeTest{ + name: "abstract contract", + val: cadence.NewContract( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }, + ).WithType(abstractContractType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooContract","fields":[{"value":{"value":"1","type":"Int"},"name":"a"},{"value":{"value":{"id":"S.test.FooStruct","fields":[{"value":{"value":"b","type":"String"},"name":"c"}]},"type":"Struct"},"name":"b"}]},"type":"Contract"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["c", 137(1)]]]), 163([h'01', "S.test.FooContract", [["a", 137(4)], ["b", 137(39)]]])], [136(h'01'), [1, 130([136(h''), ["b"]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 1 fields: [["c", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // c + 0x63, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // contract type: + // id: []byte{0x01} + // cadence-type-id: "S.test.FooContract" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 18 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follow + 0x40, + // array, 1 item follows + 0x81, + // String, 1 bytes follow + 0x61, + // "b" + 0x62, + }, + } + + resourceContractType := &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooContract", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, + } + + resourceContract := encodeTest{ + name: "Resources", + val: cadence.NewContract( + []cadence.Value{ + cadence.String("foo"), + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(42), + }, + ).WithType(fooResourceType), + }, + ).WithType(resourceContractType), + expected: []byte{ // language=json, format=json-cdc + // {"type":"Contract","value":{"id":"S.test.FooContract","fields":[{"name":"a","value":{"type":"String","value":"foo"}},{"name":"b","value":{"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"bar","value":{"type":"Int","value":"42"}}]}}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["bar", 137(4)]]]), 163([h'01', "S.test.FooContract", [["a", 137(1)], ["b", 136(h'')]]])], [136(h'01'), ["foo", [42]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["bar", int type]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // contract type: + // id: []byte{} + // cadence-type-id: "S.test.FooContract" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagContractType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 18 bytes follow + 0x72, + // S.test.FooContract + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 2 items follow + 0x82, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 1 items follow + 0x81, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 42 + 0x2a, + }, + } + + testAllEncodeAndDecode(t, simpleContract, abstractContract, resourceContract) +} + +func TestEncodeEnum(t *testing.T) { + t.Parallel() + + simpleEnumType := &cadence.EnumType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooEnum", + Fields: []cadence.Field{ + { + Identifier: "raw", + Type: cadence.UInt8Type{}, + }, + }, + } + + simpleEnum := encodeTest{ + name: "Simple", + val: cadence.NewEnum( + []cadence.Value{ + cadence.NewUInt8(1), + }, + ).WithType(simpleEnumType), + expected: []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.test.FooEnum","fields":[{"value":{"value":"1","type":"UInt8"},"name":"raw"}]},"type":"Enum"} + // + // language=edn, format=ccf + // 129([[164([h'', "S.test.FooEnum", [["raw", 137(12)]]])], [136(h''), [1]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // enum type: + // id: []byte{} + // cadence-type-id: "S.test.FooEnum" + // 1 fields: [["raw", type(uint8)]] + // tag + 0xd8, ccf.CBORTagEnumType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 14 bytes follow + 0x6e, + // S.test.FooEnum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x6e, 0x75, 0x6d, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // raw + 0x72, 0x61, 0x77, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 1 + 0x01, + }, + } + + testAllEncodeAndDecode(t, simpleEnum) +} + +func TestEncodeValueOfRestrictedType(t *testing.T) { + + t.Run("nil restricted type", func(t *testing.T) { + hasCountInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasCount", + nil, + nil, + ) + + hasSumInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasSum", + nil, + nil, + ) + + statsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("count", cadence.NewIntType()), + cadence.NewField("sum", cadence.NewIntType()), + }, + nil, + ) + + countSumRestrictedType := cadence.NewRestrictedType( + nil, + []cadence.Type{ + hasCountInterfaceType, + hasSumInterfaceType, + }, + ) + + val := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + }, + ).WithType(statsType), + }).WithType(cadence.NewVariableSizedArrayType(countSumRestrictedType)) + + expectedStatsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("sum", cadence.NewIntType()), + cadence.NewField("count", cadence.NewIntType()), + }, + nil, + ) + + expectedCountSumRestrictedType := cadence.NewRestrictedType( + nil, + []cadence.Type{ + hasSumInterfaceType, + hasCountInterfaceType, + }, + ) + + expectedVal := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(2), + cadence.NewInt(1), + }, + ).WithType(expectedStatsType), + }).WithType(cadence.NewVariableSizedArrayType(expectedCountSumRestrictedType)) + + testEncodeAndDecodeEx( + t, + val, + []byte{ + // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"count"},{"value":{"value":"2","type":"Int"},"name":"sum"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143([null, [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Stats" + // 2 fields: [["sum", type(int)], ["count", type(int)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x6c, + // S.test.Stats + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // sum + 0x73, 0x75, 0x6d, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 5 bytes follow + 0x65, + // count + 0x63, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // resource interface type: + // id: []byte{1} + // cadence-type-id: "S.test.HasSum" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 13 bytes follow + 0x6d, + // S.test.HasSum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, + // resource interface type: + // id: []byte{2} + // cadence-type-id: "S.test.HasCount" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.HasCount + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 2 items follow + 0x82, + // type + // null + 0xf6, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + }, + expectedVal, + ) + }) + + t.Run("resource restricted type", func(t *testing.T) { + hasCountInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasCount", + nil, + nil, + ) + + hasSumInterfaceType := cadence.NewResourceInterfaceType( + common.NewStringLocation(nil, "test"), + "HasSum", + nil, + nil, + ) + + statsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("count", cadence.NewIntType()), + cadence.NewField("sum", cadence.NewIntType()), + }, + nil, + ) + + countSumRestrictedType := cadence.NewRestrictedType( + statsType, + []cadence.Type{ + hasCountInterfaceType, + hasSumInterfaceType, + }, + ) + + val := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + }, + ).WithType(statsType), + }).WithType(cadence.NewVariableSizedArrayType(countSumRestrictedType)) + + expectedStatsType := cadence.NewResourceType( + common.NewStringLocation(nil, "test"), + "Stats", + []cadence.Field{ + cadence.NewField("sum", cadence.NewIntType()), + cadence.NewField("count", cadence.NewIntType()), + }, + nil, + ) + + expectedCountSumRestrictedType := cadence.NewRestrictedType( + expectedStatsType, + []cadence.Type{ + hasSumInterfaceType, + hasCountInterfaceType, + }, + ) + + expectedVal := cadence.NewArray([]cadence.Value{ + cadence.NewResource( + []cadence.Value{ + cadence.NewInt(2), + cadence.NewInt(1), + }, + ).WithType(expectedStatsType), + }).WithType(cadence.NewVariableSizedArrayType(expectedCountSumRestrictedType)) + + testEncodeAndDecodeEx( + t, + val, + []byte{ + // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Stats","fields":[{"value":{"value":"1","type":"Int"},"name":"sum"},{"value":{"value":"2","type":"Int"},"name":"count"}]},"type":"Resource"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Stats", [["sum", 137(4)], ["count", 137(4)]]]), 177([h'01', "S.test.HasSum"]), 177([h'02', "S.test.HasCount"])], [139(143([136(h''), [136(h'01'), 136(h'02')]])), [130([136(h''), [2, 1]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 3 items follow + 0x83, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Stats" + // 2 fields: [["sum", type(int)], ["count", type(int)]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 12 bytes follow + 0x6c, + // S.test.Stats + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // sum + 0x73, 0x75, 0x6d, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 5 bytes follow + 0x65, + // count + 0x63, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // resource interface type: + // id: []byte{1} + // cadence-type-id: "S.test.HasSum" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 13 bytes follow + 0x6d, + // S.test.HasSum + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x53, 0x75, 0x6d, + // resource interface type: + // id: []byte{2} + // cadence-type-id: "S.test.HasCount" + // tag + 0xd8, ccf.CBORTagResourceInterfaceType, + // array, 2 items follow + 0x82, + // id + // bytes, 1 bytes follow + 0x41, + // 2 + 0x02, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.HasCount + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 2 items follow + 0x82, + // type + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 items follow + 0x82, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // tag (big num) + 0xc2, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + }, + expectedVal, + ) + }) +} + +func TestEncodeValueOfReferenceType(t *testing.T) { + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + }, + } + + // ["a", "b"] with static type []&String + referenceToSimpleType := encodeTest{ + name: "array of reference to string", + val: cadence.NewArray([]cadence.Value{ + cadence.String("a"), + cadence.String("b"), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewStringType()), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"a","type":"String"},{"value":"b","type":"String"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 137(1)])), ["a", "b"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []&String + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // array data without inlined type + // array, 2 items follow + 0x82, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // text, 1 byte follow + 0x61, + // "b" + 0x62, + }, + } + + // ["a", nil] with static type []&String? + referenceToOptionalSimpleType := encodeTest{ + name: "array of reference to optional string", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.String("a")), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewOptionalType(cadence.NewStringType())), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 138(137(1))])), ["a", null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []&String? + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // array data without inlined type + // array, 2 items follow + 0x82, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // nil + 0xf6, + }, + } + + dictionaryType := &cadence.DictionaryType{ + KeyType: cadence.TheStringType, + ElementType: &cadence.OptionalType{ + Type: &cadence.ReferenceType{ + Type: cadence.TheInt128Type, + Authorized: false, + }, + }, + } + + // dictionary is generated by fuzzer. + dictionary := cadence.Dictionary{ + DictionaryType: dictionaryType, + Pairs: []cadence.KeyValuePair{ + { + Key: cadence.String("one"), + Value: cadence.Optional{ + Value: cadence.Int128{ + Value: big.NewInt(7456), + }, + }, + }, + }, + } + + // {"one": 7456} + optionalReferenceToSimpleType := encodeTest{ + name: "dictionary of optional reference to Int", + val: dictionary, + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"key":{"value":"one","type":"String"},"value":{"value":{"value":"7456","type":"Int128"},"type":"Optional"}}],"type":"Dictionary"} + // + // language=edn, format=ccf + // 130([141([137(1), 138(142([false, 137(9)]))]), ["one", 7456]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type {string: &Int128?} + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // string type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int128 type ID (9) + 0x09, + // array data without inlined type + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // "one" + 0x6f, 0x6e, 0x65, + // tag (big num) + 0xc2, + // bytes, 2 bytes follow + 0x42, + // 7456 + 0x1d, 0x20, + }, + } + + // ["a", 1] with static type []&AnyStruct + referenceToAnyStructWithSimpleTypes := encodeTest{ + name: "array of reference to any", + val: cadence.NewArray([]cadence.Value{ + cadence.String("a"), + cadence.NewUInt8(1), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewAnyStructType()), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"a","type":"String"},{"value":"1","type":"UInt8"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 137(39)])), [130([137(1), "a"]), 130([137(12), 1])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []&AnyStruct + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data without inlined type + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // string type ID (1) + 0x01, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt8 type ID (12) + 0x0c, + // 1 + 0x01, + }, + } + + // [FooStruct(1), FooStruct(2)] with static type []&FooStruct + referenceToStructType := encodeTest{ + name: "array of reference to struct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.String("a"), + }).WithType(simpleStructType), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, simpleStructType), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"a","type":"String"},"name":"a"}]},"type":"Struct"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"b","type":"String"},"name":"a"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 137(1)]]])], [139(142([false, 136(h'')])), [["a"], ["b"]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", StringType] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // array, 2 items follow + 0x82, + // type []&S.test.Foo + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array data without inlined type + // array, 2 items follow + 0x82, + // array, 1 items follow + 0x81, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // array, 1 items follow + 0x81, + // text, 1 byte follow + 0x61, + // "b" + 0x62, + }, + } + + // ["a", FooStruct("b")] with static type []&AnyStruct + referenceToAnyStructWithStructType := encodeTest{ + name: "array of reference to any with struct", + val: cadence.NewArray([]cadence.Value{ + cadence.String("a"), + cadence.NewStruct([]cadence.Value{ + cadence.String("b"), + }).WithType(simpleStructType), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType(false, cadence.NewAnyStructType()), + )), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":"a","type":"String"},{"value":{"id":"S.test.Foo","fields":[{"value":{"value":"1","type":"Int"},"name":"a"}]},"type":"Struct"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.Foo", [["a", 137(1)]]])], [139(142([false, 137(39)])), [130([137(1), "a"]), 130([136(h''), ["b"]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // fields: [["a", StringType] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // array, 2 items follow + 0x82, + // type []&AnyStruct + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // string type ID (1) + 0x01, + // text, 1 byte follow + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 1 item follows + 0x81, + // text, 1 byte follow + 0x61, + // "b" + 0x62, + }, + } + + // ["a", "b", nil] with type array of reference to optional AnyStruct + referenceToOptionalAnyStructType := encodeTest{ + name: "array of reference to optional AnyStruct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.String("a")), + cadence.NewOptional(cadence.NewOptional(cadence.String("b"))), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewReferenceType( + false, + cadence.NewOptionalType(cadence.NewAnyStructType()), + ))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(142([false, 138(137(39))])), [130([137(1), "a"]), 130([138(137(1)), "b"]), null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data + // array, 3 items follow + 0x83, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + // null + 0xf6, + }, + } + + optionalReferenceToAnyStructType := encodeTest{ + name: "array of optional reference to AnyStruct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.String("a")), + cadence.NewOptional(cadence.NewOptional(cadence.String("b"))), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewOptionalType( + cadence.NewReferenceType( + false, + cadence.NewAnyStructType(), + )))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":"a","type":"String"},"type":"Optional"},{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(138(142([false, 137(39)]))), [130([137(1), "a"]), 130([138(137(1)), "b"]), null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data + // array, 3 items follow + 0x83, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + // null + 0xf6, + }, + } + + optionalReferenceToOptionalAnyStructType := encodeTest{ + name: "array of optional reference to optional AnyStruct", + val: cadence.NewArray([]cadence.Value{ + cadence.NewOptional(cadence.NewOptional(cadence.String("a"))), + cadence.NewOptional(cadence.NewOptional(cadence.NewOptional(cadence.String("b")))), + cadence.NewOptional(nil), + }).WithType(cadence.NewVariableSizedArrayType( + cadence.NewOptionalType( + cadence.NewReferenceType( + false, + cadence.NewOptionalType( + cadence.NewAnyStructType(), + ))))), + expected: []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"value":{"value":"a","type":"String"},"type":"Optional"},"type":"Optional"},{"value":{"value":{"value":{"value":"b","type":"String"},"type":"Optional"},"type":"Optional"},"type":"Optional"},{"value":null,"type":"Optional"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(138(142([false, 138(137(39))]))), [130([137(1), "a"]), 130([138(137(1)), "b"]), null]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagReferenceType, + // array, 2 items follow + 0x82, + // false + 0xf4, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array data + // array, 3 items follow + 0x83, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "a" + 0x61, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // string, 1 byte follows + 0x61, + // "b" + 0x62, + // null + 0xf6, + }, + } + + testAllEncodeAndDecode(t, + referenceToSimpleType, + referenceToOptionalSimpleType, + referenceToStructType, + referenceToAnyStructWithSimpleTypes, + referenceToAnyStructWithStructType, + referenceToOptionalAnyStructType, + optionalReferenceToSimpleType, + optionalReferenceToAnyStructType, + optionalReferenceToOptionalAnyStructType, + ) +} + +func TestEncodeSimpleTypes(t *testing.T) { + + t.Parallel() + + type simpleTypes struct { + typ cadence.Type + cborSimpleTypeID int + } + + var tests []encodeTest + + for _, ty := range []simpleTypes{ + {cadence.AnyType{}, ccf.TypeAny}, + {cadence.AnyResourceType{}, ccf.TypeAnyResource}, + {cadence.MetaType{}, ccf.TypeMetaType}, + {cadence.VoidType{}, ccf.TypeVoid}, + {cadence.NeverType{}, ccf.TypeNever}, + {cadence.BoolType{}, ccf.TypeBool}, + {cadence.StringType{}, ccf.TypeString}, + {cadence.CharacterType{}, ccf.TypeCharacter}, + {cadence.BytesType{}, ccf.TypeBytes}, + {cadence.AddressType{}, ccf.TypeAddress}, + {cadence.SignedNumberType{}, ccf.TypeSignedNumber}, + {cadence.IntegerType{}, ccf.TypeInteger}, + {cadence.SignedIntegerType{}, ccf.TypeSignedInteger}, + {cadence.FixedPointType{}, ccf.TypeFixedPoint}, + {cadence.SignedFixedPointType{}, ccf.TypeSignedFixedPoint}, + {cadence.IntType{}, ccf.TypeInt}, + {cadence.Int8Type{}, ccf.TypeInt8}, + {cadence.Int16Type{}, ccf.TypeInt16}, + {cadence.Int32Type{}, ccf.TypeInt32}, + {cadence.Int64Type{}, ccf.TypeInt64}, + {cadence.Int128Type{}, ccf.TypeInt128}, + {cadence.Int256Type{}, ccf.TypeInt256}, + {cadence.UIntType{}, ccf.TypeUInt}, + {cadence.UInt8Type{}, ccf.TypeUInt8}, + {cadence.UInt16Type{}, ccf.TypeUInt16}, + {cadence.UInt32Type{}, ccf.TypeUInt32}, + {cadence.UInt64Type{}, ccf.TypeUInt64}, + {cadence.UInt128Type{}, ccf.TypeUInt128}, + {cadence.UInt256Type{}, ccf.TypeUInt256}, + {cadence.Word8Type{}, ccf.TypeWord8}, + {cadence.Word16Type{}, ccf.TypeWord16}, + {cadence.Word32Type{}, ccf.TypeWord32}, + {cadence.Word64Type{}, ccf.TypeWord64}, + {cadence.Fix64Type{}, ccf.TypeFix64}, + {cadence.UFix64Type{}, ccf.TypeUFix64}, + {cadence.BlockType{}, ccf.TypeBlock}, + {cadence.PathType{}, ccf.TypePath}, + {cadence.CapabilityPathType{}, ccf.TypeCapabilityPath}, + {cadence.StoragePathType{}, ccf.TypeStoragePath}, + {cadence.PublicPathType{}, ccf.TypePublicPath}, + {cadence.PrivatePathType{}, ccf.TypePrivatePath}, + {cadence.AccountKeyType{}, ccf.TypeAccountKey}, + {cadence.AuthAccountContractsType{}, ccf.TypeAuthAccountContracts}, + {cadence.AuthAccountKeysType{}, ccf.TypeAuthAccountKeys}, + {cadence.AuthAccountType{}, ccf.TypeAuthAccount}, + {cadence.PublicAccountContractsType{}, ccf.TypePublicAccountContracts}, + {cadence.PublicAccountKeysType{}, ccf.TypePublicAccountKeys}, + {cadence.PublicAccountType{}, ccf.TypePublicAccount}, + {cadence.DeployedContractType{}, ccf.TypeDeployedContract}, + } { + var w bytes.Buffer + + encoder := ccf.CBOREncMode.NewStreamEncoder(&w) + + err := encoder.EncodeRawBytes([]byte{ + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + }) + require.NoError(t, err) + + err = encoder.EncodeInt(ty.cborSimpleTypeID) + require.NoError(t, err) + + encoder.Flush() + + tests = append(tests, encodeTest{ + name: fmt.Sprintf("with static %s", ty.typ.ID()), + val: cadence.TypeValue{ + StaticType: ty.typ, + }, + expected: w.Bytes(), + // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"[ty.ID()]"}}} + // + // language=edn, format=ccf + // 130([137(41), 185(simple_type_id)]) + }) + } + + testAllEncodeAndDecode(t, tests...) +} + +func TestEncodeType(t *testing.T) { + + t.Parallel() + + t.Run("with static int?", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.OptionalType{Type: cadence.IntType{}}, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 186(185(4))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static int??", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.OptionalType{Type: &cadence.OptionalType{Type: cadence.IntType{}}}, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Optional", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 186(186(185(4)))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + t.Run("with static [int]", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.VariableSizedArrayType{ElementType: cadence.IntType{}}, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"VariableSizedArray", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 187(185(4))]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagVarsizedArrayTypeValue, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static [int; 3]", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ConstantSizedArrayType{ + ElementType: cadence.IntType{}, + Size: 3, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"ConstantSizedArray", "type" : {"kind" : "Int"}, "size" : 3}}} + // + // language=edn, format=ccf + // 130([137(41), 188([3, 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagConstsizedArrayTypeValue, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + }, + ) + + }) + + t.Run("with static {int:string}", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.DictionaryType{ + ElementType: cadence.StringType{}, + KeyType: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Dictionary", "key" : {"kind" : "Int"}, "value" : {"kind" : "String"}}}} + // + // language=edn, format=ccf + // 130([137(41), 189([185(4), 185(1)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + + }) + + t.Run("with static struct with no field", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[],"initializers":[]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + ) + }) + + t.Run("with static struct", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static resource of composite fields and initializers", func(t *testing.T) { + + t.Parallel() + + fooTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + fooTy2 := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo2", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + barTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + Fields: []cadence.Field{ + { + Identifier: "foo1", + Type: fooTy, + }, + }, + Initializers: [][]cadence.Parameter{ + { + cadence.Parameter{ + Type: fooTy2, + Label: "aaa", + Identifier: "aaa", + }, + }, + }, + } + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: barTy, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}],[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"bbb","id":"bbb"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])]], [[["aaa", "aaa", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo1 + 0x66, 0x6f, 0x6f, 0x31, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 elements follow + 0x80, + // initializer + // array, 0 elements follow + 0x80, + // initializers + // array, 1 elements follow + 0x81, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // text, 11 bytes follow + 0x6b, + // S.test.Foo2 + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x32, + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + }, + ) + }) + + t.Run("with static resource", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "R", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"},{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.R + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static contract", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ContractType{ + Location: utils.TestLocation, + QualifiedIdentifier: "C", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Contract","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 211([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (42) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.C + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static struct interface", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.StructInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "S", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"StructInterface","typeID":"S.test.S","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 224([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.S + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static resource interface", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ResourceInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "R", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"ResourceInterface","typeID":"S.test.R","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 225([h'', "S.test.R", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.R + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x52, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static contract interface", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ContractInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "C", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"ContractInterface","typeID":"S.test.C","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 226([h'', "S.test.C", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.C + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, + // type (nil for contract interface) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static event", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.EventType{ + Location: utils.TestLocation, + QualifiedIdentifier: "E", + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializer: []cadence.Parameter{ + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type", "value": {"staticType": {"kind": "Event", "type" : "", "typeID" : "S.test.E", "fields" : [ {"id" : "foo", "type": {"kind" : "Int"} } ], "initializers" : [[{"label" : "foo", "id" : "bar", "type": {"kind" : "Int"}}, {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}}]] } } } + // + // language=edn, format=ccf + // 130([137(41), 210([h'', "S.test.E", null, [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEventTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.E + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + // type (nil for event) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static enum", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.EnumType{ + Location: utils.TestLocation, + QualifiedIdentifier: "E", + RawType: cadence.StringType{}, + Fields: []cadence.Field{ + {Identifier: "foo", Type: cadence.IntType{}}, + }, + Initializers: [][]cadence.Parameter{ + { + {Label: "foo", Identifier: "bar", Type: cadence.IntType{}}, + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":{"kind":"String"},"kind":"Enum","typeID":"S.test.E","fields":[{"type":{"kind":"Int"},"id":"foo"}],"initializers":[[{"type":{"kind":"Int"},"label":"foo","id":"bar"}],[{"type":{"kind":"String"},"label":"qux","id":"baz"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 212([h'', "S.test.E", 185(1), [["foo", 185(4)]], [[["foo", "bar", 185(4)], ["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEnumTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.E + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 1 elements follow + 0x81, + // array, 2 element follows + 0x82, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + ) + }) + + t.Run("with static &int", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.ReferenceType{ + Authorized: false, + Type: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Reference", "type" : {"kind" : "Int"}, "authorized" : false}}}` + // + // language=edn, format=ccf + // 130([137(41), 190([false, 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagReferenceTypeValue, + // array, 2 elements follow + 0x82, + // authorized + // bool + 0xf4, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + + }) + + t.Run("with static function", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.AnyStructType{}}, + }, + Parameters: []cadence.Parameter{ + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + ReturnType: cadence.IntType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Function","typeParameters":[{"name":"T","typeBound":{"kind":"AnyStruct"}}],"parameters":[{"type":{"kind":"String"},"label":"qux","id":"baz"}],"return":{"kind":"Int"}}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 193([[["T", 185(39)]], [["qux", "baz", 185(1)]], 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagFunctionTypeValue, + // array, 3 elements follow + 0x83, + // array, 1 elements follow + 0x81, + // array, 2 elements follow + 0x82, + // string, 1 byte follows + 0x61, + // "T" + 0x54, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type (39) + 0x18, 0x27, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + }) + + t.Run("with static function nil type bound", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T"}, + }, + Parameters: []cadence.Parameter{ + {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, + }, + ReturnType: cadence.IntType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Function","typeParameters":[{"name":"T","typeBound":null}],"parameters":[{"type":{"kind":"String"},"label":"qux","id":"baz"}],"return":{"kind":"Int"}}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 193([[["T", null]], [["qux", "baz", 185(1)]], 185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagFunctionTypeValue, + // array, 3 elements follow + 0x83, + // array, 1 elements follow + 0x81, + // array, 2 elements follow + 0x82, + // string, 1 byte follows + 0x61, + // "T" + 0x54, + // null + 0xf6, + // array, 1 elements follow + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + }) + + t.Run("with static Capability", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.CapabilityType{}, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Capability"}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 192([null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagCapabilityTypeValue, + // array, 1 element follows + 0x81, + // null + 0xf6, + }, + ) + }) + + t.Run("with static Capability", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: &cadence.CapabilityType{ + BorrowType: cadence.IntType{}, + }, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Capability", "type" : {"kind" : "Int"}}}} + // + // language=edn, format=ccf + // 130([137(41), 192([185(4)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagCapabilityTypeValue, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + }, + ) + }) + + t.Run("with static nil restricted type", func(t *testing.T) { + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","type":"","restrictions":[]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191([null, []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // null + 0xf6, + // array, 0 element follows + 0x80, + }, + ) + }) + + t.Run("with static no restricted type", func(t *testing.T) { + + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + Type: cadence.IntType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{String}","type":{"kind":"Int"},"restrictions":[]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191([185(4), []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + // array, 0 element follows + 0x80, + }, + // Expected decoded RestrictedType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{}, + Type: cadence.IntType{}, + }), + }, + ) + }) + + t.Run("with static restricted type", func(t *testing.T) { + + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType": { "kind": "Restriction", "typeID":"Int{String}", "type" : {"kind" : "Int"}, "restrictions" : [ {"kind" : "String"} ]} } } + // + // language=edn, format=ccf + // 130([137(41), 191([185(4), [185(1)]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, + }, + // Expected decoded RestrictedType doesn't have type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }), + }, + ) + + }) + + t.Run("with static 2 restricted types", func(t *testing.T) { + + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.NewAnyStructType(), + cadence.StringType{}, + }, + Type: cadence.IntType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","typeID":"Int{AnyStruct, String}","type":{"kind":"Int"},"restrictions":[{"kind":"AnyStruct"},{"kind":"String"}]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191([185(4), [185(1), 185(39)]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type ID (4) + 0x04, + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type ID (1) + 0x01, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type ID (39) + 0x18, 0x27, + }, + // Expected decoded RestrictedType has sorted restrictions and no type ID. + cadence.TypeValue{ + StaticType: (&cadence.RestrictedType{ + Restrictions: []cadence.Type{ + cadence.StringType{}, + cadence.NewAnyStructType(), + }, + Type: cadence.IntType{}, + }), + }, + ) + }) + + t.Run("with static 3 restricted types", func(t *testing.T) { + + // restrictedType is generated by fuzzer. + testEncodeAndDecodeEx( + t, + cadence.TypeValue{ + StaticType: &cadence.RestrictedType{ + Type: cadence.TheAnyStructType, + Restrictions: []cadence.Type{ + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), + "TypeA", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), + "TypeB", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.IdentifierLocation("LocationC"), + "TypeC", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + }, + }, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"kind":"Restriction","typeID":"","type":{"kind":"AnyStruct"},"restrictions":[{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeA","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"A.0100000000000000.TypeB","fields":[],"initializers":[]},{"type":"","kind":"StructInterface","typeID":"I.LocationC.TypeC","fields":[],"initializers":[]}]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 191([185(39), [224([h'', "I.LocationC.TypeC", null, [], []]), 224([h'01', "A.0100000000000000.TypeA", null, [], []]), 224([h'02', "A.0100000000000000.TypeB", null, [], []])]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // AnyStruct type ID (39) + 0x18, 0x27, + // 3 sorted restrictions + // array, 3 element follows + 0x83, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 17 bytes follow + 0x71, + // "I.LocationC.TypeC" + 0x49, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x43, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "A.0100000000000000.TypeA" + 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x41, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "A.0100000000000000.TypeB" + 0x41, 0x2e, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x42, + // type + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + }, + // Expected decoded RestrictedType has sorted restrictions and no type ID. + cadence.TypeValue{ + StaticType: &cadence.RestrictedType{ + Type: cadence.TheAnyStructType, + Restrictions: []cadence.Type{ + cadence.NewStructInterfaceType( + common.IdentifierLocation("LocationC"), + "TypeC", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeA"), + "TypeA", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + cadence.NewStructInterfaceType( + common.NewAddressLocation(nil, common.Address{0x01}, "TypeB"), + "TypeB", + []cadence.Field{}, + [][]cadence.Parameter{}, + ), + }, + }, + }, + ) + }) + + t.Run("without static type", func(t *testing.T) { + + t.Parallel() + + testEncodeAndDecode( + t, + cadence.TypeValue{}, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":""}} + // + // language=edn, format=ccf + // 130([137(41), null]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // nil + 0xf6, + }, + ) + }) +} + +func TestEncodeCapability(t *testing.T) { + + t.Parallel() + + t.Run("Capability", func(t *testing.T) { + + path, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + cadence.StorageCapability{ + Path: path, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"path":{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},"borrowType":"","address":"0x0000000102030405"},"type":"Capability"} + // + // language=edn, format=ccf + // 130([144([null]), [h'0000000102030405', [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // null + 0xf6, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("array of Capability", func(t *testing.T) { + simpleStructType := &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, + } + + path1, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + path2, err := cadence.NewPath(1, "bar") + require.NoError(t, err) + + capability1 := cadence.StorageCapability{ + Path: path1, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + + capability2 := cadence.StorageCapability{ + Path: path2, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: simpleStructType, + } + + testEncodeAndDecode( + t, + cadence.NewArray([]cadence.Value{ + capability1, + capability2, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewCapabilityType(nil))), + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"path":{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"},"type":"Capability"},{"value":{"path":{"value":{"domain":"storage","identifier":"bar"},"type":"Path"},"borrowType":{"type":"","kind":"Struct","typeID":"S.test.FooStruct","fields":[{"type":{"kind":"Int"},"id":"bar"}],"initializers":[]},"address":"0x0000000102030405"},"type":"Capability"}],"type":"Array"} + // + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["bar", 137(4)]]])], [139(144([null])), [130([144([137(4)]), [h'0000000102030405', [1, "foo"]]]), 130([144([136(h'')]), [h'0000000102030405', [1, "bar"]]])]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // fields: [["bar", IntType]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // null + 0xf6, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 elements follow + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 elements follow + 0x81, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + }, + ) + }) + + t.Run("Capability", func(t *testing.T) { + path, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + cadence.StorageCapability{ + Path: path, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Capability","value":{"path":{"type":"Path","value":{"domain":"storage","identifier":"foo"}},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"}} + // + // language=edn, format=ccf + // 130([144([137(4)]), [h'0000000102030405', [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("array of Capability", func(t *testing.T) { + + path1, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + path2, err := cadence.NewPath(1, "bar") + require.NoError(t, err) + + capability1 := cadence.StorageCapability{ + Path: path1, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + capability2 := cadence.StorageCapability{ + Path: path2, + Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), + BorrowType: cadence.IntType{}, + } + + testEncodeAndDecode( + t, + cadence.NewArray([]cadence.Value{ + capability1, + capability2, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewCapabilityType(cadence.NewIntType()))), + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"path":{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"},"type":"Capability"},{"value":{"path":{"value":{"domain":"storage","identifier":"bar"},"type":"Path"},"borrowType":{"kind":"Int"},"address":"0x0000000102030405"},"type":"Capability"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(144([137(4)])), [[h'0000000102030405', [1, "foo"]], [h'0000000102030405', [1, "bar"]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagCapabilityType, + // array, 1 element follows + 0x81, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array, 2 elements follow + 0x82, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // array, 2 elements follow + 0x82, + // address + // bytes, 8 bytes folloow + 0x48, + // {1,2,3,4,5} + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + }, + ) + }) +} + +func TestDecodeFix64(t *testing.T) { + + t.Parallel() + + var maxInt int64 = sema.Fix64TypeMaxInt + var minInt int64 = sema.Fix64TypeMinInt + var maxFrac int64 = sema.Fix64TypeMaxFractional + var minFrac int64 = sema.Fix64TypeMinFractional + var factor int64 = sema.Fix64Factor + + type test struct { + name string + expected cadence.Fix64 + encodedData []byte + check func(t *testing.T, actual cadence.Value, err error) + } + + var tests = []test{ + { + name: "12.3", + expected: cadence.Fix64(12_30000000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.3"} + // + // language=edn, format=ccf + // 130([137(22), 1230000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1230000000 + 0x1a, 0x49, 0x50, 0x4f, 0x80, + }, + }, + { + name: "12.03", + expected: cadence.Fix64(12_03000000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.03"} + // + // language=edn, format=ccf + // 130([137(22), 1203000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1203000000 + 0x1a, 0x47, 0xb4, 0x52, 0xc0, + }, + }, + { + name: "12.003", + expected: cadence.Fix64(12_00300000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.003"} + // + // language=edn, format=ccf + // 130([137(22), 1200300000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200300000 + 0x1a, 0x47, 0x8b, 0x1f, 0xe0, + }, + }, + { + name: "12.0003", + expected: cadence.Fix64(12_00030000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.0003"} + // + // language=edn, format=ccf + // 130([137(22), 1200030000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200030000 + 0x1a, 0x47, 0x87, 0x01, 0x30, + }, + }, + { + name: "12.00003", + expected: cadence.Fix64(12_00003000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.00003"} + // + // language=edn, format=ccf + // 130([137(22), 1200003000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200003000 + 0x1a, 0x47, 0x86, 0x97, 0xb8, + }, + }, + { + name: "12.000003", + expected: cadence.Fix64(12_00000300), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.000003"} + // + // language=edn, format=ccf + // 130([137(22), 1200000300]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200000300 + 0x1a, 0x47, 0x86, 0x8d, 0x2c, + }, + }, + { + name: "12.0000003", + expected: cadence.Fix64(12_00000030), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "12.0000003"} + // + // language=edn, format=ccf + // 130([137(22), 1200000030]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 1200000030 + 0x1a, 0x47, 0x86, 0x8c, 0x1e, + }, + }, + { + name: "120.3", + expected: cadence.Fix64(120_30000000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "120.3"} + // + // language=edn, format=ccf + // 130([137(22), 12030000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 12030000000 + 0x1b, 0x00, 0x00, 0x00, 0x02, 0xcd, 0x0b, 0x3b, 0x80, + }, + }, + { + // 92233720368.1 + name: fmt.Sprintf("%d.1", maxInt), + expected: cadence.Fix64(9223372036810000000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "92233720368.1"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036810000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036810000000 + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xfd, 0x54, 0xc6, 0x80, + }, + }, + { + // 92233720369.1 + name: fmt.Sprintf("%d.1", maxInt+1), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "92233720369.1"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036910000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036910000000 + 0x1b, 0x80, 0x00, 0x00, 0x00, 0x03, 0x4a, 0xa7, 0x80, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + { + // -92233720368.1 + name: fmt.Sprintf("%d.1", minInt), + expected: cadence.Fix64(-9223372036810000000), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "-92233720368.1"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036810000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036810000000 + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xfd, 0x54, 0xc6, 0x7f, + }, + }, + { + // -92233720369.1 + name: fmt.Sprintf("%d.1", minInt-1), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "-92233720369.1"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036910000000]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036910000000 + 0x3b, 0x80, 0x00, 0x00, 0x00, 0x03, 0x4a, 0xa7, 0x7f, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + { + // 92233720368.54775807 + name: fmt.Sprintf("%d.%d", maxInt, maxFrac), + expected: cadence.Fix64(maxInt*factor + maxFrac), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "92233720368.54775807"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036854775807]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036854775807 + 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + // 92233720368.54775808 + name: fmt.Sprintf("%d.%d", maxInt, maxFrac+1), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "92233720368.54775808"} + // + // language=edn, format=ccf + // 130([137(22), 9223372036854775808]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // 9223372036854775808 + 0x1b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + { + // -92233720368.54775808 + name: fmt.Sprintf("%d.%d", minInt, -(minFrac)), + expected: cadence.Fix64(-9223372036854775808), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "-92233720368.54775808"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036854775808]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036854775808 + 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + // -92233720368.54775809 + name: fmt.Sprintf("%d.%d", minInt, -(minFrac - 1)), + encodedData: []byte{ + // language=json, format=json-cdc + // {"type": "Fix64", "value": "-92233720368.54775809"} + // + // language=edn, format=ccf + // 130([137(22), -9223372036854775809]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // fix64 type ID (22) + 0x16, + // -9223372036854775809 + 0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + check: func(t *testing.T, actual cadence.Value, err error) { + assert.Error(t, err) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := ccf.Decode(nil, tt.encodedData) + if tt.check != nil { + tt.check(t, actual, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expected, actual) + } + }) + } +} + +func TestExportRecursiveType(t *testing.T) { + + t.Parallel() + + ty := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "foo", + }, + }, + } + + ty.Fields[0].Type = &cadence.OptionalType{ + Type: ty, + } + + testEncode( + t, + cadence.Resource{ + Fields: []cadence.Value{ + cadence.Optional{}, + }, + }.WithType(ty), + []byte{ // language=json, format=json-cdc + // {"type":"Resource","value":{"id":"S.test.Foo","fields":[{"name":"foo","value":{"type": "Optional","value":null}}]}} + // + // language=edn, format=ccf + // 129([[161([h'', "S.test.Foo", [["foo", 138(136(h''))]]])], [136(h''), [null]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // resource type: + // id: []byte{} + // cadence-type-id: "S.test.Foo" + // 1 fields: [["foo", optional(type ref id(0))]] + // tag + 0xd8, ccf.CBORTagResourceType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 items follow + 0x82, + // text, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // nil + 0xf6, + }, + ) + +} + +func TestExportTypeValueRecursiveType(t *testing.T) { + + t.Parallel() + + t.Run("recursive field", func(t *testing.T) { + + t.Parallel() + + ty := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "foo", + }, + }, + Initializers: [][]cadence.Parameter{}, + } + + ty.Fields[0].Type = &cadence.OptionalType{ + Type: ty, + } + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: ty, + }, + []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"S.test.Foo","fields":[{"id":"foo","type":{"kind":"Optional","type":"S.test.Foo"}}],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.Foo", null, [["foo", 186(184(h''))]], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 4 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6F, 0x6F, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 bytes follow + 0x40, + // initializers + // array, 0 elements follow + 0x80, + }, + ) + + }) + + t.Run("recursive initializer", func(t *testing.T) { + + // structType is generated by fuzzer. + structType := cadence.NewStructType( + common.StringLocation("foo"), + "Foo", + []cadence.Field{}, + [][]cadence.Parameter{{{}}}, + ) + + structType.Initializers[0][0] = cadence.Parameter{ + Type: &cadence.OptionalType{Type: structType}, + Label: "aaa", + Identifier: "aaa", + } + + typeValue := cadence.NewTypeValue(structType) + + testEncodeAndDecode( + t, + typeValue, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.foo.Foo","fields":[],"initializers":[[{"type":{"type":"S.foo.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.foo.Foo", null, [], [[["aaa", "aaa", 186(184(h''))]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 4 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 9 bytes follow + 0x69, + // "S.foo.Foo" + 0x53, 0x2e, 0x66, 0x6f, 0x6f, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 1 element follows + 0x81, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 byte follows, + 0x40, + }, + ) + }) + + t.Run("recursive field and initializer", func(t *testing.T) { + + // structType is generated by fuzzer. + structType := cadence.NewStructType( + common.StringLocation("foo"), + "Foo", + []cadence.Field{{}}, + [][]cadence.Parameter{{{}}}, + ) + + structType.Fields[0] = cadence.Field{ + Type: &cadence.OptionalType{Type: structType}, + Identifier: "aa", + } + + structType.Initializers[0][0] = cadence.Parameter{ + Type: &cadence.OptionalType{Type: structType}, + Label: "aaa", + Identifier: "aaa", + } + + typeValue := cadence.NewTypeValue(structType) + + testEncodeAndDecode( + t, + typeValue, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Struct","typeID":"S.foo.Foo","fields":[{"type":{"type":"S.foo.Foo","kind":"Optional"},"id":"aa"}],"initializers":[[{"type":{"type":"S.foo.Foo","kind":"Optional"},"label":"aaa","id":"aaa"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.foo.Foo", null, [["aa", 186(184(h''))]], [[["aaa", "aaa", 186(184(h''))]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 4 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 9 bytes follow + 0x69, + // "S.foo.Foo" + 0x53, 0x2e, 0x66, 0x6f, 0x6f, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // text, 2 bytes follow + 0x62, + // "aa" + 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 byte follows + 0x40, + // initializers + // array, 1 element follows + 0x81, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // "aaa" + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 0 byte follows, + 0x40, + }, + ) + }) + + t.Run("non-recursive, repeated", func(t *testing.T) { + + t.Parallel() + + fooTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + fooTy2 := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo2", + Fields: []cadence.Field{}, + Initializers: [][]cadence.Parameter{}, + } + + barTy := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Bar", + Fields: []cadence.Field{ + { + Identifier: "foo1", + Type: fooTy, + }, + { + Identifier: "foo2", + Type: fooTy, + }, + }, + Initializers: [][]cadence.Parameter{ + { + cadence.Parameter{ + Type: &cadence.OptionalType{Type: fooTy}, + Label: "bbb", + Identifier: "bbb", + }, + cadence.Parameter{ + Type: fooTy2, + Label: "aaa", + Identifier: "aaa", + }, + }, + }, + } + + testEncodeAndDecode( + t, + cadence.TypeValue{ + StaticType: barTy, + }, + []byte{ // language=json, format=json-cdc + // {"value":{"staticType":{"type":"","kind":"Resource","typeID":"S.test.Bar","fields":[{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo","fields":[],"initializers":[]},"id":"foo1"},{"type":"S.test.Foo","id":"foo2"}],"initializers":[[{"type":{"type":"S.test.Foo","kind":"Optional"},"label":"bbb","id":"bbb"},{"type":{"type":"","kind":"Resource","typeID":"S.test.Foo2","fields":[],"initializers":[]},"label":"aaa","id":"aaa"}]]}},"type":"Type"} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "S.test.Bar", null, [["foo1", 209([h'01', "S.test.Foo", null, [], []])], ["foo2", 184(h'01')]], [[["bbb", "bbb", 186(184(h'01'))], ["aaa", "aaa", 209([h'02', "S.test.Foo2", null, [], []])]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 10 bytes follow + 0x6a, + // S.test.Bar + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x61, 0x72, + // type (nil for struct) + 0xf6, + // fields + // array, 2 element follows + 0x82, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo1 + 0x66, 0x6f, 0x6f, 0x31, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // string, 10 bytes follow + 0x6a, + // S.test.Foo + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, + // type (nil for struct) + 0xf6, + // fields + // array, 0 elements follow + 0x80, + // initializer + // array, 0 elements follow + 0x80, + // array, 2 elements follow + 0x82, + // string, 4 bytes follow + 0x64, + // foo2 + 0x66, 0x6f, 0x6f, 0x32, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // initializers + // array, 1 element follow + 0x81, + // array, 2 elements follow + 0x82, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // bbb + 0x62, 0x62, 0x62, + // text, 3 bytes follow + 0x63, + // bbb + 0x62, 0x62, 0x62, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // tag + 0xd8, ccf.CBORTagTypeValueRef, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + // array, 3 elements follow + 0x83, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // text, 3 bytes follow + 0x63, + // aaa + 0x61, 0x61, 0x61, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // text, 11 bytes follow + 0x6b, + // S.test.Foo2 + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x32, + // null + 0xf6, + // array, 0 element follows + 0x80, + // array, 0 element follows + 0x80, + }, + ) + }) +} + +func TestEncodePath(t *testing.T) { + + t.Parallel() + + t.Run("Storage", func(t *testing.T) { + path, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + path, + []byte{ // language=json, format=json-cdc + // {"value":{"domain":"storage","identifier":"foo"},"type":"Path"} + // + // language=edn, format=ccf + // 130([137(26), [1, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // StoragePath type ID (26) + 0x18, 0x1a, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("Private", func(t *testing.T) { + path, err := cadence.NewPath(2, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + path, + []byte{ // language=json, format=json-cdc + // {"type":"Path","value":{"domain":"private","identifier":"foo"}} + // + // language=edn, format=ccf + // 130([137(28), [2, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PrivatePath type ID (28) + 0x18, 0x1c, + // array, 2 elements follow + 0x82, + // 2 + 0x02, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("Public", func(t *testing.T) { + path, err := cadence.NewPath(3, "foo") + require.NoError(t, err) + + testEncodeAndDecode( + t, + path, + []byte{ // language=json, format=json-cdc + // {"type":"Path","value":{"domain":"public","identifier":"foo"}} + // + // language=edn, format=ccf + // 130([137(27), [3, "foo"]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PublicPath type ID (27) + 0x18, 0x1b, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + ) + }) + + t.Run("Array of StoragePath", func(t *testing.T) { + storagePath, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + privatePath, err := cadence.NewPath(1, "bar") + require.NoError(t, err) + + publicPath, err := cadence.NewPath(1, "baz") + require.NoError(t, err) + + arrayOfPaths := cadence.NewArray([]cadence.Value{ + storagePath, + privatePath, + publicPath, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewStoragePathType())) + + testEncodeAndDecode( + t, + arrayOfPaths, + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},{"value":{"domain":"private","identifier":"bar"},"type":"Path"},{"value":{"domain":"public","identifier":"baz"},"type":"Path"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(137(26)), [[1, "foo"], [1, "bar"], [1, "baz"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // StoragePath type ID (26) + 0x18, 0x1a, + // array, 3 elements follow + 0x83, + // element 0: storage path + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // element 1: storage path + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // element 2: storage path + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + }, + ) + }) + + t.Run("Array of Path", func(t *testing.T) { + storagePath, err := cadence.NewPath(1, "foo") + require.NoError(t, err) + + privatePath, err := cadence.NewPath(2, "bar") + require.NoError(t, err) + + publicPath, err := cadence.NewPath(3, "baz") + require.NoError(t, err) + + arrayOfPaths := cadence.NewArray([]cadence.Value{ + storagePath, + privatePath, + publicPath, + }).WithType(cadence.NewVariableSizedArrayType(cadence.NewPathType())) + + testEncodeAndDecode( + t, + arrayOfPaths, + []byte{ // language=json, format=json-cdc + // {"value":[{"value":{"domain":"storage","identifier":"foo"},"type":"Path"},{"value":{"domain":"private","identifier":"bar"},"type":"Path"},{"value":{"domain":"public","identifier":"baz"},"type":"Path"}],"type":"Array"} + // + // language=edn, format=ccf + // 130([139(137(24)), [130([137(26), [1, "foo"]]), 130([137(28), [2, "bar"]]), 130([137(27), [3, "baz"]])]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Path type ID (24) + 0x18, 0x18, + // array, 3 elements follow + 0x83, + // element 0: storage path + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // StoragePath type ID (26) + 0x18, 0x1a, + // array, 2 elements follow + 0x82, + // 1 + 0x01, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // element 1: private path + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PrivatePath type ID (28) + 0x18, 0x1c, + // array, 2 elements follow + 0x82, + // 2 + 0x02, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // element 2: public path + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // PublicPath type ID (27) + 0x18, 0x1b, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // string, 3 bytes follow + 0x63, + // baz + 0x62, 0x61, 0x7a, + }, + ) + }) +} + +func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { + + test := func(testCase encodeTest) { + + t.Run(testCase.name, func(t *testing.T) { + + t.Parallel() + + if testCase.expectedVal == nil { + testEncodeAndDecode(t, testCase.val, testCase.expected) + } else { + testEncodeAndDecodeEx(t, testCase.val, testCase.expected, testCase.expectedVal) + } + }) + } + + for _, testCase := range tests { + test(testCase) + } +} + +func TestDecodeInvalidType(t *testing.T) { + + t.Parallel() + + t.Run("empty type", func(t *testing.T) { + t.Parallel() + + encodedData := []byte{ + // language=json, format=json-cdc + // { "type":"Struct", "value":{ "id":"", "fields":[] } } + // + // language=edn, format=ccf + // 129([[160([h'', "", []])], [136(h''), []]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "" + // 0 field + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 0 bytes follow + 0x60, + // fields + // array, 0 items follow + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + } + _, err := ccf.Decode(nil, encodedData) + require.Error(t, err) + assert.Equal(t, "ccf: failed to decode: invalid type ID for built-in: ``", err.Error()) + }) + + t.Run("invalid type ID", func(t *testing.T) { + t.Parallel() + + encodedData := []byte{ + // language=json, format=json-cdc + // { "type":"Struct", "value":{ "id":"I", "fields":[] } } + // + // language=edn, format=ccf + // 129([[160([h'', "I.Foo", []])], [136(h''), []]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definition + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "I" + // 0 field + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 1 bytes follow + 0x61, + // I + 0x49, + // fields + // array, 0 items follow + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + } + _, err := ccf.Decode(nil, encodedData) + require.Error(t, err) + assert.Equal(t, "ccf: failed to decode: invalid type ID `I`: invalid identifier location type ID: missing location", err.Error()) + }) + + t.Run("unknown location prefix", func(t *testing.T) { + t.Parallel() + + encodedData := []byte{ + // language=json, format=json-cdc + // { "type":"Struct", "value":{ "id":"N.PublicKey", "fields":[] } } + // + // language=edn, format=ccf + // 129([[160([h'', "N.PublicKey", []])], [136(h''), []]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "N.PublicKey" + // 0 field + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 11 bytes follow + 0x6b, + // N.PublicKey + 0x4e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + // fields + // array, 0 items follow + 0x80, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 0 items follow + 0x80, + } + _, err := ccf.Decode(nil, encodedData) + require.Error(t, err) + assert.Equal(t, "ccf: failed to decode: invalid type ID for built-in: `N.PublicKey`", err.Error()) + }) +} + +func testEncodeAndDecode(t *testing.T, val cadence.Value, expectedCBOR []byte) { + actualCBOR := testEncode(t, val, expectedCBOR) + testDecode(t, actualCBOR, val) +} + +// testEncodeAndDecodeEx is used when val != expectedVal because of deterministic encoding. +func testEncodeAndDecodeEx(t *testing.T, val cadence.Value, expectedCBOR []byte, expectedVal cadence.Value) { + actualCBOR := testEncode(t, val, expectedCBOR) + testDecode(t, actualCBOR, expectedVal) +} + +func testEncode(t *testing.T, val cadence.Value, expectedCBOR []byte) (actualCBOR []byte) { + actualCBOR, err := ccf.Encode(val) + require.NoError(t, err) + assert.True(t, bytes.Equal(expectedCBOR, actualCBOR), fmt.Sprintf("actual: 0x%x", actualCBOR)) + return actualCBOR +} + +func testDecode(t *testing.T, actualCBOR []byte, expectedVal cadence.Value) { + decodedVal, err := ccf.Decode(nil, actualCBOR) + require.NoError(t, err) + assert.Equal( + t, + cadence.ValueWithCachedTypeID(expectedVal), + cadence.ValueWithCachedTypeID(decodedVal), + ) +} + +var resourceStructType = &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.StringType{}, + }, + { + Identifier: "b", + Type: fooResourceType, + }, + }, +} + +var fooResourceType = &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + }, +} + +var foooResourceTypeWithAbstractField = &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Fooo", + Fields: []cadence.Field{ + { + Identifier: "bar", + Type: cadence.IntType{}, + }, + { + Identifier: "baz", + Type: cadence.AnyStructType{}, + }, + }, +} + +func TestEncodeBuiltinComposites(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + typ cadence.Type + encoded []byte + }{ + { + name: "Struct", + typ: &cadence.StructType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Struct","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 208([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "StructInterface", + typ: &cadence.StructInterfaceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"StructInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 224([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Resource", + typ: &cadence.ResourceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Resource","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 209([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "ResourceInterface", + typ: &cadence.ResourceInterfaceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"ResourceInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 225([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagResourceInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Contract", + typ: &cadence.ContractType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Contract","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 211([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "ContractInterface", + typ: &cadence.ContractInterfaceType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"ContractInterface","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 226([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagContractInterfaceTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Enum", + typ: &cadence.EnumType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Enum","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 212([h'', "Foo", null, [], []])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEnumTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 0 elements follow + 0x80, + }, + }, + { + name: "Event", + typ: &cadence.EventType{ + Location: nil, + QualifiedIdentifier: "Foo", + }, + encoded: []byte{ // language=json, format=json-cdc + // {"type":"Type","value":{"staticType":{"kind":"Event","typeID":"Foo","fields":[],"initializers":[],"type":""}}} + // + // language=edn, format=ccf + // 130([137(41), 210([h'', "Foo", null, [], [[]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagEventTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 3 bytes follow + 0x63, + // Foo + 0x46, 0x6f, 0x6f, + // type (nil for event) + 0xf6, + // fields + // array, 0 element follows + 0x80, + // initializers + // array, 1 elements follow + 0x81, + // array, 0 element follow + 0x80, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + typeValue := cadence.NewTypeValue(test.typ) + testEncode(t, typeValue, test.encoded) + }) + } +} + +func TestExportFunctionValue(t *testing.T) { + + t.Parallel() + + ty := &cadence.ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + Fields: []cadence.Field{ + { + Identifier: "foo", + }, + }, + } + + ty.Fields[0].Type = &cadence.OptionalType{ + Type: ty, + } + + testEncode( + t, + cadence.Function{ + FunctionType: (&cadence.FunctionType{ + Parameters: []cadence.Parameter{}, + ReturnType: cadence.VoidType{}, + }), + }, + []byte{ // language=json, format=json-cdc + // {"value":{"functionType":{"kind":"Function","typeParameters":[],"parameters":[],"return":{"kind":"Void"}}},"type":"Function"} + // + // language=edn, format=ccf + // 130([137(51), [[], [], 185(50)]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Function type ID (51) + 0x18, 0x33, + // array, 3 elements follow + 0x83, + // element 0: type parameters + 0x80, + // element 1: parameters + // array, 0 element + 0x80, + // element 2: return type + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Void type ID (50) + 0x18, 0x32, + }, + ) +} + +func TestDeployedEvents(t *testing.T) { + var tests = []struct { + name string + event cadence.Event + expectedCBOR []byte + }{ + { + name: "FlowFees.FeesDeducted", + event: createFlowFeesFeesDeductedEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.f919ee77447b7497.FlowFees.FeesDeducted","fields":[{"value":{"value":"0.01797293","type":"UFix64"},"name":"amount"},{"value":{"value":"1.00000000","type":"UFix64"},"name":"inclusionEffort"},{"value":{"value":"0.00360123","type":"UFix64"},"name":"executionEffort"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.f919ee77447b7497.FlowFees.FeesDeducted", [["amount", 137(23)], ["executionEffort", 137(23)], ["inclusionEffort", 137(23)]]])], [136(h''), [1797293, 360123, 100000000]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.f919ee77447b7497.FlowFees.FeesDeducted" + // 3 fields: [["amount", type(ufix64)], ["executionEffort", type(ufix64)], ["inclusionEffort", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 elements follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 40 bytes follow + 0x78, 0x28, + // A.f919ee77447b7497.FlowFees.FeesDeducted + 0x41, 0x2e, 0x66, 0x39, 0x31, 0x39, 0x65, 0x65, 0x37, 0x37, 0x34, 0x34, 0x37, 0x62, 0x37, 0x34, 0x39, 0x37, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x65, 0x65, 0x73, 0x2e, 0x46, 0x65, 0x65, 0x73, 0x44, 0x65, 0x64, 0x75, 0x63, 0x74, 0x65, 0x64, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 items follow + 0x82, + // text, 6 bytes follow + 0x66, + // amount + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 items follow + 0x82, + // text, 15 bytes follow + 0x6f, + // executionEffort + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 2 + // array, 2 items follow + 0x82, + // text, 15 bytes follow + 0x6f, + // inclusionEffort + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // 1797293 + 0x1a, 0x00, 0x1b, 0x6c, 0xad, + // 360123 + 0x1a, 0x00, 0x05, 0x7e, 0xbb, + // 100000000 + 0x1a, 0x05, 0xf5, 0xe1, 0x00, + }, + }, + { + name: "FlowFees.TokensWithdrawn", + event: createFlowFeesTokensWithdrawnEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.f919ee77447b7497.FlowFees.TokensWithdrawn","fields":[{"value":{"value":"53.04112895","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.f919ee77447b7497.FlowFees.TokensWithdrawn", [["amount", 137(23)]]])], [136(h''), [5304112895]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.f919ee77447b7497.FlowFees.TokensWithdrawn" + // 1 field: [["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 43 bytes follow + 0x78, 0x2b, + // "A.f919ee77447b7497.FlowFees.TokensWithdrawn" + 0x41, 0x2e, 0x66, 0x39, 0x31, 0x39, 0x65, 0x65, 0x37, 0x37, 0x34, 0x34, 0x37, 0x62, 0x37, 0x34, 0x39, 0x37, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x65, 0x65, 0x73, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6e, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 5304112895 + 0x1b, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x26, 0x56, 0xff, + }, + }, + { + name: "FlowIDTableStaking.DelegatorRewardsPaid", + event: createFlowIDTableStakingDelegatorRewardsPaidEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid","fields":[{"value":{"value":"e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb","type":"String"},"name":"nodeID"},{"value":{"value":"92","type":"UInt32"},"name":"delegatorID"},{"value":{"value":"4.38760261","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid", [["amount", 137(23)], ["nodeID", 137(1)], ["delegatorID", 137(14)]]])], [136(h''), [438760261, "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb", 92]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid" + // 3 field: [["amount", type(ufix64)], ["nodeID", type(string)], ["delegatorID", type(uint32)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 58 bytes follow + 0x78, 0x3a, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x50, 0x61, 0x69, 0x64, + // fields + // array, 3 items follow + 0x83, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "nodeID" + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // field 2 + // array, 2 element follows + 0x82, + // text, 11 bytes follow + 0x6b, + // "delegatorID" + 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x44, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UInt32 type ID (14) + 0x0e, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 3 items follow + 0x83, + // 438760261 + 0x1a, 0x1a, 0x26, 0xf3, 0x45, + // text, 64 bytes follow + 0x78, 0x40, + // "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb" + 0x65, 0x35, 0x32, 0x63, 0x62, 0x63, 0x64, 0x38, 0x32, 0x35, 0x65, 0x33, 0x32, 0x38, 0x61, 0x63, 0x61, 0x63, 0x38, 0x64, 0x62, 0x36, 0x62, 0x63, 0x62, 0x64, 0x63, 0x62, 0x62, 0x36, 0x65, 0x37, 0x37, 0x32, 0x34, 0x38, 0x36, 0x32, 0x63, 0x38, 0x62, 0x38, 0x39, 0x62, 0x30, 0x39, 0x64, 0x38, 0x35, 0x65, 0x64, 0x63, 0x63, 0x66, 0x34, 0x31, 0x66, 0x66, 0x39, 0x39, 0x38, 0x31, 0x65, 0x62, + // 92 + 0x18, 0x5c, + }, + }, + { + name: "FlowIDTableStaking.EpochTotalRewardsPaid", + event: createFlowIDTableStakingEpochTotalRewardsPaidEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid","fields":[{"value":{"value":"1316543.00000000","type":"UFix64"},"name":"total"},{"value":{"value":"53.04112895","type":"UFix64"},"name":"fromFees"},{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"minted"},{"value":{"value":"6.04080767","type":"UFix64"},"name":"feesBurned"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid", [["total", 137(23)], ["minted", 137(23)], ["fromFees", 137(23)], ["feesBurned", 137(23)]]])], [136(h''), [131654300000000, 131648995887105, 5304112895, 604080767]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid" + // 4 field: [["total", type(ufix64)], ["minted", type(ufix64)], ["fromFees", type(ufix64)], ["feesBurned", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 59 bytes follow + 0x78, 0x3b, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x50, 0x61, 0x69, 0x64, + // fields + // array, 4 items follow + 0x84, + // field 0 + // array, 2 element follows + 0x82, + // text, 5 bytes follow + 0x65, + // "total" + 0x74, 0x6f, 0x74, 0x61, 0x6c, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "minted" + 0x6d, 0x69, 0x6e, 0x74, 0x65, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 2 + // array, 2 element follows + 0x82, + // text, 8 bytes follow + 0x68, + // "fromFees" + 0x66, 0x72, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x73, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 3 + // array, 2 element follows + 0x82, + // text, 10 bytes follow + 0x6a, + // "feesBurned" + 0x66, 0x65, 0x65, 0x73, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 4 items follow + 0x84, + // 131654300000000 + 0x1b, 0x00, 0x00, 0x77, 0xbd, 0x27, 0xc8, 0xdf, 0x00, + // 131648995887105 + 0x1b, 0x00, 0x00, 0x77, 0xbb, 0xeb, 0xa2, 0x88, 0x01, + // 5304112895 + 0x1b, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x26, 0x56, 0xff, + // 604080767 + 0x1a, 0x24, 0x01, 0x8a, 0x7f, + }, + }, + { + name: "FlowIDTableStaking.NewWeeklyPayout", + event: createFlowIDTableStakingNewWeeklyPayoutEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout","fields":[{"value":{"value":"1317778.00000000","type":"UFix64"},"name":"newPayout"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout", [["newPayout", 137(23)]]])], [136(h''), [131777800000000]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout" + // 1 field: [["newPayout", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 53 bytes follow + 0x78, 0x35, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x4e, 0x65, 0x77, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 element follows + 0x82, + // text, 9 bytes follow + 0x69, + // "newPayout" + 0x6e, 0x65, 0x77, 0x50, 0x61, 0x79, 0x6f, 0x75, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 131777800000000 + 0x1b, 0x00, 0x00, 0x77, 0xd9, 0xe8, 0xf5, 0x52, 0x00, + }, + }, + { + name: "FlowIDTableStaking.RewardsPaid", + event: createFlowIDTableStakingRewardsPaidEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid","fields":[{"value":{"value":"e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb","type":"String"},"name":"nodeID"},{"value":{"value":"1745.49955740","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid", [["amount", 137(23)], ["nodeID", 137(1)]]])], [136(h''), [174549955740, "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid" + // 2 field: [["amount", type(ufix64)], ["nodeID", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 49 bytes follow + 0x78, 0x31, + // "A.8624b52f9ddcd04a.FlowIDTableStaking.RewardsPaid" + 0x41, 0x2e, 0x38, 0x36, 0x32, 0x34, 0x62, 0x35, 0x32, 0x66, 0x39, 0x64, 0x64, 0x63, 0x64, 0x30, 0x34, 0x61, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x44, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x50, 0x61, 0x69, 0x64, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "nodeID" + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // 174549955740 + 0x1b, 0x00, 0x00, 0x00, 0x28, 0xa3, 0xfc, 0xf4, 0x9c, + // string, 64 bytes follow + 0x78, 0x40, + // "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb" + 0x65, 0x35, 0x32, 0x63, 0x62, 0x63, 0x64, 0x38, 0x32, 0x35, 0x65, 0x33, 0x32, 0x38, 0x61, 0x63, 0x61, 0x63, 0x38, 0x64, 0x62, 0x36, 0x62, 0x63, 0x62, 0x64, 0x63, 0x62, 0x62, 0x36, 0x65, 0x37, 0x37, 0x32, 0x34, 0x38, 0x36, 0x32, 0x63, 0x38, 0x62, 0x38, 0x39, 0x62, 0x30, 0x39, 0x64, 0x38, 0x35, 0x65, 0x64, 0x63, 0x63, 0x66, 0x34, 0x31, 0x66, 0x66, 0x39, 0x39, 0x38, 0x31, 0x65, 0x62, + }, + }, + { + name: "FlowToken.TokensDeposited with nil receiver", + event: createFlowTokenTokensDepositedEventNoReceiver(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensDeposited","fields":[{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"amount"},{"value":{"value":null,"type":"Optional"},"name":"to"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensDeposited", [["to", 138(137(3))], ["amount", 137(23)]]])], [136(h''), [null, 131648995887105]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensDeposited" + // 2 field: [["to", type(optional(address))], ["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 44 bytes follow + 0x78, 0x2c, + // "A.1654653399040a61.FlowToken.TokensDeposited" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 2 bytes follow + 0x62, + // "to" + 0x74, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // null + 0xf6, + // 131648995887105 + 0x1b, 0x00, 0x00, 0x77, 0xbb, 0xeb, 0xa2, 0x88, 0x01, + }, + }, + { + name: "FlowToken.TokensDeposited", + event: createFlowTokenTokensDepositedEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensDeposited","fields":[{"value":{"value":"1745.49955740","type":"UFix64"},"name":"amount"},{"value":{"value":{"value":"0x8624b52f9ddcd04a","type":"Address"},"type":"Optional"},"name":"to"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensDeposited", [["to", 138(137(3))], ["amount", 137(23)]]])], [136(h''), [h'8624B52F9DDCD04A', 174549955740]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensDeposited" + // 2 field: [["to", type(optional(address))], ["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 44 bytes follow + 0x78, 0x2c, + // "A.1654653399040a61.FlowToken.TokensDeposited" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 2 bytes follow + 0x62, + // "to" + 0x74, 0x6f, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // bytes, 8 bytes follow + 0x48, + // 0x8624b52f9ddcd04a + 0x86, 0x24, 0xb5, 0x2f, 0x9d, 0xdc, 0xd0, 0x4a, + // 174549955740 + 0x1b, 0x00, 0x00, 0x00, 0x28, 0xa3, 0xfc, 0xf4, 0x9c, + }, + }, + { + name: "FlowToken.TokensMinted", + event: createFlowTokenTokensMintedEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensMinted","fields":[{"value":{"value":"1316489.95887105","type":"UFix64"},"name":"amount"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensMinted", [["amount", 137(23)]]])], [136(h''), [131648995887105]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensMinted" + // 1 field: [["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 41 bytes follow + 0x78, 0x29, + // "A.1654653399040a61.FlowToken.TokensMinted" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x4d, 0x69, 0x6e, 0x74, 0x65, 0x64, + // fields + // array, 1 items follow + 0x81, + // field 0 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // 131648995887105 + 0x1b, 0x00, 0x00, 0x77, 0xbb, 0xeb, 0xa2, 0x88, 0x01, + }, + }, + { + name: "FlowToken.TokensWithdrawn", + event: createFlowTokenTokensWithdrawnEvent(), + expectedCBOR: []byte{ + // language=json, format=json-cdc + // {"value":{"id":"A.1654653399040a61.FlowToken.TokensWithdrawn","fields":[{"value":{"value":"53.04112895","type":"UFix64"},"name":"amount"},{"value":{"value":{"value":"0xf919ee77447b7497","type":"Address"},"type":"Optional"},"name":"from"}]},"type":"Event"} + // + // language=edn, format=ccf + // 129([[162([h'', "A.1654653399040a61.FlowToken.TokensWithdrawn", [["from", 138(137(3))], ["amount", 137(23)]]])], [136(h''), [h'F919EE77447B7497', 5304112895]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 element follows + 0x82, + // element 0: type definitions + // array, 1 element follows + 0x81, + // event type: + // id: []byte{} + // cadence-type-id: "A.1654653399040a61.FlowToken.TokensWithdrawn" + // 2 field: [["from", type(optional(address))], ["amount", type(ufix64)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 element follows + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 44 bytes follow + 0x78, 0x2c, + // "A.1654653399040a61.FlowToken.TokensWithdrawn" + 0x41, 0x2e, 0x31, 0x36, 0x35, 0x34, 0x36, 0x35, 0x33, 0x33, 0x39, 0x39, 0x30, 0x34, 0x30, 0x61, 0x36, 0x31, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6e, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 element follows + 0x82, + // text, 4 bytes follow + 0x64, + // "from" + 0x66, 0x72, 0x6f, 0x6d, + // tag + 0xd8, ccf.CBORTagOptionalType, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Address type ID (3) + 0x03, + // field 1 + // array, 2 element follows + 0x82, + // text, 6 bytes follow + 0x66, + // "amount" + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + // tag + 0xd8, ccf.CBORTagSimpleType, + // UFix64 type ID (23) + 0x17, + + // element 1: type and value + // array, 2 element follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // bytes, 8 bytes follow + 0x48, + // 0xf919ee77447b7497 + 0xf9, 0x19, 0xee, 0x77, 0x44, 0x7b, 0x74, 0x97, + // 5304112895 + 0x1b, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x26, 0x56, 0xff, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Encode Cadence value to CCF + actualCBOR, err := ccf.Encode(tt.event) + require.NoError(t, err) + require.Equal(t, tt.expectedCBOR, actualCBOR) + + // Decode CCF to Cadence value + decodedEvent, err := ccf.Decode(nil, actualCBOR) + require.NoError(t, err) + + // Test original event and decoded events are equal even if + // fields are ordered differently due to deterministic encoding. + testEventEquality(t, tt.event, decodedEvent.(cadence.Event)) + }) + } +} + +func newFlowFeesFeesDeductedEventType() *cadence.EventType { + // pub event FeesDeducted(amount: UFix64, inclusionEffort: UFix64, executionEffort: UFix64) + + address, _ := common.HexToAddress("f919ee77447b7497") + location := common.NewAddressLocation(nil, address, "FlowFees") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowFees.FeesDeducted", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "inclusionEffort", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "executionEffort", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowFeesFeesDeductedEvent() cadence.Event { + /* + A.f919ee77447b7497.FlowFees.FeesDeducted + { + "amount": "0.01797293", + "inclusionEffort": "1.00000000", + "executionEffort": "0.00360123" + } + */ + amount, _ := cadence.NewUFix64("0.01797293") + inclusionEffort, _ := cadence.NewUFix64("1.00000000") + executionEffort, _ := cadence.NewUFix64("0.00360123") + + return cadence.NewEvent( + []cadence.Value{amount, inclusionEffort, executionEffort}, + ).WithType(newFlowFeesFeesDeductedEventType()) +} + +func newFlowFeesTokensWithdrawnEventType() *cadence.EventType { + // pub event TokensWithdrawn(amount: UFix64) + + address, _ := common.HexToAddress("f919ee77447b7497") + location := common.NewAddressLocation(nil, address, "FlowFees") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowFees.TokensWithdrawn", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowFeesTokensWithdrawnEvent() cadence.Event { + /* + A.f919ee77447b7497.FlowFees.TokensWithdrawn + { + "amount": "53.04112895" + } + */ + amount, _ := cadence.NewUFix64("53.04112895") + + return cadence.NewEvent( + []cadence.Value{amount}, + ).WithType(newFlowFeesTokensWithdrawnEventType()) +} + +func newFlowTokenTokensDepositedEventType() *cadence.EventType { + // pub event TokensDeposited(amount: UFix64, to: Address?) + + address, _ := common.HexToAddress("1654653399040a61") + location := common.NewAddressLocation(nil, address, "FlowToken") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowToken.TokensDeposited", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "to", + Type: &cadence.OptionalType{ + Type: cadence.NewAddressType(), + }, + }, + }, + } +} + +func createFlowTokenTokensDepositedEventNoReceiver() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensDeposited + { + "amount": "1316489.95887105", + "to": null + } + */ + amount, _ := cadence.NewUFix64("1316489.95887105") + to := cadence.NewOptional(nil) + + return cadence.NewEvent( + []cadence.Value{amount, to}, + ).WithType(newFlowTokenTokensDepositedEventType()) +} + +func createFlowTokenTokensDepositedEvent() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensDeposited + { + "amount": "1745.49955740", + "to": "0x8624b52f9ddcd04a" + } + */ + addressBytes, _ := hex.DecodeString("8624b52f9ddcd04a") + + amount, _ := cadence.NewUFix64("1745.49955740") + to := cadence.NewOptional(cadence.BytesToAddress(addressBytes)) + + return cadence.NewEvent( + []cadence.Value{amount, to}, + ).WithType(newFlowTokenTokensDepositedEventType()) +} + +func newFlowTokenTokensMintedEventType() *cadence.EventType { + // pub event TokensMinted(amount: UFix64) + + address, _ := common.HexToAddress("1654653399040a61") + location := common.NewAddressLocation(nil, address, "FlowToken") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowToken.TokensMinted", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowTokenTokensMintedEvent() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensMinted + { + "amount": "1316489.95887105" + } + */ + amount, _ := cadence.NewUFix64("1316489.95887105") + + return cadence.NewEvent( + []cadence.Value{amount}, + ).WithType(newFlowTokenTokensMintedEventType()) +} + +func newFlowTokenTokensWithdrawnEventType() *cadence.EventType { + // pub event TokensWithdrawn(amount: UFix64, from: Address?) + + address, _ := common.HexToAddress("1654653399040a61") + location := common.NewAddressLocation(nil, address, "FlowToken") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowToken.TokensWithdrawn", + Fields: []cadence.Field{ + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "from", + Type: &cadence.OptionalType{ + Type: cadence.NewAddressType(), + }, + }, + }, + } +} + +func createFlowTokenTokensWithdrawnEvent() cadence.Event { + /* + A.1654653399040a61.FlowToken.TokensWithdrawn + { + "amount": "53.04112895", + "from": "0xf919ee77447b7497" + } + */ + addressBytes, _ := hex.DecodeString("f919ee77447b7497") + + amount, _ := cadence.NewUFix64("53.04112895") + to := cadence.NewOptional(cadence.BytesToAddress(addressBytes)) + + return cadence.NewEvent( + []cadence.Value{amount, to}, + ).WithType(newFlowTokenTokensWithdrawnEventType()) +} + +func newFlowIDTableStakingDelegatorRewardsPaidEventType() *cadence.EventType { + // pub event DelegatorRewardsPaid(nodeID: String, delegatorID: UInt32, amount: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.DelegatorRewardsPaid", + Fields: []cadence.Field{ + { + Identifier: "nodeID", + Type: cadence.StringType{}, + }, + { + Identifier: "delegatorID", + Type: cadence.UInt32Type{}, + }, + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingDelegatorRewardsPaidEvent() cadence.Event { + /* + A.8624b52f9ddcd04a.FlowIDTableStaking.DelegatorRewardsPaid + { + "nodeID": "e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb", + "delegatorID": 92, + "amount": "4.38760261" + } + */ + nodeID := cadence.String("e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb") + delegatorID := cadence.UInt32(92) + amount, _ := cadence.NewUFix64("4.38760261") + + return cadence.NewEvent( + []cadence.Value{nodeID, delegatorID, amount}, + ).WithType(newFlowIDTableStakingDelegatorRewardsPaidEventType()) +} + +func newFlowIDTableStakingEpochTotalRewardsPaidEventType() *cadence.EventType { + // pub event EpochTotalRewardsPaid(total: UFix64, fromFees: UFix64, minted: UFix64, feesBurned: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.EpochTotalRewardsPaid", + Fields: []cadence.Field{ + { + Identifier: "total", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "fromFees", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "minted", + Type: cadence.UFix64Type{}, + }, + { + Identifier: "feesBurned", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingEpochTotalRewardsPaidEvent() cadence.Event { + /* + A.8624b52f9ddcd04a.FlowIDTableStaking.EpochTotalRewardsPaid + { + "total": "1316543.00000000", + "fromFees": "53.04112895", + "minted": "1316489.95887105", + "feesBurned": "6.04080767" + } + */ + total, _ := cadence.NewUFix64("1316543.00000000") + fromFees, _ := cadence.NewUFix64("53.04112895") + minted, _ := cadence.NewUFix64("1316489.95887105") + feesBurned, _ := cadence.NewUFix64("6.04080767") + + return cadence.NewEvent( + []cadence.Value{total, fromFees, minted, feesBurned}, + ).WithType(newFlowIDTableStakingEpochTotalRewardsPaidEventType()) +} + +func newFlowIDTableStakingNewWeeklyPayoutEventType() *cadence.EventType { + // pub event NewWeeklyPayout(newPayout: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.NewWeeklyPayout", + Fields: []cadence.Field{ + { + Identifier: "newPayout", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingNewWeeklyPayoutEvent() cadence.Event { + /* + A.8624b52f9ddcd04a.FlowIDTableStaking.NewWeeklyPayout + { + "newPayout": "1317778.00000000" + } + */ + newPayout, _ := cadence.NewUFix64("1317778.00000000") + + return cadence.NewEvent( + []cadence.Value{newPayout}, + ).WithType(newFlowIDTableStakingNewWeeklyPayoutEventType()) +} + +func newFlowIDTableStakingRewardsPaidEventType() *cadence.EventType { + // pub event RewardsPaid(nodeID: String, amount: UFix64) + + address, _ := common.HexToAddress("8624b52f9ddcd04a") + location := common.NewAddressLocation(nil, address, "FlowIDTableStaking") + + return &cadence.EventType{ + Location: location, + QualifiedIdentifier: "FlowIDTableStaking.RewardsPaid", + Fields: []cadence.Field{ + { + Identifier: "nodeID", + Type: cadence.StringType{}, + }, + { + Identifier: "amount", + Type: cadence.UFix64Type{}, + }, + }, + } +} + +func createFlowIDTableStakingRewardsPaidEvent() cadence.Event { + nodeID, _ := cadence.NewString("e52cbcd825e328acac8db6bcbdcbb6e7724862c8b89b09d85edccf41ff9981eb") + amount, _ := cadence.NewUFix64("1745.49955740") + + return cadence.NewEvent( + []cadence.Value{nodeID, amount}, + ).WithType(newFlowIDTableStakingRewardsPaidEventType()) +} + +func testEventEquality(t *testing.T, event1 cadence.Event, event2 cadence.Event) { + require.True(t, event1.Type().Equal(event2.Type())) + require.Equal(t, len(event1.Fields), len(event2.Fields)) + require.Equal(t, len(event1.EventType.Fields), len(event2.EventType.Fields)) + + for i, event1FieldType := range event1.EventType.Fields { + + foundField := false + + for j, event2FieldType := range event2.EventType.Fields { + if event1FieldType.Identifier == event2FieldType.Identifier { + require.Equal(t, event1.Fields[i], event2.Fields[j]) + foundField = true + break + } + } + + require.True(t, foundField) + } +} + +func TestDecodeTruncatedData(t *testing.T) { + data, err := ccf.Encode(createFlowTokenTokensWithdrawnEvent()) + require.NoError(t, err) + + _, err = ccf.Decode(nil, data) + require.NoError(t, err) + + for i := len(data) - 1; i >= 0; i-- { + decodedVal, err := ccf.Decode(nil, data[:i]) + require.Nil(t, decodedVal) + require.Error(t, err) + } +} + +func TestDecodeInvalidData(t *testing.T) { + type testCase struct { + name string + data []byte + } + + testCases := []testCase{ + { + name: "nil", + data: nil, + }, + { + name: "empty", + data: []byte{}, + }, + { + name: "malformed CBOR data for potential OOM", + data: []byte{0x9b, 0x00, 0x00, 0x42, 0xfa, 0x42, 0xfa, 0x42, 0xfa, 0x42}, + }, + { + name: "mismatched type and value", + data: []byte{ + // language=edn, format=ccf + // 130([137(1), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // true + 0xf5, + }, + }, + { + name: "not found type definition", + data: []byte{ + // language=edn, format=ccf + // 130([136(h''), [1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "unreferenced type definition", + data: []byte{ + // language=edn, format=ccf + // 129([[162([h'', "S.test.FooEvent", [["a", 137(4)], ["b", 137(1)]]]), 160([h'1', "S.test.FooStruct", [["a", 137(4)], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 2 items follow + 0x82, + // event type: + // id: []byte{} + // cadence-type-id: "S.test.FooEvent" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagEventType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 15 bytes follow + 0x6f, + // S.test.FooEvent + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 2 fields: [["a", type(int)], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + }, + { + name: "nil type", + data: []byte{ + // language=edn, format=ccf + // 130([null, true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // nil + 0xf6, + // true + 0xf5, + }, + }, + { + name: "nil type definitions", + data: []byte{ + // language=edn, format=ccf + // 129(null, [137(0), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // nil + 0xf6, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + }, + { + name: "nil type definition", + data: []byte{ + // language=edn, format=ccf + // 129([null], [137(0), true]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // array, 1 items follow + 0x81, + // nil + 0xf6, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + }, + }, + { + name: "nil optional inner type", + data: []byte{ + // language=edn, format=ccf + // 130([138(null), null]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagOptionalType, + // nil + 0xf6, + // nil + 0xf6, + }, + }, + { + name: "nil element type in constant sized array", + data: []byte{ + // language=edn, format=ccf + // 130([140[1, null], [1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type constant-sized [1]nil + // tag + 0xd8, ccf.CBORTagConstsizedArrayType, + // array, 2 items follow + 0x82, + // number of elements + 0x01, + // nil + 0xf6, + // array data without inlined type definition + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil element type in variable sized array", + data: []byte{ + // language=edn, format=ccf + // 130([139(null), [1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type []nil + // tag + 0xd8, ccf.CBORTagVarsizedArrayType, + // null + 0xf6, + // array data without inlined type definition + // array, 1 items follow + 0x81, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil key type in dictionary type", + data: []byte{ + // language=edn, format=ccf + // 130([141([nil, 137(4)]), ["a", 1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[nil]int) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // null + 0xf6, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Int type ID (4) + 0x04, + // array data without inlined type definition + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil element type in dictionary type", + data: []byte{ + // language=edn, format=ccf + // 130([141([137(1), nil]), ["a", 1]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // type (map[int]nil) + // tag + 0xd8, ccf.CBORTagDictType, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + // null + 0xf6, + // array data without inlined type definition + // array, 2 items follow + 0x82, + // string, 1 bytes follow + 0x61, + // a + 0x61, + // tag (big num) + 0xc2, + // bytes, 1 bytes follow + 0x41, + // 1 + 0x01, + }, + }, + { + name: "nil composite field type", + data: []byte{ + // language=edn, format=ccf + // 129([[160([h'', "S.test.FooStruct", [["a", nil], ["b", 137(1)]]])], [136(h''), [1, "foo"]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // element 0: type definitions + // array, 1 items follow + 0x81, + // struct type: + // id: []byte{} + // cadence-type-id: "S.test.FooStruct" + // 2 fields: [["a", nil], ["b", type(string)]] + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // id + // bytes, 0 bytes follow + 0x40, + // cadence-type-id + // string, 16 bytes follow + 0x70, + // S.test.FooStruct + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 2 items follow + 0x82, + // field 0 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // a + 0x61, + // null + 0xf6, + // field 1 + // array, 2 items follow + 0x82, + // text, 1 bytes follow + 0x61, + // b + 0x62, + // tag + 0xd8, ccf.CBORTagSimpleType, + // String type ID (1) + 0x01, + + // element 1: type and value + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 bytes follow + 0x40, + // array, 2 items follow + 0x82, + // tag (big number) + 0xc2, + // bytes, 1 byte follow + 0x41, + // 1 + 0x01, + // String, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + }, + }, + { + name: "nil inner type in optional type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 186(null)]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagOptionalTypeValue, + // null + 0xf6, + }, + }, + { + name: "nil element type in constant sized array type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 188([3, null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagConstsizedArrayTypeValue, + // array, 2 elements follow + 0x82, + // 3 + 0x03, + // null + 0xf6, + }, + }, + { + name: "nil element type in variable sized array type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 187(null)]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagVarsizedArrayTypeValue, + // null + 0xf6, + }, + }, + { + name: "nil key type in dictionary type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 189([null, 185(1)])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, + // null + 0xf6, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + }, + { + name: "nil element type in dictionary type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 189([185(4), null])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagDictTypeValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // null + 0xf6, + }, + }, + { + name: "nil field type in struct type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", null]], [[["foo", "bar", 185(4)]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // null + 0xf6, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + }, + { + name: "nil initializer type in struct type value", + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 208([h'', "S.test.S", null, [["foo", 185(4)]], [[["foo", "bar", null]], [["qux", "baz", 185(1)]]]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 elements follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 elements follow + 0x85, + // bytes, 0 bytes follow + 0x40, + // string, 8 bytes follow + 0x68, + // S.test.So + 0x53, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, + // type (nil for struct) + 0xf6, + // fields + // array, 1 element follows + 0x81, + // array, 2 elements follow + 0x82, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // Int type (4) + 0x04, + // initializers + // array, 2 elements follow + 0x82, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // foo + 0x66, 0x6f, 0x6f, + // string, 3 bytes follow + 0x63, + // bar + 0x62, 0x61, 0x72, + // null + 0xf6, + // array, 1 element follows + 0x81, + // array, 3 elements follow + 0x83, + // string, 3 bytes follow + 0x63, + // qux + 0x71, 0x75, 0x78, + // string, 3 bytes follow + 0x63, + // bax + 0x62, 0x61, 0x7a, + // tag + 0xd8, ccf.CBORTagSimpleTypeValue, + // String type (1) + 0x01, + }, + }, + { + name: "null restriction in restricted type value", + // Data is generated by fuzzer. + data: []byte{ + // language=edn, format=ccf + // 130([137(41), 191([208([h'', "S.\ufffd0000000.000000", null, [], []]), [null]])]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Meta type ID (41) + 0x18, 0x29, + // tag + 0xd8, ccf.CBORTagRestrictedTypeValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagStructTypeValue, + // array, 5 items follow + 0x85, + // ccf type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 19 bytes follow + 0x73, + // "S.�0000000.000000" + 0x53, 0x2e, 0xef, 0xbf, 0xbd, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + // type + // nil + 0xf6, + // fields + // array, 0 item follows + 0x80, + // initializers + // array, 0 item follows + 0x80, + // restrictions + // array, 1 item follows + 0x81, + // nil + 0xf6, + }, + }, + { + name: "extraneous data", + data: []byte{ + // language=edn, format=ccf + // 130([137(0), true]), 0 + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // Bool type ID (0) + 0x00, + // true + 0xf5, + // extraneous data + 0x00, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + decodedVal, err := ccf.Decode(nil, tc.data) + require.Nil(t, decodedVal) + require.Error(t, err) + }) + } +} + +func TestEncodeValueOfRestrictedInterface(t *testing.T) { + + // Values and types are generated by fuzzer. + /* + // Type def + + struct OuterStruct { + var field: MiddleStruct + } + + struct MiddleStruct { + var field: AnyStruct{Interface} + } + + struct interface Interface {} + + struct InnerStruct: Interface {} // 'InnerStruct' conforms to 'Interface' + + // Value + + OuterStruct { + field: MiddleStruct { + field: InnerStruct{} // <-- here the value is the implementation, for the restricted type. + } + } + */ + + interfaceType := cadence.NewStructInterfaceType( + common.StringLocation("LocationA"), + "Interface", + nil, + nil, + ) + + middleStruct := cadence.NewStructType( + common.StringLocation("LocationB"), + "MiddleStruct", + []cadence.Field{ + { + Type: cadence.NewRestrictedType( + cadence.TheAnyStructType, []cadence.Type{interfaceType}), + Identifier: "field", + }, + }, + nil, + ) + + outerStructType := cadence.NewStructType( + common.StringLocation("LocationC"), + "OuterStruct", + []cadence.Field{ + { + Type: middleStruct, + Identifier: "field", + }, + }, + nil, + ) + + innerStructType := cadence.NewStructType( + common.StringLocation("LocationD"), + "InnerStruct", + []cadence.Field{}, + nil, + ) + + value := cadence.NewStruct([]cadence.Value{ + cadence.NewStruct([]cadence.Value{ + cadence.NewStruct([]cadence.Value{}).WithType(innerStructType), + }).WithType(middleStruct), + }).WithType(outerStructType) + + testEncodeAndDecode( + t, + value, + []byte{ // language=json, format=json-cdc + // {"value":{"id":"S.LocationC.OuterStruct","fields":[{"value":{"value":{"id":"S.LocationB.MiddleStruct","fields":[{"value":{"value":{"id":"S.LocationD.InnerStruct","fields":[]},"type":"Struct"},"name":"field"}]},"type":"Struct"},"name":"field"}]},"type":"Struct"} + // + // language=edn, format=ccf + // 129([[176([h'', "S.LocationA.Interface"]), 160([h'01', "S.LocationC.OuterStruct", [["field", 136(h'03')]]]), 160([h'02', "S.LocationD.InnerStruct", []]), 160([h'03', "S.LocationB.MiddleStruct", [["field", 143([137(39), [136(h'')]])]]])], [136(h'01'), [[130([136(h'02'), []])]]]]) + // + // language=cbor, format=ccf + // tag + 0xd8, ccf.CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + // array, 4 items follow + 0x84, + // tag + 0xd8, ccf.CBORTagStructInterfaceType, + // array, 2 items follow + 0x82, + // CCF type ID + // bytes, 0 byte follows + 0x40, + // cadence type ID + // text, 21 bytes follow + 0x75, + // "S.LocationA.Interface" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 items follow + 0x83, + // CCF type ID + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // text, 23 bytes follow + 0x77, + // "S.LocationC.OuterStruct" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x2e, 0x4f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 item follows + 0x81, + // array, 2 item follows + 0x82, + // text, 5 bytes follow + 0x65, + // "field" + 0x66, 0x69, 0x65, 0x6c, 0x64, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follow + 0x41, + // 3 + 0x03, + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 item follows + 0x83, + // CCF type ID + // bytes, 1 byte follow + 0x41, + // 2 + 0x02, + // Cadence type ID + // text, 23 bytes follow + 0x77, + // "S.LocationD.InnerStruct" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x2e, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 0 item follows + 0x80, + // tag + 0xd8, ccf.CBORTagStructType, + // array, 3 item follows + 0x83, + // CCF type ID + // bytes, 1 byte follow + 0x41, + // 3 + 0x03, + // Cadence type ID + // text, 24 bytes follow + 0x78, 0x18, + // "S.LocationB.MiddleStruct" + 0x53, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + // fields + // array, 1 item follows + 0x81, + // array, 2 item follows + 0x82, + // text, 5 bytes follow + 0x65, + // "field" + 0x66, 0x69, 0x65, 0x6c, 0x64, + // tag + 0xd8, ccf.CBORTagRestrictedType, + // array, 2 item follows + 0x82, + // tag + 0xd8, ccf.CBORTagSimpleType, + // AnyStruct type ID (39) + 0x18, 0x27, + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 0 byte follows + 0x40, + // array, 2 item follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 1 + 0x01, + // array, 1 item follows + 0x81, + // array, 1 item follows + 0x81, + // tag + 0xd8, ccf.CBORTagTypeAndValue, + // array, 2 item follows + 0x82, + // tag + 0xd8, ccf.CBORTagTypeRef, + // bytes, 1 byte follows + 0x41, + // 2 + 0x02, + // array, 0 item follows + 0x80, + }, + ) +} diff --git a/encoding/ccf/ccf_type_id.go b/encoding/ccf/ccf_type_id.go new file mode 100644 index 0000000000..7459624ed8 --- /dev/null +++ b/encoding/ccf/ccf_type_id.go @@ -0,0 +1,105 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "fmt" + "math/big" + + "github.com/onflow/cadence" +) + +// ccfTypeID represents CCF type ID. +type ccfTypeID uint64 + +func newCCFTypeID(b []byte) ccfTypeID { + return ccfTypeID(new(big.Int).SetBytes(b).Uint64()) +} + +func newCCFTypeIDFromUint64(i uint64) ccfTypeID { + return ccfTypeID(i) +} + +func (id ccfTypeID) Bytes() []byte { + return new(big.Int).SetUint64(uint64(id)).Bytes() +} + +func (id ccfTypeID) Equal(other ccfTypeID) bool { + return id == other +} + +// ccfTypeIDByCadenceType maps a Cadence type ID to a CCF type ID +// +// IMPORTANT: Don't use cadence.Type as map key because all Cadence composite/interface +// types are pointers, and different instance of the same type will be treated as +// different map key. +type ccfTypeIDByCadenceType map[string]ccfTypeID + +func (types ccfTypeIDByCadenceType) id(t cadence.Type) (ccfTypeID, error) { + id, ok := types[t.ID()] + if !ok { + return 0, fmt.Errorf("CCF type ID not found for type %s", t.ID()) + } + return id, nil +} + +type cadenceTypeByCCFTypeID struct { + types map[ccfTypeID]cadence.Type + referencedTypes map[ccfTypeID]struct{} +} + +func newCadenceTypeByCCFTypeID() *cadenceTypeByCCFTypeID { + return &cadenceTypeByCCFTypeID{ + types: make(map[ccfTypeID]cadence.Type), + referencedTypes: make(map[ccfTypeID]struct{}), + } +} + +func (ids *cadenceTypeByCCFTypeID) add(id ccfTypeID, typ cadence.Type) bool { + if ids.has(id) { + return false + } + ids.types[id] = typ + return true +} + +func (ids *cadenceTypeByCCFTypeID) reference(id ccfTypeID) { + ids.referencedTypes[id] = struct{}{} +} + +func (ids *cadenceTypeByCCFTypeID) typ(id ccfTypeID) (cadence.Type, error) { + t, ok := ids.types[id] + if !ok { + return nil, fmt.Errorf("type not found for CCF type ID %d", id) + } + return t, nil +} + +func (ids *cadenceTypeByCCFTypeID) has(id ccfTypeID) bool { + _, ok := ids.types[id] + return ok +} + +func (ids *cadenceTypeByCCFTypeID) count() int { + return len(ids.types) +} + +func (ids *cadenceTypeByCCFTypeID) hasUnreferenced() bool { + return len(ids.types) > len(ids.referencedTypes) +} diff --git a/encoding/ccf/ccf_type_id_test.go b/encoding/ccf/ccf_type_id_test.go new file mode 100644 index 0000000000..dd5c2fb08f --- /dev/null +++ b/encoding/ccf/ccf_type_id_test.go @@ -0,0 +1,146 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/tests/utils" +) + +func TestCCFTypeID(t *testing.T) { + + t.Parallel() + + testCases := []struct { + name string + input uint64 + encodedBytes []byte + }{ + {name: "min", input: 0, encodedBytes: []byte{}}, + {name: "42", input: 42, encodedBytes: []byte{0x2a}}, + {name: "256", input: 256, encodedBytes: []byte{0x01, 0x00}}, + {name: "max", input: math.MaxUint64, encodedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Create CCF type ID from uint64. + ccfID := newCCFTypeIDFromUint64(tc.input) + + // Encode CCF type ID to bytes. + encodedBytes := ccfID.Bytes() + require.Equal(t, tc.encodedBytes, encodedBytes) + + // Decode CCF type ID from bytes. + decodedCCFID := newCCFTypeID(encodedBytes) + require.Equal(t, ccfTypeID(tc.input), decodedCCFID) + + // Compare decoded CCF type ID with original. + require.True(t, ccfID.Equal(decodedCCFID)) + + // Compare modified CCF type ID with original. + if len(encodedBytes) == 0 { + encodedBytes = []byte{0x00} + } + encodedBytes[0] = ^encodedBytes[0] + require.False(t, ccfID.Equal(newCCFTypeID(encodedBytes))) + }) + } +} + +func TestCCFTypeIDByCadenceType(t *testing.T) { + + t.Parallel() + + // Create ccfTypeIDByCadenceType map + ccfIDs := make(ccfTypeIDByCadenceType) + + // Lookup non-existent CCF type ID. + _, err := ccfIDs.id(simpleStructType()) + require.Error(t, err) + + // Add entry. + ccfID := newCCFTypeIDFromUint64(1) + ccfIDs[simpleStructType().ID()] = ccfID + + // Lookup existing CCF type ID. + id, err := ccfIDs.id(simpleStructType()) + require.Equal(t, ccfID, id) + require.NoError(t, err) +} + +func TestCadenceTypeByCCFTypeID(t *testing.T) { + + t.Parallel() + + cadenceTypes := newCadenceTypeByCCFTypeID() + + // Add new entry. + newType := cadenceTypes.add(newCCFTypeIDFromUint64(0), simpleStructType()) + require.True(t, newType) + + // Add entry with duplicate CCF type ID. + newType = cadenceTypes.add(newCCFTypeIDFromUint64(0), simpleStructType2()) + require.False(t, newType) + + // Lookup existing cadence type. + typ, err := cadenceTypes.typ(newCCFTypeIDFromUint64(0)) + require.True(t, typ.Equal(simpleStructType())) + require.False(t, typ.Equal(simpleStructType2())) + require.NoError(t, err) + + // Lookup non-existent cadence type. + typ, err = cadenceTypes.typ(newCCFTypeIDFromUint64(1)) + require.Nil(t, typ) + require.Error(t, err) +} + +func simpleStructType() *cadence.StructType { + return &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + }, + } +} + +func simpleStructType2() *cadence.StructType { + return &cadence.StructType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooStruct2", + Fields: []cadence.Field{ + { + Identifier: "a", + Type: cadence.IntType{}, + }, + { + Identifier: "b", + Type: cadence.StringType{}, + }, + }, + } +} diff --git a/encoding/ccf/consts.go b/encoding/ccf/consts.go new file mode 100644 index 0000000000..ee01a62912 --- /dev/null +++ b/encoding/ccf/consts.go @@ -0,0 +1,165 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +// CCF uses CBOR tag numbers 128-255, which are unassigned by [IANA] +// (https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml). +// +// !!! *WARNING* !!! +// +// CCF Codec *MUST* comply with CCF Specifications. Relevant changes +// must be in sync between codec and specifications. +// +// Only add new tag number by: +// - replacing existing placeholders (`_`) with new tag number +// +// Only remove tag number by: +// - replace existing tag number with a placeholder `_` +// +// DO *NOT* REPLACE EXISTING TAG NUMBERS! +// DO *NOT* ADD NEW TAG NUMBERS IN BETWEEN! +// DO *NOT* APPEND NEW TAG NUMBERS AT END! +// +// By not appending tag numbers to the end, we have larger block of +// unused tag numbers if needed. Tag numbers in 128-255 are +// unassigned in CBOR, and we currently use 128-231. Since each +// group of tags in this range have reserved space available, +// there is no need to append new tag numbers in 232-255. + +const ( + // CBOR tag numbers (128-135) for root objects (131-135 are reserved) + CBORTagTypeDef = 128 + iota + CBORTagTypeDefAndValue + CBORTagTypeAndValue + _ + _ + _ + _ + _ + + // CBOR tag numbers (136-183) for types + // inline types (145-159 are reserved) + CBORTagTypeRef + CBORTagSimpleType + CBORTagOptionalType + CBORTagVarsizedArrayType + CBORTagConstsizedArrayType + CBORTagDictType + CBORTagReferenceType + CBORTagRestrictedType + CBORTagCapabilityType + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // composite types (165-175 are reserved) + CBORTagStructType + CBORTagResourceType + CBORTagEventType + CBORTagContractType + CBORTagEnumType + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // interface types (179-183 are reserved) + CBORTagStructInterfaceType + CBORTagResourceInterfaceType + CBORTagContractInterfaceType + _ + _ + _ + _ + _ + + // CBOR tag numbers (184-231) for type value + // non-composite and non-interface type values (194-207 are reserved) + CBORTagTypeValueRef + CBORTagSimpleTypeValue + CBORTagOptionalTypeValue + CBORTagVarsizedArrayTypeValue + CBORTagConstsizedArrayTypeValue + CBORTagDictTypeValue + CBORTagReferenceTypeValue + CBORTagRestrictedTypeValue + CBORTagCapabilityTypeValue + CBORTagFunctionTypeValue + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // composite type values (213-223 are reserved) + CBORTagStructTypeValue + CBORTagResourceTypeValue + CBORTagEventTypeValue + CBORTagContractTypeValue + CBORTagEnumTypeValue + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // interface type values (227-231 are reserved) + CBORTagStructInterfaceTypeValue + CBORTagResourceInterfaceTypeValue + CBORTagContractInterfaceTypeValue + _ + _ + _ + _ + _ +) diff --git a/encoding/ccf/decode.go b/encoding/ccf/decode.go new file mode 100644 index 0000000000..7d5dd0ba20 --- /dev/null +++ b/encoding/ccf/decode.go @@ -0,0 +1,2052 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "errors" + "fmt" + "math" + "math/big" + goRuntime "runtime" + + "github.com/fxamacker/cbor/v2" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" + cadenceErrors "github.com/onflow/cadence/runtime/errors" +) + +// CBORDecMode +// +// See https://github.com/fxamacker/cbor: +// "For best performance, reuse EncMode and DecMode after creating them." +// +// Security Considerations in Section 10 of RFC 8949 states: +// +// "Hostile input may be constructed to overrun buffers, to overflow or underflow integer arithmetic, +// or to cause other decoding disruption. CBOR data items might have lengths or sizes that are +// intentionally extremely large or too short. Resource exhaustion attacks might attempt to lure a +// decoder into allocating very big data items (strings, arrays, maps, or even arbitrary precision numbers) +// or exhaust the stack depth by setting up deeply nested items. Decoders need to have appropriate resource +// management to mitigate these attacks." +var CBORDecMode = func() cbor.DecMode { + decMode, err := cbor.DecOptions{ + IndefLength: cbor.IndefLengthForbidden, + IntDec: cbor.IntDecConvertNone, + MaxArrayElements: 20_000_000, // 20 MB is current grpc size limit so this is more than enough + MaxMapPairs: 20_000_000, // 20 MB is current grpc size limit so this is more than enough + MaxNestedLevels: math.MaxInt16, + }.DecMode() + if err != nil { + panic(err) + } + return decMode +}() + +// Decoder decodes CCF-encoded representations of Cadence values. +// Since CBOR security considerations apply to CCF, the CBOR +// codec used by CCF Decoder uses limits (e.g. MaxArrayElements, +// MaxMapPairs, MaxNestedLevels) specified by CBORDecMode. +type Decoder struct { + // CCF codec uses CBOR codec under the hood. + dec *cbor.StreamDecoder + gauge common.MemoryGauge +} + +// Decode returns a Cadence value decoded from its CCF-encoded representation. +// +// This function returns an error if the bytes represent CCF that is malformed, +// invalid, or does not comply with requirements in the CCF specification. +func Decode(gauge common.MemoryGauge, b []byte) (cadence.Value, error) { + dec := NewDecoder(gauge, b) + + v, err := dec.Decode() + if err != nil { + return nil, err + } + + if dec.dec.NumBytesDecoded() != len(b) { + return nil, cadenceErrors.NewDefaultUserError("ccf: failed to decode: decoded %d bytes, received %d bytes", dec.dec.NumBytesDecoded(), len(b)) + } + + return v, nil +} + +// NewDecoder initializes a Decoder that will decode CCF-encoded bytes from the +// given bytes. +func NewDecoder(gauge common.MemoryGauge, b []byte) *Decoder { + // NOTE: encoded data is not copied by decoder. + // CCF codec uses CBOR codec under the hood. + return &Decoder{ + dec: CBORDecMode.NewByteStreamDecoder(b), + gauge: gauge, + } +} + +// Decode reads CCF-encoded bytes and decodes them to a Cadence value. +// +// This function returns an error if the bytes represent CCF that is malformed, +// invalid, or does not comply with requirements in the CCF specification. +func (d *Decoder) Decode() (value cadence.Value, err error) { + // Capture panics that occur during decoding. + defer func() { + // Recover panic error if there is any. + if r := recover(); r != nil { + // Don't recover Go errors, internal errors, or non-errors. + switch r := r.(type) { + case goRuntime.Error, cadenceErrors.InternalError: + panic(r) + case error: + err = r + default: + panic(r) + } + } + + // Add context to error if there is any. + if err != nil { + err = cadenceErrors.NewDefaultUserError("ccf: failed to decode: %s", err) + } + }() + + // Decode top level message. + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return nil, err + } + + switch tagNum { + case CBORTagTypeDefAndValue: + // Decode ccf-typedef-and-value-message. + return d.decodeTypeDefAndValue() + + case CBORTagTypeAndValue: + // Decode ccf-type-and-value-message. + return d.decodeTypeAndValue(newCadenceTypeByCCFTypeID()) + + default: + return nil, fmt.Errorf( + "unsupported top level CCF message with CBOR tag number %d", + tagNum, + ) + } +} + +// decodeTypeDefAndValue decodes encoded ccf-typedef-and-value-message +// without tag number as +// language=CDDL +// ccf-typedef-and-value-message = +// +// ; cbor-tag-typedef-and-value +// #6.129([ +// typedef: composite-typedef, +// type-and-value: inline-type-and-value +// ]) +func (d *Decoder) decodeTypeDefAndValue() (cadence.Value, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: typedef + types, err := d.decodeTypeDefs() + if err != nil { + return nil, err + } + + // element 1: type and value + val, err := d.decodeTypeAndValue(types) + if err != nil { + return nil, err + } + + // Check if there is any unreferenced type definition. + if types.hasUnreferenced() { + return nil, errors.New("found unreferenced type definition") + } + + return val, nil +} + +// decodeTypeAndValue decodes encoded ccf-type-and-value-message +// without tag number as +// language=CDDL +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130(inline-type-and-value) +// +// inline-type-and-value = [ +// +// type: inline-type, +// value: value, +// +// ] +func (d *Decoder) decodeTypeAndValue(types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: inline-type + t, err := d.decodeInlineType(types) + if err != nil { + return nil, err + } + + // element 1: value + return d.decodeValue(t, types) +} + +// decodeValue decodes encoded value of type t. +// language=CDDL +// value = +// +// ccf-type-and-value-message +// / simple-value +// / optional-value +// / array-value +// / dict-value +// / composite-value +// / path-value +// / capability-value +// / function-value +// / type-value +// +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130([ +// type: inline-type, +// value: value +// ]) +// +// simple-value = +// +// void-value +// / bool-value +// / character-value +// / string-value +// / address-value +// / uint-value +// / uint8-value +// / uint16-value +// / uint32-value +// / uint64-value +// / uint128-value +// / uint256-value +// / int-value +// / int8-value +// / int16-value +// / int32-value +// / int64-value +// / int128-value +// / int256-value +// / word8-value +// / word16-value +// / word32-value +// / word64-value +// / fix64-value +// / ufix64-value +func (d *Decoder) decodeValue(t cadence.Type, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + if t == nil { + return nil, fmt.Errorf("unexpected nil type") + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // 'inline-type-and-value MUST NOT be used when type can be omitted as described + // in "Cadence Types and Values Encoding".' + // If type t for the value to be decoded is a concrete type (e.g. IntType), + // value MUST NOT be ccf-type-and-value-message. + + switch t := t.(type) { + case cadence.VoidType: + return d.decodeVoid() + + case *cadence.OptionalType: + return d.decodeOptional(t, types) + + case cadence.BoolType: + return d.decodeBool() + + case cadence.CharacterType: + return d.decodeCharacter() + + case cadence.StringType: + return d.decodeString() + + case cadence.AddressType: + return d.decodeAddress() + + case cadence.IntType: + return d.decodeInt() + + case cadence.Int8Type: + return d.decodeInt8() + + case cadence.Int16Type: + return d.decodeInt16() + + case cadence.Int32Type: + return d.decodeInt32() + + case cadence.Int64Type: + return d.decodeInt64() + + case cadence.Int128Type: + return d.decodeInt128() + + case cadence.Int256Type: + return d.decodeInt256() + + case cadence.UIntType: + return d.decodeUInt() + + case cadence.UInt8Type: + return d.decodeUInt8() + + case cadence.UInt16Type: + return d.decodeUInt16() + + case cadence.UInt32Type: + return d.decodeUInt32() + + case cadence.UInt64Type: + return d.decodeUInt64() + + case cadence.UInt128Type: + return d.decodeUInt128() + + case cadence.UInt256Type: + return d.decodeUInt256() + + case cadence.Word8Type: + return d.decodeWord8() + + case cadence.Word16Type: + return d.decodeWord16() + + case cadence.Word32Type: + return d.decodeWord32() + + case cadence.Word64Type: + return d.decodeWord64() + + case cadence.Fix64Type: + return d.decodeFix64() + + case cadence.UFix64Type: + return d.decodeUFix64() + + case *cadence.VariableSizedArrayType: + return d.decodeArray(t, false, 0, types) + + case *cadence.ConstantSizedArrayType: + return d.decodeArray(t, true, uint64(t.Size), types) + + case *cadence.DictionaryType: + return d.decodeDictionary(t, types) + + case *cadence.ResourceType: + return d.decodeResource(t, types) + + case *cadence.StructType: + return d.decodeStruct(t, types) + + case *cadence.EventType: + return d.decodeEvent(t, types) + + case *cadence.ContractType: + return d.decodeContract(t, types) + + case cadence.StoragePathType: + return d.decodePath() + + case cadence.PublicPathType: + return d.decodePath() + + case cadence.PrivatePathType: + return d.decodePath() + + case cadence.MetaType: + // cadenceTypeByCCFTypeID uses a map with CCF type ID as keys. + // CCF type ID can collide if we reuse types (the variable) + // for type values because: + // - CCF type IDs are zero-based for composite TypeValue. + // - CCF type IDs are zero-based for composite type definitions. + typeValue, err := d.decodeNullableTypeValue(newCadenceTypeByCCFTypeID()) + if err != nil { + return nil, err + } + return cadence.NewMeteredTypeValue(d.gauge, typeValue), nil + + case *cadence.CapabilityType: + return d.decodeCapability(t, types) + + case *cadence.EnumType: + return d.decodeEnum(t, types) + + case *cadence.ReferenceType: + // When static type is a reference type, encoded value is its deferenced type. + return d.decodeValue(t.Type, types) + + default: + err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) + if err != nil { + return nil, fmt.Errorf("unexpected encoded value of Cadence type %s (%T): %s", t.ID(), t, err.Error()) + } + + // Decode ccf-type-and-value-message. + return d.decodeTypeAndValue(types) + } +} + +// decodeVoid decodes encoded void-value as +// language=CDDL +// void-value = nil +func (d *Decoder) decodeVoid() (cadence.Value, error) { + err := d.dec.DecodeNil() + if err != nil { + return nil, err + } + return cadence.NewMeteredVoid(d.gauge), nil +} + +// decodeBool decodes encoded bool-value as +// language=CDDL +// bool-value = bool +func (d *Decoder) decodeBool() (cadence.Value, error) { + b, err := d.dec.DecodeBool() + if err != nil { + return nil, err + } + return cadence.NewMeteredBool(d.gauge, b), nil +} + +// decodeCharacter decodes encoded character-value as +// language=CDDL +// character-value = tstr +func (d *Decoder) decodeCharacter() (cadence.Value, error) { + s, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + return cadence.NewMeteredCharacter( + d.gauge, + common.NewCadenceCharacterMemoryUsage(len(s)), + func() string { + return s + }) +} + +// decodeString decodes encoded string-value as +// language=CDDL +// string-value = tstr +// NOTE: invalid UTF-8 is rejected. +func (d *Decoder) decodeString() (cadence.Value, error) { + s, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + + return cadence.NewMeteredString( + d.gauge, + common.NewCadenceStringMemoryUsage(len(s)), + func() string { + return s + }, + ) +} + +// decodeAddress decodes address-value as +// language=CDDL +// address-value = bstr .size 8 +func (d *Decoder) decodeAddress() (cadence.Value, error) { + b, err := d.dec.DecodeBytes() + if err != nil { + return nil, err + } + if len(b) != 8 { + return nil, fmt.Errorf("encoded address-value has length %d (expected 8 bytes)", len(b)) + } + return cadence.BytesToMeteredAddress(d.gauge, b), nil +} + +// decodeInt decodes int-value as +// language=CDDL +// int-value = bigint +func (d *Decoder) decodeInt() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + + return cadence.NewMeteredIntFromBig( + d.gauge, + common.NewCadenceIntMemoryUsage( + common.BigIntByteLength(bigInt), + ), + func() *big.Int { + return bigInt + }, + ), nil +} + +// decodeInt8 decodes int8-value as +// language=CDDL +// int8-value = (int .ge -128) .le 127 +func (d *Decoder) decodeInt8() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + if i < math.MinInt8 || i > math.MaxInt8 { + return nil, fmt.Errorf( + "encoded int8-value %d is outside range of Int8 [%d, %d]", + i, + math.MinInt8, + math.MaxInt8, + ) + } + return cadence.NewMeteredInt8(d.gauge, int8(i)), nil +} + +// decodeInt16 decodes int16-value as +// language=CDDL +// int16-value = (int .ge -32768) .le 32767 +func (d *Decoder) decodeInt16() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + if i < math.MinInt16 || i > math.MaxInt16 { + return nil, fmt.Errorf( + "encoded int16-value %d is outside range of Int16 [%d, %d]", + i, + math.MinInt16, + math.MaxInt16, + ) + } + return cadence.NewMeteredInt16(d.gauge, int16(i)), nil +} + +// decodeInt32 decodes int32-value as +// language=CDDL +// int32-value = (int .ge -2147483648) .le 2147483647 +func (d *Decoder) decodeInt32() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + if i < math.MinInt32 || i > math.MaxInt32 { + return nil, fmt.Errorf( + "encoded int32-value %d is outside range of Int32 [%d, %d]", + i, + math.MinInt32, + math.MaxInt32, + ) + } + return cadence.NewMeteredInt32(d.gauge, int32(i)), nil +} + +// decodeInt64 decodes int64-value as +// language=CDDL +// int64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (d *Decoder) decodeInt64() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + return cadence.NewMeteredInt64(d.gauge, i), nil +} + +// decodeInt128 decodes int128-value as +// language=CDDL +// int128-value = bigint +func (d *Decoder) decodeInt128() (cadence.Value, error) { + return cadence.NewMeteredInt128FromBig( + d.gauge, + func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode Int128: %s", err)) + } + return bigInt + }, + ) +} + +// decodeInt256 decodes int256-value as +// language=CDDL +// int256-value = bigint +func (d *Decoder) decodeInt256() (cadence.Value, error) { + return cadence.NewMeteredInt256FromBig( + d.gauge, + func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode Int256: %s", err)) + } + return bigInt + }, + ) +} + +// decodeUInt decodes uint-value as +// language=CDDL +// uint-value = bigint .ge 0 +func (d *Decoder) decodeUInt() (cadence.Value, error) { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + return nil, err + } + if bigInt.Sign() < 0 { + return nil, errors.New("encoded uint-value is negative") + } + return cadence.NewMeteredUIntFromBig( + d.gauge, + common.NewCadenceIntMemoryUsage( + common.BigIntByteLength(bigInt), + ), + func() *big.Int { + return bigInt + }, + ) +} + +// decodeUInt8 decodes uint8-value as +// language=CDDL +// uint8-value = uint .le 255 +func (d *Decoder) decodeUInt8() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint8 { + return nil, fmt.Errorf( + "encoded uint8-value %d is outside range of Uint8 [0, %d]", + i, + math.MaxUint8, + ) + } + return cadence.NewMeteredUInt8(d.gauge, uint8(i)), nil +} + +// decodeUInt16 decodes uint16-value as +// language=CDDL +// uint16-value = uint .le 65535 +func (d *Decoder) decodeUInt16() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint16 { + return nil, fmt.Errorf( + "encoded uint16-value %d is outside range of Uint16 [0, %d]", + i, + math.MaxUint16, + ) + } + return cadence.NewMeteredUInt16(d.gauge, uint16(i)), nil +} + +// decodeUInt32 decodes uint32-value as +// language=CDDL +// uint32-value = uint .le 4294967295 +func (d *Decoder) decodeUInt32() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint32 { + return nil, fmt.Errorf( + "encoded uint32-value %d is outside range of Uint32 [0, %d]", + i, + math.MaxUint32, + ) + } + return cadence.NewMeteredUInt32(d.gauge, uint32(i)), nil +} + +// decodeUInt64 decodes uint64-value as +// language=CDDL +// uint64-value = uint .le 18446744073709551615 +func (d *Decoder) decodeUInt64() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + return cadence.NewMeteredUInt64(d.gauge, i), nil +} + +// decodeUInt128 decodes uint128-value as +// language=CDDL +// uint128-value = bigint .ge 0 +func (d *Decoder) decodeUInt128() (cadence.Value, error) { + // NewMeteredUInt128FromBig checks if decoded big.Int is positive. + return cadence.NewMeteredUInt128FromBig( + d.gauge, + func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode UInt128: %s", err)) + } + return bigInt + }, + ) +} + +// decodeUInt256 decodes uint256-value as +// language=CDDL +// uint256-value = bigint .ge 0 +func (d *Decoder) decodeUInt256() (cadence.Value, error) { + // NewMeteredUInt256FromBig checks if decoded big.Int is positive. + return cadence.NewMeteredUInt256FromBig( + d.gauge, + func() *big.Int { + bigInt, err := d.dec.DecodeBigInt() + if err != nil { + panic(fmt.Errorf("failed to decode UInt256: %s", err)) + } + return bigInt + }, + ) +} + +// decodeWord8 decodes word8-value as +// language=CDDL +// word8-value = uint .le 255 +func (d *Decoder) decodeWord8() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint8 { + return nil, fmt.Errorf( + "encoded word8-value %d is outside range of Word8 [0, %d]", + i, + math.MaxUint8, + ) + } + return cadence.NewMeteredWord8(d.gauge, uint8(i)), nil +} + +// decodeWord16 decodes word16-value as +// language=CDDL +// word16-value = uint .le 65535 +func (d *Decoder) decodeWord16() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint16 { + return nil, fmt.Errorf( + "encoded word16-value %d is outside range of Word16 [0, %d]", + i, + math.MaxUint16, + ) + } + return cadence.NewMeteredWord16(d.gauge, uint16(i)), nil +} + +// decodeWord32 decodes word32-value as +// language=CDDL +// word32-value = uint .le 4294967295 +func (d *Decoder) decodeWord32() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + if i > math.MaxUint32 { + return nil, fmt.Errorf( + "encoded word32-value %d is outside range of Word32 [0, %d]", + i, + math.MaxUint32, + ) + } + return cadence.NewMeteredWord32(d.gauge, uint32(i)), nil +} + +// decodeWord64 decodes word64-value as +// language=CDDL +// word64-value = uint .le 18446744073709551615 +func (d *Decoder) decodeWord64() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + return cadence.NewMeteredWord64(d.gauge, i), nil +} + +// decodeFix64 decodes fix64-value as +// language=CDDL +// fix64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (d *Decoder) decodeFix64() (cadence.Value, error) { + i, err := d.dec.DecodeInt64() + if err != nil { + return nil, err + } + return cadence.NewMeteredFix64FromRawFixedPointNumber(d.gauge, i) +} + +// decodeUFix64 decodes ufix64-value as +// language=CDDL +// ufix64-value = uint .le 18446744073709551615 +func (d *Decoder) decodeUFix64() (cadence.Value, error) { + i, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + return cadence.NewMeteredUFix64FromRawFixedPointNumber(d.gauge, i) +} + +// decodeOptional decodes encoded optional-value as +// language=CDDL +// optional-value = nil / value +func (d *Decoder) decodeOptional(typ *cadence.OptionalType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Peek ahead for next CBOR data item type + nextType, err := d.dec.NextType() + if err != nil { + return nil, err + } + + switch nextType { + case cbor.NilType: + // Decode nil. + err := d.dec.DecodeNil() + if err != nil { + return nil, err + } + return newNilOptionalValue(d.gauge, typ), nil + + default: + // Decode value. + value, err := d.decodeValue(typ.Type, types) + if err != nil { + return nil, err + } + return cadence.NewMeteredOptional(d.gauge, value), nil + } +} + +// decodeArray decodes encoded array-value as +// language=CDDL +// array-value = [* value] +func (d *Decoder) decodeArray(typ cadence.ArrayType, hasKnownSize bool, knownSize uint64, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array length. + n, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if hasKnownSize && knownSize != n { + return nil, fmt.Errorf( + "encoded array-value has %d elements (expected %d elements)", + n, + knownSize, + ) + } + + elementType := typ.Element() + + values := make([]cadence.Value, n) + for i := 0; i < int(n); i++ { + // Decode value. + element, err := d.decodeValue(elementType, types) + if err != nil { + return nil, err + } + values[i] = element + } + + v, err := cadence.NewMeteredArray( + d.gauge, + len(values), + func() ([]cadence.Value, error) { + return values, nil + }, + ) + if err != nil { + return nil, err + } + + return v.WithType(typ), nil +} + +// decodeDictionary decodes encoded dict-value as +// language=CDDL +// dict-value = [* (key: value, value: value)] +func (d *Decoder) decodeDictionary(typ *cadence.DictionaryType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // Decode array length. + n, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + // Check if number of elements is even. + if n%2 != 0 { + return nil, fmt.Errorf( + "encoded dict-value has %d elements (expected even number of elements)", + n, + ) + } + + pairCount := int(n / 2) + + value, err := cadence.NewMeteredDictionary( + d.gauge, + pairCount, + func() ([]cadence.KeyValuePair, error) { + pairs := make([]cadence.KeyValuePair, pairCount) + + // previousKeyRawBytes is used to determine if dictionary keys are sorted + var previousKeyRawBytes []byte + + for i := 0; i < pairCount; i++ { + // element i: key + + // Decode key as raw bytes to check that key pairs are sorted by key. + keyRawBytes, err := d.dec.DecodeRawBytes() + if err != nil { + return nil, err + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "dict-value key-value pairs MUST be sorted by key." + if !bytesAreSortedBytewise(previousKeyRawBytes, keyRawBytes) { + return nil, fmt.Errorf("encoded dict-value keys are not sorted") + } + + previousKeyRawBytes = keyRawBytes + + // decode key from raw bytes + keyDecoder := NewDecoder(d.gauge, keyRawBytes) + key, err := keyDecoder.decodeValue(typ.KeyType, types) + if err != nil { + return nil, err + } + + // element i+1: value + element, err := d.decodeValue(typ.ElementType, types) + if err != nil { + return nil, err + } + + pairs[i] = cadence.NewMeteredKeyValuePair(d.gauge, key, element) + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "Keys MUST be unique in dict-value. Decoders are not always required to check + // for duplicate dictionary keys. In some cases, checking for duplicate dictionary + // key is not necessary or it may be delegated to the application." + // + // Here, decoder doesn't check uniqueness of dictionary keys + // because checking is delegated (entrusted) to Cadence runtime. + return pairs, nil + }, + ) + if err != nil { + return nil, err + } + + return value.WithType(typ), nil +} + +// decodeComposite decodes encoded composite-value as +// language=CDDL +// composite-value = [* (field: value)] +func (d *Decoder) decodeComposite(fieldTypes []cadence.Field, types *cadenceTypeByCCFTypeID) ([]cadence.Value, error) { + fieldCount := len(fieldTypes) + + // Decode number of fields. + err := decodeCBORArrayWithKnownSize(d.dec, uint64(fieldCount)) + if err != nil { + return nil, err + } + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceField, + Amount: uint64(fieldCount), + }) + + fieldValues := make([]cadence.Value, fieldCount) + + for i := 0; i < fieldCount; i++ { + // Decode field. + field, err := d.decodeValue(fieldTypes[i].Type, types) + if err != nil { + return nil, err + } + fieldValues[i] = field + } + + return fieldValues, nil +} + +// decodeStruct decodes encoded composite-value as +// language=CDDL +// composite-value = [* (field: value)] +func (d *Decoder) decodeStruct(typ *cadence.StructType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredStruct( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodeResource decodes encoded composite-value as +// language=CDDL +// composite-value = [* (field: value)] +func (d *Decoder) decodeResource(typ *cadence.ResourceType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + resource, err := cadence.NewMeteredResource( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return resource.WithType(typ), nil +} + +// decodeEvent decodes encoded composite-value as +// language=CDDL +// composite-value = [* (field: value)] +func (d *Decoder) decodeEvent(typ *cadence.EventType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredEvent( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodeContract decodes encoded composite-value as +// language=CDDL +// composite-value = [* (field: value)] +func (d *Decoder) decodeContract(typ *cadence.ContractType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredContract( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodeEnum decodes encoded composite-value as +// language=CDDL +// composite-value = [* (field: value)] +func (d *Decoder) decodeEnum(typ *cadence.EnumType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + fieldValues, err := d.decodeComposite(typ.Fields, types) + if err != nil { + return nil, err + } + + v, err := cadence.NewMeteredEnum( + d.gauge, + len(fieldValues), + func() ([]cadence.Value, error) { + return fieldValues, nil + }, + ) + if err != nil { + return nil, err + } + + // typ is already metered at creation. + return v.WithType(typ), nil +} + +// decodePath decodes path-value as +// language=CDDL +// path-value = [ +// +// domain: uint, +// identifier: tstr, +// +// ] +func (d *Decoder) decodePath() (cadence.Value, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // Decode domain. + pathDomain, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + + // Decode identifier. + identifier, err := d.dec.DecodeString() + if err != nil { + return nil, err + } + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindRawString, + // No need to add 1 to account for empty string: string is metered in Path struct. + Amount: uint64(len(identifier)), + }) + + return cadence.NewMeteredPath(d.gauge, common.PathDomain(pathDomain), identifier) +} + +// decodeCapability decodes encoded capability-value as +// language=CDDL +// capability-value = [ +// +// address: address-value, +// path: path-value +// +// ] +func (d *Decoder) decodeCapability(typ *cadence.CapabilityType, types *cadenceTypeByCCFTypeID) (cadence.Value, error) { + // typ can be different from runtime CapabilityType because borrow type can be nil. + // In this case, runtime type is encoded with the value (as tag content for tag CBORTagTypeAndValue). + + // Check next encoded CBOR type. + nextType, err := d.dec.NextType() + if err != nil { + return nil, err + } + + if nextType == cbor.TagType { + err := decodeCBORTagWithKnownNumber(d.dec, CBORTagTypeAndValue) + if err != nil { + return nil, fmt.Errorf("unexpected encoded value of Cadence type %s (%T): %s", typ.ID(), typ, err.Error()) + } + + // Decode ccf-type-and-value-message. + return d.decodeTypeAndValue(types) + } + + // Decode array head of length 2. + err = decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // Decode address. + address, err := d.decodeAddress() + if err != nil { + return nil, err + } + + // Decode path. + path, err := d.decodePath() + if err != nil { + return nil, err + } + + return cadence.NewMeteredStorageCapability( + d.gauge, + path.(cadence.Path), + address.(cadence.Address), + typ.BorrowType), + nil +} + +// decodeTypeValue decodes encoded type-value as +// language=CDDL +// type-value = simple-type-value +// +// / optional-type-value +// / varsized-array-type-value +// / constsized-array-type-value +// / dict-type-value +// / struct-type-value +// / resource-type-value +// / contract-type-value +// / event-type-value +// / enum-type-value +// / struct-interface-type-value +// / resource-interface-type-value +// / contract-interface-type-value +// / function-type-value +// / reference-type-value +// / restricted-type-value +// / capability-type-value +// / type-value-ref +func (d *Decoder) decodeTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + // Decode tag number. + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return nil, err + } + + switch tagNum { + + case CBORTagTypeValueRef: + return d.decodeTypeRef(visited) + + case CBORTagSimpleTypeValue: + return d.decodeSimpleTypeID() + + case CBORTagOptionalTypeValue: + return d.decodeOptionalType(visited, d.decodeTypeValue) + + case CBORTagVarsizedArrayTypeValue: + return d.decodeVarSizedArrayType(visited, d.decodeTypeValue) + + case CBORTagConstsizedArrayTypeValue: + return d.decodeConstantSizedArrayType(visited, d.decodeTypeValue) + + case CBORTagDictTypeValue: + return d.decodeDictType(visited, d.decodeTypeValue) + + case CBORTagCapabilityTypeValue: + return d.decodeCapabilityType(visited, d.decodeNullableTypeValue) + + case CBORTagReferenceTypeValue: + return d.decodeReferenceType(visited, d.decodeTypeValue) + + case CBORTagRestrictedTypeValue: + return d.decodeRestrictedType(visited, d.decodeNullableTypeValue, d.decodeTypeValue) + + case CBORTagFunctionTypeValue: + return d.decodeFunctionTypeValue(visited) + + case CBORTagStructTypeValue: + return d.decodeStructTypeValue(visited) + + case CBORTagResourceTypeValue: + return d.decodeResourceTypeValue(visited) + + case CBORTagEventTypeValue: + return d.decodeEventTypeValue(visited) + + case CBORTagContractTypeValue: + return d.decodeContractTypeValue(visited) + + case CBORTagEnumTypeValue: + return d.decodeEnumTypeValue(visited) + + case CBORTagStructInterfaceTypeValue: + return d.decodeStructInterfaceTypeValue(visited) + + case CBORTagResourceInterfaceTypeValue: + return d.decodeResourceInterfaceTypeValue(visited) + + case CBORTagContractInterfaceTypeValue: + return d.decodeContractInterfaceTypeValue(visited) + + default: + return nil, fmt.Errorf("unsupported type-value with CBOR tag number %d", tagNum) + } +} + +// decodeNullableTypeValue decodes encoded type-value or nil. +func (d *Decoder) decodeNullableTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + cborType, err := d.dec.NextType() + if err != nil { + return nil, err + } + if cborType == cbor.NilType { + err = d.dec.DecodeNil() + return nil, err + } + return d.decodeTypeValue(visited) +} + +// decodeStructTypeValue decodes struct-type-value as +// language=CDDL +// struct-type-value = +// +// ; cbor-tag-struct-type-value +// #6.208(composite-type-value) +func (d *Decoder) decodeStructTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded struct-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredStructType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeResourceTypeValue decodes resource-type-value as +// language=CDDL +// resource-type-value = +// +// ; cbor-tag-resource-type-value +// #6.209(composite-type-value) +func (d *Decoder) decodeResourceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded resource-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredResourceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeEventTypeValue decodes event-type-value as +// language=CDDL +// event-type-value = +// +// ; cbor-tag-event-type-value +// #6.210(composite-type-value) +func (d *Decoder) decodeEventTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded event-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredEventType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeContractTypeValue decodes contract-type-value as +// language=CDDL +// contract-type-value = +// +// ; cbor-tag-contract-type-value +// #6.211(composite-type-value) +func (d *Decoder) decodeContractTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded contract-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredContractType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeEnumTypeValue decodes enum-type-value as +// language=CDDL +// enum-type-value = +// +// ; cbor-tag-enum-type-value +// #6.212(composite-type-value) +func (d *Decoder) decodeEnumTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ == nil { + return nil, fmt.Errorf("encoded enum-type-value has nil type") + } + return cadence.NewMeteredEnumType( + d.gauge, + location, + qualifiedIdentifier, + typ, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeStructInterfaceTypeValue decodes struct-inteface-type-value as +// language=CDDL +// struct-interface-type-value = +// +// ; cbor-tag-struct-interface-type-value +// #6.224(composite-type-value) +func (d *Decoder) decodeStructInterfaceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded struct-interface-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredStructInterfaceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeResourceInterfaceTypeValue decodes resource-inteface-type-value as +// language=CDDL +// resource-interface-type-value = +// +// ; cbor-tag-resource-interface-type-value +// #6.225(composite-type-value) +func (d *Decoder) decodeResourceInterfaceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded resource-interface-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredResourceInterfaceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + + return d.decodeCompositeTypeValue(visited, ctr) +} + +// decodeContractInterfaceTypeValue decodes contract-inteface-type-value as +// language=CDDL +// contract-interface-type-value = +// +// ; cbor-tag-contract-interface-type-value +// #6.226(composite-type-value) +func (d *Decoder) decodeContractInterfaceTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + ctr := func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, + ) (cadence.Type, error) { + if typ != nil { + return nil, fmt.Errorf( + "encoded contract-interface-type-value has type %s (expected nil type)", + typ.ID(), + ) + } + return cadence.NewMeteredContractInterfaceType( + d.gauge, + location, + qualifiedIdentifier, + nil, + nil, + ), nil + } + return d.decodeCompositeTypeValue(visited, ctr) +} + +type compositeTypeConstructor func( + location common.Location, + qualifiedIdentifier string, + typ cadence.Type, +) (cadence.Type, error) + +type compositeTypeValue struct { + ccfID ccfTypeID + location common.Location + identifier string + typ cadence.Type + rawFields []byte + rawInitializers []byte +} + +// decodeCompositeTypeValue decodes composite-type-value. +// See _decodeCompositeTypeValue for details. +func (d *Decoder) decodeCompositeTypeValue( + visited *cadenceTypeByCCFTypeID, + constructor compositeTypeConstructor, +) (cadence.Type, error) { + compTypeValue, err := d._decodeCompositeTypeValue(visited) + if err != nil { + return nil, err + } + + compositeType, err := constructor( + compTypeValue.location, + compTypeValue.identifier, + compTypeValue.typ, + ) + if err != nil { + return nil, err + } + + if compositeType == nil { + // Sanity check that compositeType isn't nil. + return nil, errors.New("unexpected nil composite type value") + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.id MUST be identical to the zero-based encoding order type-value." + if compTypeValue.ccfID != newCCFTypeIDFromUint64(uint64(visited.count())) { + return nil, fmt.Errorf( + "encoded composite-type-value's CCF type ID %d doesn't match zero-based encoding order composite-type-value", + compTypeValue.ccfID, + ) + } + + newType := visited.add(compTypeValue.ccfID, compositeType) + if !newType { + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.id MUST be unique in the same composite-type-value data item." + return nil, fmt.Errorf("found duplicate CCF type ID %d in encoded composite-type-value", compTypeValue.ccfID) + } + + // Decode fields after type is resolved to handle recursive types. + dec := NewDecoder(d.gauge, compTypeValue.rawFields) + fields, err := dec.decodeCompositeFields(visited, dec.decodeTypeValue) + if err != nil { + return nil, err + } + + // Decode initializers after type is resolved to handle recursive types. + dec = NewDecoder(d.gauge, compTypeValue.rawInitializers) + initializers, err := dec.decodeInitializerTypeValues(visited) + if err != nil { + return nil, err + } + + switch compositeType := compositeType.(type) { + case *cadence.StructType: + compositeType.Fields = fields + compositeType.Initializers = initializers + + case *cadence.ResourceType: + compositeType.Fields = fields + compositeType.Initializers = initializers + + case *cadence.EventType: + if len(initializers) != 1 { + return nil, fmt.Errorf( + "encoded event-type-value has %d initializations (expected 1 initialization)", + len(initializers), + ) + } + compositeType.Fields = fields + compositeType.Initializer = initializers[0] + + case *cadence.ContractType: + compositeType.Fields = fields + compositeType.Initializers = initializers + + case *cadence.EnumType: + compositeType.Fields = fields + compositeType.Initializers = initializers + + case *cadence.StructInterfaceType: + compositeType.Fields = fields + compositeType.Initializers = initializers + + case *cadence.ResourceInterfaceType: + compositeType.Fields = fields + compositeType.Initializers = initializers + + case *cadence.ContractInterfaceType: + compositeType.Fields = fields + compositeType.Initializers = initializers + } + + return compositeType, nil +} + +// _decodeCompositeTypeValue decodes composite-type-value as +// language=CDDL +// composite-type-value = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// ; type is only used by enum type value +// type: nil / type-value, +// fields: [ +// * [ +// name: tstr, +// type: type-value +// ] +// ] +// initializers: [ +// ? [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +// +// ] +func (d *Decoder) _decodeCompositeTypeValue(visited *cadenceTypeByCCFTypeID) (*compositeTypeValue, error) { + // Decode array of length 5 + err := decodeCBORArrayWithKnownSize(d.dec, 5) + if err != nil { + return nil, err + } + + // element 0: id (used to lookup repeated or recursive types) + ccfID, err := d.decodeCCFTypeID() + if err != nil { + return nil, err + } + + // element 1: cadence-type-id + _, location, identifier, err := d.decodeCadenceTypeID() + if err != nil { + return nil, err + } + + // element 2: type (only used by enum type value) + typ, err := d.decodeNullableTypeValue(visited) + if err != nil { + return nil, err + } + + // element 3: fields + rawFields, err := d.dec.DecodeRawBytes() + if err != nil { + return nil, err + } + + // element 4: initializers + rawInitializers, err := d.dec.DecodeRawBytes() + if err != nil { + return nil, err + } + + return &compositeTypeValue{ + ccfID: ccfID, + location: location, + identifier: identifier, + typ: typ, + rawFields: rawFields, + rawInitializers: rawInitializers, + }, nil +} + +// decodeInitializerTypeValues decodes composite initializers as +// language=CDDL +// +// initializers: [ +// ? [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +func (d *Decoder) decodeInitializerTypeValues(visited *cadenceTypeByCCFTypeID) ([][]cadence.Parameter, error) { + // Decode number of initializers. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if count > 1 { + return nil, fmt.Errorf("expect 0 or 1 initializer, got %d initializers", count) + } + + // Unmetered because this is created as an array of nil arrays, not Parameter structs. + initializerTypes := make([][]cadence.Parameter, count) + for i := 0; i < int(count); i++ { + initializerTypes[i], err = d.decodeParameterTypeValues(visited) + if err != nil { + return nil, err + } + } + + return initializerTypes, nil +} + +// decodeTypeParameterTypeValues decodes type parameters as +// language=CDDL +// +// type-parameters: [ +// * [ +// name: tstr, +// type-bound: type-value / nil +// ] +// ] +func (d *Decoder) decodeTypeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([]cadence.TypeParameter, error) { + // Decode number of parameters. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if count == 0 { + return []cadence.TypeParameter{}, nil + } + + typeParameterTypes := make([]cadence.TypeParameter, count) + typeParameterNames := make(map[string]struct{}, count) + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceTypeParameter, + Amount: count, + }) + + for i := 0; i < int(count); i++ { + // Decode type parameter. + typeParam, err := d.decodeTypeParameterTypeValue(visited) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "All parameter lists MUST have unique identifier" + if _, ok := typeParameterNames[typeParam.Name]; ok { + return nil, fmt.Errorf("found duplicate type parameter name %s", typeParam.Name) + } + + typeParameterNames[typeParam.Name] = struct{}{} + typeParameterTypes[i] = typeParam + } + + return typeParameterTypes, nil +} + +// decodeTypeParameterTypeValue decodes type parameter as +// language=CDDL +// +// [ +// name: tstr, +// type-bound: type-value / nil +// ] +func (d *Decoder) decodeTypeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.TypeParameter, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return cadence.TypeParameter{}, err + } + + // element 0: name + name, err := d.dec.DecodeString() + if err != nil { + return cadence.TypeParameter{}, err + } + + // element 2: type + t, err := d.decodeNullableTypeValue(visited) + if err != nil { + return cadence.TypeParameter{}, err + } + + // Unmetered because decodeTypeParamTypeValue is metered in decodeTypeParamTypeValues and called nowhere else + // Type is metered. + return cadence.NewTypeParameter(name, t), nil +} + +// decodeParameterTypeValues decodes composite initializer parameter types as +// language=CDDL +// +// [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +func (d *Decoder) decodeParameterTypeValues(visited *cadenceTypeByCCFTypeID) ([]cadence.Parameter, error) { + // Decode number of parameters. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if count == 0 { + return []cadence.Parameter{}, nil + } + + parameterTypes := make([]cadence.Parameter, count) + parameterLabels := make(map[string]struct{}, count) + parameterIdentifiers := make(map[string]struct{}, count) + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceParameter, + Amount: count, + }) + + for i := 0; i < int(count); i++ { + // Decode parameter. + param, err := d.decodeParameterTypeValue(visited) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "All parameter lists MUST have unique identifier" + if _, ok := parameterLabels[param.Label]; ok { + return nil, fmt.Errorf("found duplicate parameter label %s", param.Label) + } + + if _, ok := parameterIdentifiers[param.Identifier]; ok { + return nil, fmt.Errorf("found duplicate parameter identifier %s", param.Identifier) + } + + parameterLabels[param.Label] = struct{}{} + parameterIdentifiers[param.Identifier] = struct{}{} + + parameterTypes[i] = param + } + + return parameterTypes, nil +} + +// decodeParameterTypeValue decodes composite initializer parameter as +// language=CDDL +// +// [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +func (d *Decoder) decodeParameterTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Parameter, error) { + // Decode array head of length 3 + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return cadence.Parameter{}, err + } + + // element 0: label + label, err := d.dec.DecodeString() + if err != nil { + return cadence.Parameter{}, err + } + + // element 1: identifier + identifier, err := d.dec.DecodeString() + if err != nil { + return cadence.Parameter{}, err + } + + // element 2: type + t, err := d.decodeTypeValue(visited) + if err != nil { + return cadence.Parameter{}, err + } + + if t == nil { + return cadence.Parameter{}, errors.New("unexpected nil parameter type") + } + + // Unmetered because decodeParamTypeValue is metered in decodeParamTypeValues and called nowhere else + // Type is metered. + return cadence.NewParameter(label, identifier, t), nil +} + +// decodeFunctionTypeValue decodes encoded function-value as +// language=CDDL +// function-value = [ +// +// type-parameters: [ +// * [ +// name: tstr, +// type-bound: type-value / nil +// ] +// ] +// parameters: [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// return-type: type-value +// +// ] +func (d *Decoder) decodeFunctionTypeValue(visited *cadenceTypeByCCFTypeID) (cadence.Type, error) { + // Decode array head of length 3 + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return nil, err + } + + // element 0: type parameters + typeParameters, err := d.decodeTypeParameterTypeValues(visited) + if err != nil { + return nil, err + } + + // element 1: parameters + parameters, err := d.decodeParameterTypeValues(visited) + if err != nil { + return nil, err + } + + // element 2: return-type + returnType, err := d.decodeTypeValue(visited) + if err != nil { + return nil, err + } + + if returnType == nil { + return nil, errors.New("unexpected nil function return type") + } + + // TODO: + purity := cadence.FunctionPurityUnspecified + + return cadence.NewMeteredFunctionType( + d.gauge, + purity, + typeParameters, + parameters, + returnType, + ), nil +} + +func decodeCBORArrayWithKnownSize(dec *cbor.StreamDecoder, n uint64) error { + c, err := dec.DecodeArrayHead() + if err != nil { + return err + } + if c != n { + return fmt.Errorf("CBOR array has %d elements (expected %d elements)", c, n) + } + return nil +} + +func decodeCBORTagWithKnownNumber(dec *cbor.StreamDecoder, n uint64) error { + tagNum, err := dec.DecodeTagNumber() + if err != nil { + return err + } + if tagNum != n { + return fmt.Errorf("CBOR tag number is %d (expected %d)", tagNum, n) + } + return nil +} + +// newNilOptionalValue returns (nested) cadence.Optional nil value. +func newNilOptionalValue(gauge common.MemoryGauge, ot *cadence.OptionalType) cadence.Optional { + v := cadence.NewMeteredOptional(gauge, nil) + for { + var ok bool + ot, ok = ot.Type.(*cadence.OptionalType) + if !ok { + break + } + v = cadence.NewMeteredOptional(gauge, v) + } + return v +} diff --git a/encoding/ccf/decode_type.go b/encoding/ccf/decode_type.go new file mode 100644 index 0000000000..6ab8e674bf --- /dev/null +++ b/encoding/ccf/decode_type.go @@ -0,0 +1,660 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "errors" + "fmt" + + "github.com/fxamacker/cbor/v2" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" +) + +type cadenceTypeID string + +type decodeTypeFn func(types *cadenceTypeByCCFTypeID) (cadence.Type, error) + +// decodeInlineType decodes inline-type as +// language=CDDL +// inline-type = +// +// simple-type +// / optional-type +// / varsized-array-type +// / constsized-array-type +// / dict-type +// / reference-type +// / restricted-type +// / capability-type +// / type-ref +// +// All exported Cadence types needs to be handled in this function, +// including abstract and interface types. +func (d *Decoder) decodeInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, error) { + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return nil, err + } + + switch tagNum { + case CBORTagSimpleType: + return d.decodeSimpleTypeID() + + case CBORTagOptionalType: + return d.decodeOptionalType(types, d.decodeInlineType) + + case CBORTagVarsizedArrayType: + return d.decodeVarSizedArrayType(types, d.decodeInlineType) + + case CBORTagConstsizedArrayType: + return d.decodeConstantSizedArrayType(types, d.decodeInlineType) + + case CBORTagDictType: + return d.decodeDictType(types, d.decodeInlineType) + + case CBORTagReferenceType: + return d.decodeReferenceType(types, d.decodeInlineType) + + case CBORTagRestrictedType: + return d.decodeRestrictedType(types, d.decodeNullableInlineType, d.decodeInlineType) + + case CBORTagCapabilityType: + return d.decodeCapabilityType(types, d.decodeNullableInlineType) + + case CBORTagTypeRef: + return d.decodeTypeRef(types) + + default: + return nil, fmt.Errorf("unsupported encoded inline type with CBOR tag number %d", tagNum) + } +} + +// decodeNullableInlineType decodes encoded inline-type or nil. +func (d *Decoder) decodeNullableInlineType(types *cadenceTypeByCCFTypeID) (cadence.Type, error) { + cborType, err := d.dec.NextType() + if err != nil { + return nil, err + } + if cborType == cbor.NilType { + err = d.dec.DecodeNil() + return nil, err + } + return d.decodeInlineType(types) +} + +// decodeSimpleTypeID decodes encoded simple-type-id. +// See CCF specification for complete list of simple-type-id. +func (d *Decoder) decodeSimpleTypeID() (cadence.Type, error) { + simpleTypeID, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + + switch simpleTypeID { + case TypeBool: + return cadence.TheBoolType, nil + + case TypeString: + return cadence.TheStringType, nil + + case TypeCharacter: + return cadence.TheCharacterType, nil + + case TypeAddress: + return cadence.TheAddressType, nil + + case TypeInt: + return cadence.TheIntType, nil + + case TypeInt8: + return cadence.TheInt8Type, nil + + case TypeInt16: + return cadence.TheInt16Type, nil + + case TypeInt32: + return cadence.TheInt32Type, nil + + case TypeInt64: + return cadence.TheInt64Type, nil + + case TypeInt128: + return cadence.TheInt128Type, nil + + case TypeInt256: + return cadence.TheInt256Type, nil + + case TypeUInt: + return cadence.TheUIntType, nil + + case TypeUInt8: + return cadence.TheUInt8Type, nil + + case TypeUInt16: + return cadence.TheUInt16Type, nil + + case TypeUInt32: + return cadence.TheUInt32Type, nil + + case TypeUInt64: + return cadence.TheUInt64Type, nil + + case TypeUInt128: + return cadence.TheUInt128Type, nil + + case TypeUInt256: + return cadence.TheUInt256Type, nil + + case TypeWord8: + return cadence.TheWord8Type, nil + + case TypeWord16: + return cadence.TheWord16Type, nil + + case TypeWord32: + return cadence.TheWord32Type, nil + + case TypeWord64: + return cadence.TheWord64Type, nil + + case TypeFix64: + return cadence.TheFix64Type, nil + + case TypeUFix64: + return cadence.TheUFix64Type, nil + + case TypePath: + return cadence.ThePathType, nil + + case TypeCapabilityPath: + return cadence.TheCapabilityPathType, nil + + case TypeStoragePath: + return cadence.TheStoragePathType, nil + + case TypePublicPath: + return cadence.ThePublicPathType, nil + + case TypePrivatePath: + return cadence.ThePrivatePathType, nil + + case TypeAuthAccount: + return cadence.TheAuthAccountType, nil + + case TypePublicAccount: + return cadence.ThePublicAccountType, nil + + case TypeAuthAccountKeys: + return cadence.TheAuthAccountKeysType, nil + + case TypePublicAccountKeys: + return cadence.ThePublicAccountKeysType, nil + + case TypeAuthAccountContracts: + return cadence.TheAuthAccountContractsType, nil + + case TypePublicAccountContracts: + return cadence.ThePublicAccountContractsType, nil + + case TypeDeployedContract: + return cadence.TheDeployedContractType, nil + + case TypeAccountKey: + return cadence.TheAccountKeyType, nil + + case TypeBlock: + return cadence.TheBlockType, nil + + case TypeAny: + return cadence.TheAnyType, nil + + case TypeAnyStruct: + return cadence.TheAnyStructType, nil + + case TypeAnyResource: + return cadence.TheAnyResourceType, nil + + case TypeMetaType: + return cadence.TheMetaType, nil + + case TypeNever: + return cadence.TheNeverType, nil + + case TypeNumber: + return cadence.TheNumberType, nil + + case TypeSignedNumber: + return cadence.TheSignedNumberType, nil + + case TypeInteger: + return cadence.TheIntegerType, nil + + case TypeSignedInteger: + return cadence.TheSignedIntegerType, nil + + case TypeFixedPoint: + return cadence.TheFixedPointType, nil + + case TypeSignedFixedPoint: + return cadence.TheSignedFixedPointType, nil + + case TypeBytes: + return cadence.TheBytesType, nil + + case TypeVoid: + return cadence.TheVoidType, nil + + default: + return nil, fmt.Errorf("unsupported encoded simple type ID %d", simpleTypeID) + } +} + +// decodeOptionalType decodes optional-type or optional-type-value as +// language=CDDL +// optional-type = +// +// ; cbor-tag-optional-type +// #6.138(inline-type) +// +// optional-type-value = +// +// ; cbor-tag-optional-type-value +// #6.186(type-value) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeOptionalType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode inline-type or type-value. + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + if elementType == nil { + return nil, errors.New("unexpected nil type as optional inner type") + } + return cadence.NewMeteredOptionalType(d.gauge, elementType), nil +} + +// decodeVarSizedArrayType decodes varsized-array-type or varsized-array-type-value as +// language=CDDL +// varsized-array-type = +// +// ; cbor-tag-varsized-array-type +// #6.139(inline-type) +// +// varsized-array-type-value = +// +// ; cbor-tag-varsized-array-type-value +// #6.187(type-value) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeVarSizedArrayType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode inline-type or type-value. + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + if elementType == nil { + return nil, errors.New("unexpected nil type as variable sized array element type") + } + return cadence.NewMeteredVariableSizedArrayType(d.gauge, elementType), nil +} + +// decodeConstantSizedArrayType decodes constsized-array-type or constsized-array-type-value as +// language=CDDL +// constsized-array-type = +// +// ; cbor-tag-constsized-array-type +// #6.140([ +// array-size: uint, +// element-type: inline-type +// ]) +// +// constsized-array-type-value = +// +// ; cbor-tag-constsized-array-type-value +// #6.188([ +// array-size: uint, +// element-type: type-value +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeConstantSizedArrayType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: array-size + size, err := d.dec.DecodeUint64() + if err != nil { + return nil, err + } + + // element 1: element-type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + if elementType == nil { + return nil, errors.New("unexpected nil type as constant sized array element type") + } + + return cadence.NewMeteredConstantSizedArrayType(d.gauge, uint(size), elementType), nil +} + +// decodeDictType decodes dict-type or dict-type-value as +// language=CDDL +// dict-type = +// +// ; cbor-tag-dict-type +// #6.141([ +// key-type: inline-type, +// element-type: inline-type +// ]) +// +// dict-type-value = +// +// ; cbor-tag-dict-type-value +// #6.189([ +// key-type: type-value, +// element-type: type-value +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeDictType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: key type (inline-type or type-value) + keyType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + if keyType == nil { + return nil, errors.New("unexpected nil type as dictionary key type") + } + + // element 1: element type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + if elementType == nil { + return nil, errors.New("unexpected nil type as dictionary element type") + } + + return cadence.NewMeteredDictionaryType(d.gauge, keyType, elementType), nil +} + +// decodeCapabilityType decodes capability-type or capability-type-value as +// language=CDDL +// capability-type = +// +// ; cbor-tag-capability-type +// ; use an array as an extension point +// #6.144([ +// ; borrow-type +// inline-type / nil +// ]) +// +// capability-type-value = +// +// ; cbor-tag-capability-type-value +// ; use an array as an extension point +// #6.192([ +// ; borrow-type +// type-value / nil +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeCapabilityType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode array head of length 1 + err := decodeCBORArrayWithKnownSize(d.dec, 1) + if err != nil { + return nil, err + } + + // element 0: borrow-type (inline-type or type-value) + borrowType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + return cadence.NewMeteredCapabilityType(d.gauge, borrowType), nil +} + +// decodeReferenceType decodes reference-type or reference-type-value as +// language=CDDL +// reference-type = +// +// ; cbor-tag-reference-type +// #6.142([ +// authorized: bool, +// type: inline-type, +// ]) +// +// reference-type-value = +// +// ; cbor-tag-reference-type-value +// #6.190([ +// authorized: bool, +// type: type-value, +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeReferenceType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: authorized + authorized, err := d.dec.DecodeBool() + if err != nil { + return nil, err + } + + // element 0: type (inline-type or type-value) + elementType, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + if elementType == nil { + return nil, errors.New("unexpected nil type as reference type") + } + + return cadence.NewMeteredReferenceType(d.gauge, authorized, elementType), nil +} + +// decodeRestrictedType decodes restricted-type or restricted-type-value as +// language=CDDL +// restricted-type = +// +// ; cbor-tag-restricted-type +// #6.143([ +// type: inline-type / nil, +// restrictions: [* inline-type] +// ]) +// +// restricted-type-value = +// +// ; cbor-tag-restricted-type-value +// #6.191([ +// type: type-value / nil, +// restrictions: [* type-value] +// ]) +// +// NOTE: decodeTypeFn is responsible for decoding inline-type or type-value. +func (d *Decoder) decodeRestrictedType( + types *cadenceTypeByCCFTypeID, + decodeTypeFn decodeTypeFn, + decodeRestrictionTypeFn decodeTypeFn, +) (cadence.Type, error) { + // Decode array of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return nil, err + } + + // element 0: type + typ, err := decodeTypeFn(types) + if err != nil { + return nil, err + } + + // element 1: restrictions + restrictionCount, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + restrictionTypeIDs := make(map[string]struct{}, restrictionCount) + var previousRestrictedTypeID string + + restrictions := make([]cadence.Type, restrictionCount) + for i := 0; i < int(restrictionCount); i++ { + // Decode restriction. + restrictedType, err := decodeRestrictionTypeFn(types) + if err != nil { + return nil, err + } + + if restrictedType == nil { + return nil, errors.New("unexpected nil type as restriction type") + } + + restrictedTypeID := restrictedType.ID() + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "Elements MUST be unique in restricted-type or restricted-type-value." + if _, ok := restrictionTypeIDs[restrictedTypeID]; ok { + return nil, fmt.Errorf("found duplicate restricted type %s", restrictedTypeID) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" + // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." + if !stringsAreSortedBytewise(previousRestrictedTypeID, restrictedTypeID) { + return nil, fmt.Errorf("restricted types are not sorted (%s, %s)", previousRestrictedTypeID, restrictedTypeID) + } + + restrictionTypeIDs[restrictedTypeID] = struct{}{} + previousRestrictedTypeID = restrictedTypeID + + restrictions[i] = restrictedType + } + + return cadence.NewMeteredRestrictedType( + d.gauge, + typ, + restrictions, + ), nil +} + +// decodeCCFTypeID decodes encoded id as +// language=CDDL +// id = bstr +func (d *Decoder) decodeCCFTypeID() (ccfTypeID, error) { + b, err := d.dec.DecodeBytes() + if err != nil { + return 0, err + } + return newCCFTypeID(b), nil +} + +// decodeCadenceTypeID decodes encoded cadence-type-id as +// language=CDDL +// cadence-type-id = tstr +func (d *Decoder) decodeCadenceTypeID() (cadenceTypeID, common.Location, string, error) { + typeID, err := d.dec.DecodeString() + if err != nil { + return "", nil, "", err + } + + location, identifier, err := common.DecodeTypeID(d.gauge, typeID) + if err != nil { + return cadenceTypeID(typeID), nil, "", fmt.Errorf("invalid type ID `%s`: %w", typeID, err) + } else if location == nil && sema.NativeCompositeTypes[typeID] == nil { + // If the location is nil and there is no native composite type with this ID, then it's an invalid type. + // Note: This was moved out from the common.DecodeTypeID() to avoid the circular dependency. + return cadenceTypeID(typeID), nil, "", fmt.Errorf("invalid type ID for built-in: `%s`", typeID) + } + + return cadenceTypeID(typeID), location, identifier, nil +} + +// decodeTypeRef decodes encoded type-ref as +// language=CDDL +// type-ref = +// +// ; cbor-tag-type-ref +// #6.136(id) +func (d *Decoder) decodeTypeRef(types *cadenceTypeByCCFTypeID) (cadence.Type, error) { + id, err := d.decodeCCFTypeID() + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "type-ref.id MUST refer to composite-type.id." + // "type-value-ref.id MUST refer to composite-type-value.id in the same composite-type-value data item." + t, err := types.typ(id) + if err != nil { + return nil, err + } + + // Track referenced type definition so the decoder can detect + // encoded but not referenced type definition (extraneous data). + types.reference(id) + + return t, nil +} diff --git a/encoding/ccf/decode_typedef.go b/encoding/ccf/decode_typedef.go new file mode 100644 index 0000000000..d7eacfc52a --- /dev/null +++ b/encoding/ccf/decode_typedef.go @@ -0,0 +1,503 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "errors" + "fmt" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" + cadenceErrors "github.com/onflow/cadence/runtime/errors" +) + +type rawFieldsWithCCFTypeID struct { + typeID ccfTypeID + rawFields []byte +} + +// decodeTypeDefs decodes composite/interface type definitions as +// language=CDDL +// composite-typedef = [ +// +// ; one-or-more instead of zero-or-more because: +// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) +// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` +// + ( +// struct-type +// / resource-type +// / contract-type +// / event-type +// / enum-type +// / struct-interface-type +// / resource-interface-type +// / contract-interface-type +// )] +func (d *Decoder) decodeTypeDefs() (*cadenceTypeByCCFTypeID, error) { + // Decode number of type definitions. + count, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + if count == 0 { + return nil, errors.New("found 0 type definition in composite-typedef (expected at least 1 type definition)") + } + + types := newCadenceTypeByCCFTypeID() + + // NOTE: composite fields are not decoded while composite types are decoded + // because field type might reference composite type that hasn't decoded yet. + rawFieldsOfTypes := make([]rawFieldsWithCCFTypeID, 0, count) + + // cadenceTypeIDs is used to check if cadence type IDs are unique in type definitions. + cadenceTypeIDs := make(map[cadenceTypeID]struct{}, count) + + // previousCadenceID is used to check if type definitions are sorted by cadence type IDs. + var previousCadenceID cadenceTypeID + + for i := uint64(0); i < count; i++ { + ccfID, cadenceID, rawFields, err := d.decodeTypeDef(types) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type.cadence-type-id MUST be unique in + // ccf-typedef-message or ccf-typedef-and-value-message." + if _, ok := cadenceTypeIDs[cadenceID]; ok { + return nil, fmt.Errorf("found duplicate Cadence type ID %s in composite-typedef", cadenceID) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.id in ccf-typedef-and-value-message MUST + // be identical to its zero-based index in composite-typedef." + if !ccfID.Equal(newCCFTypeIDFromUint64(i)) { + return nil, fmt.Errorf( + "CCF type ID %d doesn't match composite-typedef index %d in composite-typedef", + ccfID, + i, + ) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "Type definitions MUST be sorted by cadence-type-id in composite-typedef." + if !stringsAreSortedBytewise(string(previousCadenceID), string(cadenceID)) { + return nil, fmt.Errorf( + "Cadence type ID (%s, %s) isn't sorted in composite-typedef", + string(previousCadenceID), + string(cadenceID), + ) + } + + if len(rawFields) > 0 { + rawFieldsOfTypes = append(rawFieldsOfTypes, rawFieldsWithCCFTypeID{typeID: ccfID, rawFields: rawFields}) + } + + cadenceTypeIDs[cadenceID] = struct{}{} + previousCadenceID = cadenceID + } + + // Decode fields after all high-level type definitions are resolved. + for _, rawFields := range rawFieldsOfTypes { + typ, err := types.typ(rawFields.typeID) + if err != nil { + panic(cadenceErrors.NewUnexpectedErrorFromCause(err)) + } + + dec := NewDecoder(d.gauge, rawFields.rawFields) + fields, err := dec.decodeCompositeFields(types, dec.decodeInlineType) + if err != nil { + return nil, err + } + + switch typ := typ.(type) { + case cadence.CompositeType: + typ.SetCompositeFields(fields) + + default: + return nil, fmt.Errorf("unsupported type %s (%T) in composite-typedef", typ.ID(), typ) + } + } + + return types, nil +} + +// decodeTypeDef decodes composite/interface type in type definition as +// language=CDDL +// struct-type = +// +// ; cbor-tag-struct-type +// #6.160(composite-type) +// +// resource-type = +// +// ; cbor-tag-resource-type +// #6.161(composite-type) +// +// event-type = +// +// ; cbor-tag-event-type +// #6.162(composite-type) +// +// contract-type = +// +// ; cbor-tag-contract-type +// #6.163(composite-type) +// +// enum-type = +// +// ; cbor-tag-enum-type +// #6.164(composite-type) +// +// struct-interface-type = +// +// ; cbor-tag-struct-interface-type +// #6.176(interface-type) +// +// resource-interface-type = +// +// ; cbor-tag-resource-interface-type +// #6.177(interface-type) +// +// contract-interface-type = +// +// ; cbor-tag-contract-interface-type +// #6.178(interface-type) +func (d *Decoder) decodeTypeDef( + types *cadenceTypeByCCFTypeID, +) ( + ccfTypeID, + cadenceTypeID, + []byte, + error, +) { + tagNum, err := d.dec.DecodeTagNumber() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + switch tagNum { + case CBORTagStructType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredStructType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, ctr) + + case CBORTagResourceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredResourceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, ctr) + + case CBORTagEventType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredEventType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, ctr) + + case CBORTagContractType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredContractType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeCompositeType(types, ctr) + + case CBORTagEnumType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredEnumType( + d.gauge, + location, + identifier, + nil, + nil, + nil, + ) + } + return d.decodeCompositeType(types, ctr) + + case CBORTagStructInterfaceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredStructInterfaceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeInterfaceType(types, ctr) + + case CBORTagResourceInterfaceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredResourceInterfaceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeInterfaceType(types, ctr) + + case CBORTagContractInterfaceType: + ctr := func(location common.Location, identifier string) cadence.Type { + return cadence.NewMeteredContractInterfaceType( + d.gauge, + location, + identifier, + nil, + nil, + ) + } + return d.decodeInterfaceType(types, ctr) + + default: + return ccfTypeID(0), + cadenceTypeID(""), + nil, + fmt.Errorf("unsupported type definition with CBOR tag number %d", tagNum) + } +} + +// decodeCompositeType decodes composite type in type definition as +// language=CDDL +// composite-type = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// fields: [ +// * [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +// +// ] +func (d *Decoder) decodeCompositeType( + types *cadenceTypeByCCFTypeID, + constructor func(common.Location, string) cadence.Type, +) ( + ccfTypeID, + cadenceTypeID, + []byte, + error, +) { + + // Decode array head of length 3. + err := decodeCBORArrayWithKnownSize(d.dec, 3) + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // element 0: id + ccfID, err := d.decodeCCFTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." + if types.has(ccfID) { + return ccfTypeID(0), cadenceTypeID(""), nil, fmt.Errorf("found duplicate CCF type ID %d in composite-type", ccfID) + } + + // element 1: cadence-type-id + cadenceID, location, identifier, err := d.decodeCadenceTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // element 2: fields + rawField, err := d.dec.DecodeRawBytes() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // The return value can be ignored, because its non-existence was already checked above + _ = types.add(ccfID, constructor(location, identifier)) + + return ccfID, cadenceID, rawField, nil +} + +// decodeCompositeFields decodes field types as +// language=CDDL +// +// fields: [ +// * [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +func (d *Decoder) decodeCompositeFields(types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) ([]cadence.Field, error) { + // Decode number of fields. + fieldCount, err := d.dec.DecodeArrayHead() + if err != nil { + return nil, err + } + + fields := make([]cadence.Field, fieldCount) + fieldNames := make(map[string]struct{}, fieldCount) + var previousFieldName string + + common.UseMemory(d.gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceField, + Amount: fieldCount, + }) + + for i := 0; i < int(fieldCount); i++ { + field, err := d.decodeCompositeField(types, decodeTypeFn) + if err != nil { + return nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "field-name MUST be unique in composite-type." + // "name MUST be unique in composite-type-value.fields." + if _, ok := fieldNames[field.Identifier]; ok { + return nil, fmt.Errorf("found duplicate field name %s in composite-type", field.Identifier) + } + + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.fields MUST be sorted by name" + // "composite-type-value.fields MUST be sorted by name." + if !stringsAreSortedBytewise(previousFieldName, field.Identifier) { + return nil, fmt.Errorf("field names are not sorted in composite-type (%s, %s)", previousFieldName, field.Identifier) + } + + fieldNames[field.Identifier] = struct{}{} + previousFieldName = field.Identifier + fields[i] = field + } + + return fields, nil +} + +// decodeCompositeField decodes field type as +// language=CDDL +// +// [ +// field-name: tstr, +// field-type: inline-type +// ] +func (d *Decoder) decodeCompositeField(types *cadenceTypeByCCFTypeID, decodeTypeFn decodeTypeFn) (cadence.Field, error) { + // Decode array head of length 2 + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return cadence.Field{}, err + } + + // element 0: field-name + fieldName, err := d.dec.DecodeString() + if err != nil { + return cadence.Field{}, err + } + + // element 1: field-type + fieldType, err := decodeTypeFn(types) + if err != nil { + return cadence.Field{}, err + } + + if fieldType == nil { + return cadence.Field{}, errors.New("unexpected nil type as composite field type") + } + + // Unmetered because decodeCompositeField is metered in decodeCompositeFields and called nowhere else + // fieldType is still metered. + return cadence.NewField(fieldName, fieldType), nil +} + +// decodeInterfaceType decodes interface type as +// language=CDDL +// interface-type = [ +// +// id: id, +// cadence-type-id: tstr, +// +// ] +func (d *Decoder) decodeInterfaceType( + types *cadenceTypeByCCFTypeID, + constructor func(common.Location, string) cadence.Type, +) ( + ccfTypeID, + cadenceTypeID, + []byte, // always nil since interface type definition doesn't contain fields + error, +) { + + // Decode array head of length 2. + err := decodeCBORArrayWithKnownSize(d.dec, 2) + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // element 0: id + ccfID, err := d.decodeCCFTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // "Valid CCF Encoding Requirements" in CCF specs: + // + // "composite-type.id MUST be unique in ccf-typedef-message or ccf-typedef-and-value-message." + if types.has(ccfID) { + return ccfTypeID(0), cadenceTypeID(""), nil, fmt.Errorf("found duplicate CCF type ID %d in interface-type", ccfID) + } + + // element 1: cadence-type-id + cadenceID, location, identifier, err := d.decodeCadenceTypeID() + if err != nil { + return ccfTypeID(0), cadenceTypeID(""), nil, err + } + + // The return value can be ignored, because its non-existence was already checked above + _ = types.add(ccfID, constructor(location, identifier)) + + return ccfID, cadenceID, nil, nil +} diff --git a/encoding/ccf/encode.go b/encoding/ccf/encode.go new file mode 100644 index 0000000000..9f71212e3d --- /dev/null +++ b/encoding/ccf/encode.go @@ -0,0 +1,1905 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "bytes" + "fmt" + "io" + goRuntime "runtime" + "sort" + "sync" + + "github.com/fxamacker/cbor/v2" + + "github.com/onflow/cadence" + cadenceErrors "github.com/onflow/cadence/runtime/errors" +) + +// CBOREncMode +// +// See https://github.com/fxamacker/cbor: +// "For best performance, reuse EncMode and DecMode after creating them." +var CBOREncMode = func() cbor.EncMode { + options := cbor.CoreDetEncOptions() + options.BigIntConvert = cbor.BigIntConvertNone + encMode, err := options.EncMode() + if err != nil { + panic(err) + } + return encMode +}() + +// An Encoder converts Cadence values into CCF-encoded bytes. +type Encoder struct { + // CCF codec uses CBOR codec under the hood. + enc *cbor.StreamEncoder + // cachedSortedFieldIndex contains sorted field index of Cadence composite types. + cachedSortedFieldIndex map[string][]int // key: composite type ID, value: sorted field indexes +} + +// Encode returns the CCF-encoded representation of the given value. +// +// This function returns an error if the Cadence value cannot be represented in CCF. +func Encode(value cadence.Value) ([]byte, error) { + var w bytes.Buffer + + enc := NewEncoder(&w) + defer enc.enc.Close() + + err := enc.Encode(value) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +// MustEncode returns the CCF-encoded representation of the given value, or panics +// if the value cannot be represented in CCF. +func MustEncode(value cadence.Value) []byte { + b, err := Encode(value) + if err != nil { + panic(err) + } + return b +} + +// NewEncoder initializes an Encoder that will write CCF-encoded bytes to the +// given io.Writer. +func NewEncoder(w io.Writer) *Encoder { + // CCF codec uses CBOR codec under the hood. + return &Encoder{ + enc: CBOREncMode.NewStreamEncoder(w), + cachedSortedFieldIndex: make(map[string][]int), + } +} + +// Encode writes the CCF-encoded representation of the given value to this +// encoder's io.Writer. +// +// This function returns an error if the given value's type is not supported +// by the encoder. +func (e *Encoder) Encode(value cadence.Value) (err error) { + // capture panics + defer func() { + // Recover panic error if there is any. + if r := recover(); r != nil { + // Don't recover Go errors, internal errors, or non-errors. + switch r := r.(type) { + case goRuntime.Error, cadenceErrors.InternalError: + panic(r) + case error: + err = r + default: + panic(r) + } + } + + // Add context to error if there is any. + if err != nil { + err = fmt.Errorf( + "ccf: failed to encode value (type %T, %q): %s", + value, + value.Type().ID(), + err, + ) + } + }() + + // Traverse value to find all composite types. + types, tids := compositeTypesFromValue(value) + + if len(types) == 0 { + // Encode top level message: ccf-type-and-value-message. + err = e.encodeTypeAndValue(value, tids) + } else { + // Encode top level message: ccf-typedef-and-value-message. + err = e.encodeTypeDefAndValue(value, types, tids) + } + if err != nil { + return err + } + + return e.enc.Flush() +} + +// encodeTypeDefAndValue encodes type definition and value as +// language=CDDL +// ccf-typedef-and-value-message = +// +// ; cbor-tag-typedef-and-value +// #6.129([ +// typedef: composite-typedef, +// type-and-value: inline-type-and-value +// ]) +func (e *Encoder) encodeTypeDefAndValue( + value cadence.Value, + types []cadence.Type, + tids ccfTypeIDByCadenceType, +) error { + // Encode tag number cbor-tag-typedef-and-value and array head of length 2. + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagTypeDefAndValue, + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: typedef + err = e.encodeTypeDefs(types, tids) + if err != nil { + return err + } + + // element 1: type and value + return e.encodeInlineTypeAndValue(value, tids) +} + +// encodeTypeAndValue encodes type and value as +// language=CDDL +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130(inline-type-and-value) +func (e *Encoder) encodeTypeAndValue(value cadence.Value, tids ccfTypeIDByCadenceType) error { + // Encode tag number + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagTypeAndValue, + }) + if err != nil { + return err + } + + return e.encodeInlineTypeAndValue(value, tids) +} + +// encodeTypeAndValueWithNoTag encodes inline type and value as +// language=CDDL +// inline-type-and-value = [ +// +// type: inline-type, +// value: value, +// +// ] +func (e *Encoder) encodeInlineTypeAndValue(value cadence.Value, tids ccfTypeIDByCadenceType) error { + // Encode array head of length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + runtimeType := value.Type() + + // element 0: inline-type + err = e.encodeInlineType(runtimeType, tids) + if err != nil { + return err + } + + // element 1: value + return e.encodeValue(value, runtimeType, tids) +} + +// encodeTypeDefs encodes composite/interface type definitions as +// language=CDDL +// composite-typedef = [ +// +// ; one-or-more instead of zero-or-more because: +// ; - when encoding a primitive type, such as boolean or string, `ccf-type-and-value-message` is used (no `composite-typedef` at all) +// ; - when encoding a composite type, such as event, `ccf-typedef-and-value-message` is used, which encodes at least one `composite-typedef` +// + ( +// struct-type +// / resource-type +// / contract-type +// / event-type +// / enum-type +// / struct-interface-type +// / resource-interface-type +// / contract-interface-type +// )] +func (e *Encoder) encodeTypeDefs(types []cadence.Type, tids ccfTypeIDByCadenceType) error { + // Encode array head with number of type definitions. + err := e.enc.EncodeArrayHead(uint64(len(types))) + if err != nil { + return err + } + + for _, typ := range types { + + switch typ := typ.(type) { + case cadence.CompositeType: + // Encode struct-type, resource-type, contract-type, event-type, or enum-type. + err = e.encodeCompositeType(typ, tids) + if err != nil { + return err + } + + case cadence.InterfaceType: + // Encode struct-interface-type, resource-interface-type, or contract-interface-type. + err = e.encodeInterfaceType(typ, tids) + if err != nil { + return err + } + + default: + panic(cadenceErrors.NewUnexpectedError("unexpected type %s in type definition", typ.ID())) + } + } + + return nil +} + +// encodeValue traverses the object graph of the provided value and encodes it as +// language=CDDL +// value = +// +// ccf-type-and-value-message +// / simple-value +// / optional-value +// / array-value +// / dict-value +// / composite-value +// / path-value +// / capability-value +// / function-value +// / type-value +// +// ccf-type-and-value-message = +// +// ; cbor-tag-type-and-value +// #6.130([ +// type: inline-type, +// value: value +// ]) +// +// simple-value = +// +// void-value +// / bool-value +// / character-value +// / string-value +// / address-value +// / uint-value +// / uint8-value +// / uint16-value +// / uint32-value +// / uint64-value +// / uint128-value +// / uint256-value +// / int-value +// / int8-value +// / int16-value +// / int32-value +// / int64-value +// / int128-value +// / int256-value +// / word8-value +// / word16-value +// / word32-value +// / word64-value +// / fix64-value +// / ufix64-value +// +// IMPORTANT: +// "Valid CCF Encoding Requirements" in CCF Specification states: +// +// "Encoders are not required to check for invalid input items +// (e.g. invalid UTF-8 strings, duplicate dictionary keys, etc.) +// Applications MUST NOT provide invalid items to encoders." +// +// cadence.String and cadence.Character must be valid UTF-8 +// and it is the application's responsibility to provide +// the CCF encoder with valid UTF-8 strings. +func (e *Encoder) encodeValue( + v cadence.Value, + staticType cadence.Type, + tids ccfTypeIDByCadenceType, +) error { + + runtimeType := v.Type() + + // CCF requires value to have non-nil type. + if runtimeType == nil { + panic(cadenceErrors.NewUnexpectedError("value (%T) has nil type", v)) + } + + if needToEncodeRuntimeType(staticType, runtimeType) { + // Get type that needs to be encoded as inline type. + inlineType := getTypeToEncodeAsCCFInlineType(staticType, runtimeType) + + // Encode ccf-type-and-value-message. + + // Encode tag number and array head of length 2. + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagTypeAndValue, + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: type as inline-type + err = e.encodeInlineType(inlineType, tids) + if err != nil { + return err + } + + // element 1: value + } + + switch v := v.(type) { + case cadence.Void: + return e.encodeVoid(v) + + case cadence.Optional: + return e.encodeOptional(v, tids) + + case cadence.Bool: + return e.encodeBool(v) + + case cadence.Character: + return e.encodeCharacter(v) + + case cadence.String: + return e.encodeString(v) + + case cadence.Address: + return e.encodeAddress(v) + + case cadence.Int: + return e.encodeInt(v) + + case cadence.Int8: + return e.encodeInt8(v) + + case cadence.Int16: + return e.encodeInt16(v) + + case cadence.Int32: + return e.encodeInt32(v) + + case cadence.Int64: + return e.encodeInt64(v) + + case cadence.Int128: + return e.encodeInt128(v) + + case cadence.Int256: + return e.encodeInt256(v) + + case cadence.UInt: + return e.encodeUInt(v) + + case cadence.UInt8: + return e.encodeUInt8(v) + + case cadence.UInt16: + return e.encodeUInt16(v) + + case cadence.UInt32: + return e.encodeUInt32(v) + + case cadence.UInt64: + return e.encodeUInt64(v) + + case cadence.UInt128: + return e.encodeUInt128(v) + + case cadence.UInt256: + return e.encodeUInt256(v) + + case cadence.Word8: + return e.encodeWord8(v) + + case cadence.Word16: + return e.encodeWord16(v) + + case cadence.Word32: + return e.encodeWord32(v) + + case cadence.Word64: + return e.encodeWord64(v) + + case cadence.Fix64: + return e.encodeFix64(v) + + case cadence.UFix64: + return e.encodeUFix64(v) + + case cadence.Array: + return e.encodeArray(v, tids) + + case cadence.Dictionary: + return e.encodeDictionary(v, tids) + + case cadence.Struct: + return e.encodeStruct(v, tids) + + case cadence.Resource: + return e.encodeResource(v, tids) + + case cadence.Event: + return e.encodeEvent(v, tids) + + case cadence.Contract: + return e.encodeContract(v, tids) + + case cadence.Path: + return e.encodePath(v) + + case cadence.TypeValue: + // cadence.TypeValue is encoded as self-contained, without any + // reference to tids. So tids isn't passed to encodeTypeValue(). + // + // encodeTypeValue() receives a new ccfTypeIDByCadenceType to deduplicate + // composite type values within the same CCF type value encoding. + // For example, when a composite type appears more than once + // (recursive or repeated as nested type) within the same type value, + // it is only encoded once and is subsequently represented by its CCF ID. + // For type value encoding, CCF type ID is sequentially generated by + // traversal order. + // + // If x.StaticType is nil, type value is encoded as nil. + return e.encodeNullableTypeValue(v.StaticType, ccfTypeIDByCadenceType{}) + + case cadence.StorageCapability: + return e.encodeCapability(v) + + case cadence.Enum: + return e.encodeEnum(v, tids) + + case cadence.Function: + // cadence.Function is encoded as self-contained, without any + // reference to tids. So tids isn't passed to encodeFunction(). + // + // encodeFunction() receives a new ccfTypeIDByCadenceType to deduplicate + // composite type values within the same CCF function value encoding. + // For example, when a composite type appears more than once + // (recursive or repeated as nested type) within the same function value, + // it is only encoded once and is subsequently represented by its CCF ID. + // For function value encoding, CCF type ID is sequentially generated by + // traversal order of sorted parameters and return type. + return e.encodeFunction(v.FunctionType, ccfTypeIDByCadenceType{}) + + default: + panic(cadenceErrors.NewUnexpectedError("cannot encode unsupported value (%T)", v)) + } +} + +// encodeVoid encodes cadence.Void as +// language=CDDL +// void-value = nil +func (e *Encoder) encodeVoid(v cadence.Void) error { + return e.enc.EncodeNil() +} + +// encodeOptional encodes cadence.Optional as +// language=CDDL +// optional-value = nil / value +func (e *Encoder) encodeOptional(v cadence.Optional, tids ccfTypeIDByCadenceType) error { + innerValue := v.Value + if innerValue == nil { + return e.enc.EncodeNil() + } + // Use innerValue.Type() as static type to avoid encoding type + // because OptionalType is already encoded. + return e.encodeValue(innerValue, innerValue.Type(), tids) +} + +// encodeBool encodes cadence.Bool as +// language=CDDL +// bool-value = bool +func (e *Encoder) encodeBool(v cadence.Bool) error { + return e.enc.EncodeBool(bool(v)) +} + +// encodeCharacter encodes cadence.Character as +// language=CDDL +// character-value = tstr +func (e *Encoder) encodeCharacter(v cadence.Character) error { + return e.enc.EncodeString(string(v)) +} + +// encodeString encodes cadence.String as +// language=CDDL +// string-value = tstr +func (e *Encoder) encodeString(v cadence.String) error { + return e.enc.EncodeString(string(v)) +} + +// encodeAddress encodes cadence.Address as +// language=CDDL +// address-value = bstr .size 8 +func (e *Encoder) encodeAddress(v cadence.Address) error { + return e.enc.EncodeBytes(v[:]) +} + +// encodeInt encodes cadence.Int as +// language=CDDL +// int-value = bigint +func (e *Encoder) encodeInt(v cadence.Int) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeInt8 encodes cadence.Int8 as +// language=CDDL +// int8-value = (int .ge -128) .le 127 +func (e *Encoder) encodeInt8(v cadence.Int8) error { + return e.enc.EncodeInt8(int8(v)) +} + +// encodeInt16 encodes cadence.Int16 as +// language=CDDL +// int16-value = (int .ge -32768) .le 32767 +func (e *Encoder) encodeInt16(v cadence.Int16) error { + return e.enc.EncodeInt16(int16(v)) +} + +// encodeInt32 encodes cadence.Int32 as +// language=CDDL +// int32-value = (int .ge -2147483648) .le 2147483647 +func (e *Encoder) encodeInt32(v cadence.Int32) error { + return e.enc.EncodeInt32(int32(v)) +} + +// encodeInt64 encodes cadence.Int64 as +// language=CDDL +// int64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (e *Encoder) encodeInt64(v cadence.Int64) error { + return e.enc.EncodeInt64(int64(v)) +} + +// encodeInt128 encodes cadence.Int128 as +// language=CDDL +// int128-value = bigint +func (e *Encoder) encodeInt128(v cadence.Int128) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeInt256 encodes cadence.Int256 as +// language=CDDL +// int256-value = bigint +func (e *Encoder) encodeInt256(v cadence.Int256) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeUInt encodes cadence.UInt as +// language=CDDL +// uint-value = bigint .ge 0 +func (e *Encoder) encodeUInt(v cadence.UInt) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeUInt8 encodes cadence.UInt8 as +// language=CDDL +// uint8-value = uint .le 255 +func (e *Encoder) encodeUInt8(v cadence.UInt8) error { + return e.enc.EncodeUint8(uint8(v)) +} + +// encodeUInt16 encodes cadence.UInt16 as +// language=CDDL +// uint16-value = uint .le 65535 +func (e *Encoder) encodeUInt16(v cadence.UInt16) error { + return e.enc.EncodeUint16(uint16(v)) +} + +// encodeUInt32 encodes cadence.UInt32 as CBOR uint. +// language=CDDL +// uint32-value = uint .le 4294967295 +func (e *Encoder) encodeUInt32(v cadence.UInt32) error { + return e.enc.EncodeUint32(uint32(v)) +} + +// encodeUInt64 encodes cadence.UInt64 as +// language=CDDL +// uint64-value = uint .le 18446744073709551615 +func (e *Encoder) encodeUInt64(v cadence.UInt64) error { + return e.enc.EncodeUint64(uint64(v)) +} + +// encodeUInt128 encodes cadence.UInt128 as +// language=CDDL +// uint128-value = bigint .ge 0 +func (e *Encoder) encodeUInt128(v cadence.UInt128) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeUInt256 encodes cadence.UInt256 as +// language=CDDL +// uint256-value = bigint .ge 0 +func (e *Encoder) encodeUInt256(v cadence.UInt256) error { + return e.enc.EncodeBigInt(v.Big()) +} + +// encodeWord8 encodes cadence.Word8 as +// language=CDDL +// word8-value = uint .le 255 +func (e *Encoder) encodeWord8(v cadence.Word8) error { + return e.enc.EncodeUint8(uint8(v)) +} + +// encodeWord16 encodes cadence.Word16 as +// language=CDDL +// word16-value = uint .le 65535 +func (e *Encoder) encodeWord16(v cadence.Word16) error { + return e.enc.EncodeUint16(uint16(v)) +} + +// encodeWord32 encodes cadence.Word32 as +// language=CDDL +// word32-value = uint .le 4294967295 +func (e *Encoder) encodeWord32(v cadence.Word32) error { + return e.enc.EncodeUint32(uint32(v)) +} + +// encodeWord64 encodes cadence.Word64 as +// language=CDDL +// word64-value = uint .le 18446744073709551615 +func (e *Encoder) encodeWord64(v cadence.Word64) error { + return e.enc.EncodeUint64(uint64(v)) +} + +// encodeFix64 encodes cadence.Fix64 as +// language=CDDL +// fix64-value = (int .ge -9223372036854775808) .le 9223372036854775807 +func (e *Encoder) encodeFix64(v cadence.Fix64) error { + return e.enc.EncodeInt64(int64(v)) +} + +// encodeUFix64 encodes cadence.UFix64 as +// language=CDDL +// ufix64-value = uint .le 18446744073709551615 +func (e *Encoder) encodeUFix64(v cadence.UFix64) error { + return e.enc.EncodeUint64(uint64(v)) +} + +// encodeArray encodes cadence.Array as +// language=CDDL +// array-value = [* value] +func (e *Encoder) encodeArray(v cadence.Array, tids ccfTypeIDByCadenceType) error { + staticElementType := v.ArrayType.Element() + + // Encode array head with number of array elements. + err := e.enc.EncodeArrayHead(uint64(len(v.Values))) + if err != nil { + return err + } + + for _, element := range v.Values { + // Encode element as value. + err = e.encodeValue(element, staticElementType, tids) + if err != nil { + return err + } + } + + return nil +} + +// encodeDictionary encodes cadence.Dictionary as +// language=CDDL +// dict-value = [* (key: value, value: value)] +func (e *Encoder) encodeDictionary(v cadence.Dictionary, tids ccfTypeIDByCadenceType) error { + if len(v.Pairs) > 1 { + return e.encodeSortedDictionary(v, tids) + } + + staticKeyType := v.DictionaryType.KeyType + staticElementType := v.DictionaryType.ElementType + + // Encode array head with array size of 2 * number of pairs. + err := e.enc.EncodeArrayHead(uint64(len(v.Pairs)) * 2) + if err != nil { + return err + } + + for _, pair := range v.Pairs { + + // Encode dictionary key as value. + err = e.encodeValue(pair.Key, staticKeyType, tids) + if err != nil { + return err + } + + // Encode dictionary value as value. + err = e.encodeValue(pair.Value, staticElementType, tids) + if err != nil { + return err + } + } + + return nil +} + +// encodeDictionary encodes cadence.Dictionary as +// language=CDDL +// dict-value = [* (key: value, value: value)] +func (e *Encoder) encodeSortedDictionary(v cadence.Dictionary, tids ccfTypeIDByCadenceType) error { + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "dict-value key-value pairs MUST be sorted by key." + + // Use a new buffer for sorting key value pairs. + buf := getBuffer() + defer putBuffer(buf) + + // Encode and sort key value pairs. + sortedPairs, err := encodeAndSortKeyValuePairs(buf, v, tids) + if err != nil { + return err + } + + // Encode array head with 2 * number of pairs. + err = e.enc.EncodeArrayHead(uint64(len(v.Pairs)) * 2) + if err != nil { + return err + } + + for _, pair := range sortedPairs { + // Encode key and value. + err = e.enc.EncodeRawBytes(pair.encodedPair) + if err != nil { + return err + } + } + + return nil +} + +func encodeAndSortKeyValuePairs( + buf *bytes.Buffer, + v cadence.Dictionary, + tids ccfTypeIDByCadenceType, +) ( + []encodedKeyValuePair, + error, +) { + staticKeyType := v.DictionaryType.KeyType + staticElementType := v.DictionaryType.ElementType + + encodedPairs := make([]encodedKeyValuePair, len(v.Pairs)) + + e := NewEncoder(buf) + + for i, pair := range v.Pairs { + + off := buf.Len() + + // Encode dictionary key as value. + err := e.encodeValue(pair.Key, staticKeyType, tids) + if err != nil { + return nil, err + } + + // Get encoded key length (must flush first). + e.enc.Flush() + keyLength := buf.Len() - off + + // Encode dictionary value as value. + err = e.encodeValue(pair.Value, staticElementType, tids) + if err != nil { + return nil, err + } + + // Get encoded pair length (must flush first). + e.enc.Flush() + pairLength := buf.Len() - off + + encodedPairs[i] = encodedKeyValuePair{keyLength: keyLength, pairLength: pairLength} + } + + // Reslice buf for encoded key and pair by offset and length. + b := buf.Bytes() + off := 0 + for i := 0; i < len(encodedPairs); i++ { + encodedPairs[i].encodedKey = b[off : off+encodedPairs[i].keyLength] + encodedPairs[i].encodedPair = b[off : off+encodedPairs[i].pairLength] + off += encodedPairs[i].pairLength + } + if off != len(b) { + // Sanity check + panic(cadenceErrors.NewUnexpectedError("encoded dictionary pairs' offset %d doesn't match buffer length %d", off, len(b))) + } + + sort.Sort(bytewiseKeyValuePairSorter(encodedPairs)) + + return encodedPairs, nil +} + +// encodeStruct encodes cadence.Struct as +// language=CDDL +// composite-value = [* (field: value)] +func (e *Encoder) encodeStruct(v cadence.Struct, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.StructType, v.Fields, tids) +} + +// encodeResource encodes cadence.Resource as +// language=CDDL +// composite-value = [* (field: value)] +func (e *Encoder) encodeResource(v cadence.Resource, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.ResourceType, v.Fields, tids) +} + +// encodeEvent encodes cadence.Event as +// language=CDDL +// composite-value = [* (field: value)] +func (e *Encoder) encodeEvent(v cadence.Event, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.EventType, v.Fields, tids) +} + +// encodeContract encodes cadence.Contract as +// language=CDDL +// composite-value = [* (field: value)] +func (e *Encoder) encodeContract(v cadence.Contract, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.ContractType, v.Fields, tids) +} + +// encodeEnum encodes cadence.Enum as +// language=CDDL +// composite-value = [* (field: value)] +func (e *Encoder) encodeEnum(v cadence.Enum, tids ccfTypeIDByCadenceType) error { + return e.encodeComposite(v.EnumType, v.Fields, tids) +} + +// encodeComposite encodes composite types as +// language=CDDL +// composite-value = [* (field: value)] +func (e *Encoder) encodeComposite( + typ cadence.CompositeType, + fields []cadence.Value, + tids ccfTypeIDByCadenceType, +) error { + staticFieldTypes := typ.CompositeFields() + + if len(staticFieldTypes) != len(fields) { + panic(cadenceErrors.NewUnexpectedError( + "%s field count %d doesn't match declared field type count %d", + typ.ID(), + len(fields), + len(staticFieldTypes), + )) + } + + // Encode array head with number of fields. + err := e.enc.EncodeArrayHead(uint64(len(fields))) + if err != nil { + return err + } + + switch len(fields) { + case 0: + // Short-circuit if there is no field. + return nil + + case 1: + // Avoid overhead of sorting if there is only one field. + return e.encodeValue(fields[0], staticFieldTypes[0].Type, tids) + + default: + sortedIndexes := e.getSortedFieldIndex(typ) + + if len(sortedIndexes) != len(staticFieldTypes) { + panic(cadenceErrors.NewUnexpectedError("number of sorted indexes doesn't match number of field types")) + } + + for _, index := range sortedIndexes { + // Encode sorted field as value. + err = e.encodeValue(fields[index], staticFieldTypes[index].Type, tids) + if err != nil { + return err + } + } + + return nil + } +} + +// encodePath encodes cadence.Path as +// language=CDDL +// path-value = [ +// +// domain: uint, +// identifier: tstr, +// +// ] +func (e *Encoder) encodePath(x cadence.Path) error { + // Encode array head with length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: domain as CBOR uint. + err = e.enc.EncodeUint8(uint8(x.Domain)) + if err != nil { + return err + } + + // element 1: identifier as CBOR tstr. + return e.enc.EncodeString(x.Identifier) +} + +// encodeCapability encodes cadence.StorageCapability as +// language=CDDL +// capability-value = [ +// +// address: address-value, +// path: path-value +// +// ] +func (e *Encoder) encodeCapability(capability cadence.StorageCapability) error { + // Encode array head with length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: address + err = e.encodeAddress(capability.Address) + if err != nil { + return err + } + + // element 1: path + return e.encodePath(capability.Path) +} + +// encodeFunction encodes cadence.FunctionType as +// language=CDDL +// function-value = [ +// +// type-parameters: [ +// * [ +// name: tstr, +// type-bound: type-value / nil +// ] +// ] +// parameters: [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// return-type: type-value +// +// ] +func (e *Encoder) encodeFunction(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { + // Encode array head of length 3. + err := e.enc.EncodeRawBytes([]byte{ + // array, 3 items follow + 0x83, + }) + if err != nil { + return err + } + + // element 0: type parameters as array. + err = e.encodeTypeParameterTypeValues(typ.TypeParameters, visited) + if err != nil { + return err + } + + // element 1: parameters as array. + err = e.encodeParameterTypeValues(typ.Parameters, visited) + if err != nil { + return err + } + + // element 2: return type as type-value. + return e.encodeTypeValue(typ.ReturnType, visited) +} + +// encodeTypeValue encodes cadence.Type as +// language=CDDL +// type-value = +// +// simple-type-value +// / optional-type-value +// / varsized-array-type-value +// / constsized-array-type-value +// / dict-type-value +// / struct-type-value +// / resource-type-value +// / contract-type-value +// / event-type-value +// / enum-type-value +// / struct-interface-type-value +// / resource-interface-type-value +// / contract-interface-type-value +// / function-type-value +// / reference-type-value +// / restricted-type-value +// / capability-type-value +// / type-value-ref +// +// TypeValue is used differently from inline type or type definition. +// Inline type and type definition are used to decode exported value, +// while TypeValue is exported value, which type is cadence.MetaType. +// Thus, TypeValue can encode more information than type or type definition. +func (e *Encoder) encodeTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceType) error { + switch typ.(type) { + case cadence.CompositeType, cadence.InterfaceType: + cadenceTypeID := typ.ID() + if ccfID, ok := visited[cadenceTypeID]; ok { + // Encode visited composite/interface type value + // using CCF type id for compactness. + return e.encodeTypeValueRef(ccfID) + } + // type value ccf type id is index of visited composite/interface + // type value depth first. + visited[cadenceTypeID] = ccfTypeID(len(visited)) + } + + simpleTypeID, ok := simpleTypeIDByType(typ) + if ok { + return e.encodeSimpleTypeValue(simpleTypeID) + } + + switch typ := typ.(type) { + case *cadence.OptionalType: + return e.encodeOptionalTypeValue(typ, visited) + + case *cadence.VariableSizedArrayType: + return e.encodeVarSizedArrayTypeValue(typ, visited) + + case *cadence.ConstantSizedArrayType: + return e.encodeConstantSizedArrayTypeValue(typ, visited) + + case *cadence.DictionaryType: + return e.encodeDictTypeValue(typ, visited) + + case *cadence.StructType: + return e.encodeStructTypeValue(typ, visited) + + case *cadence.ResourceType: + return e.encodeResourceTypeValue(typ, visited) + + case *cadence.EventType: + return e.encodeEventTypeValue(typ, visited) + + case *cadence.ContractType: + return e.encodeContractTypeValue(typ, visited) + + case *cadence.StructInterfaceType: + return e.encodeStructInterfaceTypeValue(typ, visited) + + case *cadence.ResourceInterfaceType: + return e.encodeResourceInterfaceTypeValue(typ, visited) + + case *cadence.ContractInterfaceType: + return e.encodeContractInterfaceTypeValue(typ, visited) + + case *cadence.FunctionType: + return e.encodeFunctionTypeValue(typ, visited) + + case *cadence.ReferenceType: + return e.encodeReferenceTypeValue(typ, visited) + + case *cadence.RestrictedType: + return e.encodeRestrictedTypeValue(typ, visited) + + case *cadence.CapabilityType: + return e.encodeCapabilityTypeValue(typ, visited) + + case *cadence.EnumType: + return e.encodeEnumTypeValue(typ, visited) + + default: + panic(cadenceErrors.NewUnexpectedError("unsupported type value %s (%T)", typ.ID(), typ)) + } +} + +// encodeNullableTypeValue encodes cadence.Type as type-value or nil. +func (e *Encoder) encodeNullableTypeValue(typ cadence.Type, visited ccfTypeIDByCadenceType) error { + if typ == nil { + return e.encodeNilTypeValue() + } + return e.encodeTypeValue(typ, visited) +} + +// encodeTypeValueRef encodes type value ID as +// language=CDDL +// type-value-ref = +// +// ; cbor-tag-type-value-ref +// #6.184(id) +func (e *Encoder) encodeTypeValueRef(id ccfTypeID) error { + rawTagNum := []byte{0xd8, CBORTagTypeValueRef} + return e.encodeTypeRefWithRawTag(id, rawTagNum) +} + +// encodeSimpleTypeValue encodes cadence simple type value as +// language=CDDL +// simple-type-value = +// +// ; cbor-tag-simple-type-value +// #6.185(simple-type-id) +func (e *Encoder) encodeSimpleTypeValue(id uint64) error { + rawTagNum := []byte{0xd8, CBORTagSimpleTypeValue} + return e.encodeSimpleTypeWithRawTag(id, rawTagNum) +} + +// encodeOptionalTypeValue encodes cadence.OptionalType as +// language=CDDL +// optional-type-value = +// +// ; cbor-tag-optional-type-value +// #6.186(type-value) +func (e *Encoder) encodeOptionalTypeValue(typ *cadence.OptionalType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagOptionalTypeValue} + return e.encodeOptionalTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeVarSizedArrayTypeValue encodes cadence.VariableSizedArrayType as +// language=CDDL +// varsized-array-type-value = +// +// ; cbor-tag-varsized-array-type-value +// #6.187(type-value) +func (e *Encoder) encodeVarSizedArrayTypeValue(typ *cadence.VariableSizedArrayType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagVarsizedArrayTypeValue} + return e.encodeVarSizedArrayTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeConstantSizedArrayTypeValue encodes cadence.ConstantSizedArrayType as +// language=CDDL +// constsized-array-type-value = +// +// ; cbor-tag-constsized-array-type-value +// #6.188([ +// array-size: uint, +// element-type: type-value +// ]) +func (e *Encoder) encodeConstantSizedArrayTypeValue(typ *cadence.ConstantSizedArrayType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagConstsizedArrayTypeValue} + return e.encodeConstantSizedArrayTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeDictTypeValue encodes cadence.DictionaryType as +// language=CDDL +// dict-type-value = +// +// ; cbor-tag-dict-type-value +// #6.189([ +// key-type: type-value, +// element-type: type-value +// ]) +func (e *Encoder) encodeDictTypeValue(typ *cadence.DictionaryType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagDictTypeValue} + return e.encodeDictTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeReferenceTypeValue encodes cadence.ReferenceType as +// language=CDDL +// reference-type-value = +// +// ; cbor-tag-reference-type-value +// #6.190([ +// authorized: bool, +// type: type-value, +// ]) +func (e *Encoder) encodeReferenceTypeValue(typ *cadence.ReferenceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagReferenceTypeValue} + return e.encodeReferenceTypeWithRawTag( + typ, + visited, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeRestrictedTypeValue encodes cadence.RestrictedType as +// language=CDDL +// restricted-type-value = +// +// ; cbor-tag-restricted-type-value +// #6.191([ +// type: type-value / nil, +// restrictions: [* type-value] +// ]) +func (e *Encoder) encodeRestrictedTypeValue(typ *cadence.RestrictedType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagRestrictedTypeValue} + return e.encodeRestrictedTypeWithRawTag( + typ, + visited, + e.encodeNullableTypeValue, + e.encodeTypeValue, + rawTagNum, + ) +} + +// encodeCapabilityTypeValue encodes cadence.CapabilityType as +// language=CDDL +// capability-type-value = +// +// ; cbor-tag-capability-type-value +// ; use an array as an extension point +// #6.192([ +// ; borrow-type +// type-value / nil +// ]) +func (e *Encoder) encodeCapabilityTypeValue(typ *cadence.CapabilityType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagCapabilityTypeValue} + return e.encodeCapabilityTypeWithRawTag( + typ, + visited, + e.encodeNullableTypeValue, + rawTagNum, + ) +} + +// encodeStructTypeValue encodes cadence.StructType as +// language=CDDL +// struct-type-value = +// +// ; cbor-tag-struct-type-value +// #6.208(composite-type-value) +func (e *Encoder) encodeStructTypeValue(typ *cadence.StructType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagStructTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeResourceTypeValue encodes cadence.ResourceType as +// language=CDDL +// resource-type-value = +// +// ; cbor-tag-resource-type-value +// #6.209(composite-type-value) +func (e *Encoder) encodeResourceTypeValue(typ *cadence.ResourceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagResourceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeEventTypeValue encodes cadence.EventType as +// language=CDDL +// event-type-value = +// +// ; cbor-tag-event-type-value +// #6.210(composite-type-value) +func (e *Encoder) encodeEventTypeValue(typ *cadence.EventType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagEventTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + [][]cadence.Parameter{typ.Initializer}, + visited, + rawTagNum, + ) +} + +// encodeContractTypeValue encodes cadence.ContractType as +// language=CDDL +// contract-type-value = +// +// ; cbor-tag-contract-type-value +// #6.211(composite-type-value) +func (e *Encoder) encodeContractTypeValue(typ *cadence.ContractType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagContractTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeEnumTypeValue encodes cadence.EnumType as +// language=CDDL +// enum-type-value = +// +// ; cbor-tag-enum-type-value +// #6.212(composite-type-value) +func (e *Encoder) encodeEnumTypeValue(typ *cadence.EnumType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagEnumTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + typ.RawType, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeStructInterfaceTypeValue encodes cadence.StructInterfaceType as +// language=CDDL +// struct-interface-type-value = +// +// ; cbor-tag-struct-interface-type-value +// #6.224(composite-type-value) +func (e *Encoder) encodeStructInterfaceTypeValue(typ *cadence.StructInterfaceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagStructInterfaceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeResourceInterfaceTypeValue encodes cadence.ResourceInterfaceType as +// language=CDDL +// resource-interface-type-value = +// +// ; cbor-tag-resource-interface-type-value +// #6.225(composite-type-value) +func (e *Encoder) encodeResourceInterfaceTypeValue(typ *cadence.ResourceInterfaceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagResourceInterfaceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeContractInterfaceTypeValue encodes cadence.ContractInterfaceType as +// language=CDDL +// contract-interface-type-value = +// +// ; cbor-tag-contract-interface-type-value +// #6.226(composite-type-value) +func (e *Encoder) encodeContractInterfaceTypeValue(typ *cadence.ContractInterfaceType, visited ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagContractInterfaceTypeValue} + return e.encodeCompositeTypeValue( + typ.ID(), + nil, + typ.Fields, + typ.Initializers, + visited, + rawTagNum, + ) +} + +// encodeCompositeTypeValue encodes composite and interface type values as +// language=CDDL +// composite-type-value = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// ; type is only used by enum type value +// type: nil / type-value, +// fields: [ +// * [ +// name: tstr, +// type: type-value +// ] +// ] +// initializers: [ +// ? [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +// +// ] +func (e *Encoder) encodeCompositeTypeValue( + cadenceTypeID string, + typ cadence.Type, + fieldTypes []cadence.Field, + initializerTypes [][]cadence.Parameter, + visited ccfTypeIDByCadenceType, + rawTagNum []byte, +) error { + ccfID, ok := visited[cadenceTypeID] + if !ok { + panic(cadenceErrors.NewUnexpectedError("CCF type ID not found for composite type value %s", cadenceTypeID)) + } + + // Encode given tag number indicating cadence type value. + err := e.enc.EncodeRawBytes(rawTagNum) + if err != nil { + return err + } + + // Encode CBOR array head with length 5. + err = e.enc.EncodeArrayHead(5) + if err != nil { + return err + } + + // element 0: ccf type id as bstr. + // It is used to lookup repeated or recursive types within the same encoded type value. + err = e.encodeCCFTypeID(ccfID) + if err != nil { + return err + } + + // element 1: cadence type id as tstr. + err = e.encodeCadenceTypeID(cadenceTypeID) + if err != nil { + return err + } + + // element 2: type as nil or type-value. + // Type is only used by enum type value. + err = e.encodeNullableTypeValue(typ, visited) + if err != nil { + return err + } + + // element 3: fields as array. + err = e.encodeFieldTypeValues(fieldTypes, visited) + if err != nil { + return err + } + + // element 4: initializers as array. + return e.encodeInitializerTypeValues(initializerTypes, visited) +} + +// encodeFieldTypeValues encodes composite field types as +// language=CDDL +// +// fields: [ +// * [ +// name: tstr, +// type: type-value +// ] +// ] +func (e *Encoder) encodeFieldTypeValues(fieldTypes []cadence.Field, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of field types. + err := e.enc.EncodeArrayHead(uint64(len(fieldTypes))) + if err != nil { + return err + } + + switch len(fieldTypes) { + case 0: + // Short-circuit if there is no field type. + return nil + + case 1: + // Avoid overhead of sorting if there is only one field type. + return e.encodeFieldTypeValue(fieldTypes[0], visited) + + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type-value.fields MUST be sorted by name." + + // NOTE: bytewiseFieldIdentifierSorter doesn't sort fieldTypes in place. + // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes + // index. + sorter := newBytewiseFieldSorter(fieldTypes) + + sort.Sort(sorter) + + // Encode sorted field types. + for _, index := range sorter.indexes { + err = e.encodeFieldTypeValue(fieldTypes[index], visited) + if err != nil { + return err + } + } + + return nil + } +} + +// encodeFieldTypeValue encodes one composite field type as +// language=CDDL +// +// [ +// name: tstr, +// type: type-value +// ] +func (e *Encoder) encodeFieldTypeValue(fieldType cadence.Field, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with length 2. + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: field name as tstr. + err = e.enc.EncodeString(fieldType.Identifier) + if err != nil { + return err + } + + // element 1: field type as type-value. + return e.encodeTypeValue(fieldType.Type, visited) +} + +// encodeInitializerTypeValues encodes composite initializers as +// language=CDDL +// +// initializers: [ +// ? [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +// ] +func (e *Encoder) encodeInitializerTypeValues(initializerTypes [][]cadence.Parameter, visited ccfTypeIDByCadenceType) error { + if len(initializerTypes) > 1 { + return fmt.Errorf("got %d initializers, want 0 or 1 initializer", len(initializerTypes)) + } + + // Encode CBOR array head with number of initializers. + err := e.enc.EncodeArrayHead(uint64(len(initializerTypes))) + if err != nil { + return err + } + + // Encode initializers. + for _, params := range initializerTypes { + err = e.encodeParameterTypeValues(params, visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeTypeParameterTypeValues encodes type parameters as +// language=CDDL +// +// type-parameters: [ +// +// *[ +// name: tstr, +// type-bound: type-value / nil +// ] +// +// ] +func (e *Encoder) encodeTypeParameterTypeValues(typeParameters []cadence.TypeParameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of type parameters. + err := e.enc.EncodeArrayHead(uint64(len(typeParameters))) + if err != nil { + return err + } + + // Encode type parameters. + for _, param := range typeParameters { + err = e.encodeTypeParameterTypeValue(param, visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeTypeParameterTypeValue encodes type parameter as +// language=CDDL +// +// [ +// name: tstr, +// type-bound: type-value / nil +// ] +func (e *Encoder) encodeTypeParameterTypeValue(typeParameter cadence.TypeParameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with length 2 + err := e.enc.EncodeRawBytes([]byte{ + // array, 2 items follow + 0x82, + }) + if err != nil { + return err + } + + // element 0: name as tstr. + err = e.enc.EncodeString(typeParameter.Name) + if err != nil { + return err + } + + // element 1: type as type-bound. + return e.encodeNullableTypeValue(typeParameter.TypeBound, visited) +} + +// encodeParameterTypeValues encodes composite initializer parameter types as +// language=CDDL +// +// [ +// * [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +// ] +func (e *Encoder) encodeParameterTypeValues(parameterTypes []cadence.Parameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with number of parameter types. + err := e.enc.EncodeArrayHead(uint64(len(parameterTypes))) + if err != nil { + return err + } + + // Encode parameter types. + for _, param := range parameterTypes { + err = e.encodeParameterTypeValue(param, visited) + if err != nil { + return err + } + } + + return nil +} + +// encodeParameterTypeValue encodes composite initializer parameter as +// language=CDDL +// +// [ +// label: tstr, +// identifier: tstr, +// type: type-value +// ] +func (e *Encoder) encodeParameterTypeValue(parameterType cadence.Parameter, visited ccfTypeIDByCadenceType) error { + // Encode CBOR array head with length 3 + err := e.enc.EncodeRawBytes([]byte{ + // array, 3 items follow + 0x83, + }) + if err != nil { + return err + } + + // element 0: label as tstr. + err = e.enc.EncodeString(parameterType.Label) + if err != nil { + return err + } + + // element 1: identifier as tstr. + err = e.enc.EncodeString(parameterType.Identifier) + if err != nil { + return err + } + + // element 2: type as type-value. + return e.encodeTypeValue(parameterType.Type, visited) +} + +// encodeFunctionTypeValue encodes cadence.FunctionType as +// language=CDDL +// function-type-value = +// +// ; cbor-tag-function-type-value +// #6.193(function-value) +func (e *Encoder) encodeFunctionTypeValue(typ *cadence.FunctionType, visited ccfTypeIDByCadenceType) error { + // Encode tag number and array head of length 3. + err := e.enc.EncodeRawBytes([]byte{ + // tag number + 0xd8, CBORTagFunctionTypeValue, + }) + if err != nil { + return err + } + + // Encode function-value. + return e.encodeFunction(typ, visited) +} + +// encodeNilTypeValue encodes nil type value as CBOR nil. +func (e *Encoder) encodeNilTypeValue() error { + return e.enc.EncodeNil() +} + +// needToEncodeRuntimeType returns true if runtimeType needs to be encoded because: +// - static type is missing (top level value doesn't have static type) +// - static type is different from runtime type (static type is abstract type) +func needToEncodeRuntimeType(staticType cadence.Type, runtimeType cadence.Type) bool { + if staticType == nil { + return true + } + if staticType.Equal(runtimeType) { + return false + } + + // Here, static type is different from runtime type. + + switch staticType := staticType.(type) { + case *cadence.OptionalType: + // Handle special case of runtime type being OptionalType{NeverType}. + // We handle special case of Optional{nil} because its runtime type is OptionalType{NeverType} + // while its static type can be different, such as OptionalType{AddressType}. + // For example, TokensDeposited event is defined as `TokensDeposited(amount: UFix64, to: Address?)`, + // field to's type is OptionalType{AddressType} and its value can be nil with runtime type + // OptionalType{NeverType}. Even though runtime type is different from static type (field type), + // encoder encodes nil value without encoding its runtime type. + if isOptionalNeverType(runtimeType) { + return false + } + + // If both staticType and runtimeType are optional types, check again + // with unwrapped inner types. For example, runtimeType shouldn't be + // encoded if staticType is optional reference to string type and + // runtimeType is optional string type. After unwrapping optional + // types, needToEncodeRuntimeType returns false because staticType is + // reference to string type and runtimeType is string type. + if or, ok := runtimeType.(*cadence.OptionalType); ok { + return needToEncodeRuntimeType(staticType.Type, or.Type) + } + + case *cadence.ReferenceType: + // Handle special case of static type being ReferenceType. + // Encoder doesn't need to encode runtime type if runtime type is the deferenced type of static type. + return needToEncodeRuntimeType(staticType.Type, runtimeType) + } + + return true +} + +// getTypeToEncodeAsCCFInlineType returns runtime type to be encoded after +// removing redundant type info that is present in staticType because +// staticType is already encoded at higher level. +func getTypeToEncodeAsCCFInlineType(staticType cadence.Type, runtimeType cadence.Type) cadence.Type { + for { + switch st := staticType.(type) { + case *cadence.OptionalType: + rot, ok := runtimeType.(*cadence.OptionalType) + if !ok { + // staticType is optional type while runtime type isn't. + panic(cadenceErrors.NewUnexpectedError("static type (%T) is optional type while runtime type (%T) isn't", staticType, runtimeType)) + } + + // Unwrap optional type container from both staticType and runtimeType. + // Static type is encoded at higher level, so encoded runtime type shouldn't repeat + // the same info. Here, inline type is runtime type after unwrapping optional type + // that is present in both static and runtime types. + staticType = st.Type + runtimeType = rot.Type + + case *cadence.ReferenceType: + // Unwrap reference type from static type and try again. + staticType = st.Type + + default: + return runtimeType + } + } +} + +// isOptionalNeverType returns true if t is (nested) optional never type. +func isOptionalNeverType(t cadence.Type) bool { + for { + ot, ok := t.(*cadence.OptionalType) + if !ok { + return false + } + + if ot.Type.Equal(cadence.NewNeverType()) { + return true + } + t = ot.Type + } +} + +var bufferPool = sync.Pool{ + New: func() interface{} { + e := new(bytes.Buffer) + e.Grow(64) + return e + }, +} + +func getBuffer() *bytes.Buffer { + return bufferPool.Get().(*bytes.Buffer) +} + +func putBuffer(e *bytes.Buffer) { + e.Reset() + bufferPool.Put(e) +} + +func (e *Encoder) getSortedFieldIndex(t cadence.CompositeType) []int { + cadenceTypeID := t.ID() + + if indexes, ok := e.cachedSortedFieldIndex[cadenceTypeID]; ok { + return indexes + } + + // NOTE: bytewiseFieldIdentifierSorter doesn't sort fields in place. + // bytewiseFieldIdentifierSorter.indexes is used as sorted fieldTypes + // index. + sorter := newBytewiseFieldSorter(t.CompositeFields()) + + sort.Sort(sorter) + + e.cachedSortedFieldIndex[cadenceTypeID] = sorter.indexes + return sorter.indexes +} diff --git a/encoding/ccf/encode_type.go b/encoding/ccf/encode_type.go new file mode 100644 index 0000000000..5b800d55d1 --- /dev/null +++ b/encoding/ccf/encode_type.go @@ -0,0 +1,518 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "sort" + + "github.com/onflow/cadence" + cadenceErrors "github.com/onflow/cadence/runtime/errors" +) + +type encodeTypeFn func(typ cadence.Type, tids ccfTypeIDByCadenceType) error + +// encodeInlineType encodes cadence.Type as +// language=CDDL +// inline-type = +// +// simple-type +// / optional-type +// / varsized-array-type +// / constsized-array-type +// / dict-type +// / reference-type +// / restricted-type +// / capability-type +// / type-ref +// +// All exported Cadence types need to be supported by this function, +// including abstract and interface types. +func (e *Encoder) encodeInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType) error { + simpleTypeID, ok := simpleTypeIDByType(typ) + if ok { + return e.encodeSimpleType(simpleTypeID) + } + + switch typ := typ.(type) { + case *cadence.OptionalType: + return e.encodeOptionalType(typ, tids) + + case *cadence.VariableSizedArrayType: + return e.encodeVarSizedArrayType(typ, tids) + + case *cadence.ConstantSizedArrayType: + return e.encodeConstantSizedArrayType(typ, tids) + + case *cadence.DictionaryType: + return e.encodeDictType(typ, tids) + + case cadence.CompositeType, cadence.InterfaceType: + id, err := tids.id(typ) + if err != nil { + panic(cadenceErrors.NewUnexpectedErrorFromCause(err)) + } + return e.encodeTypeRef(id) + + case *cadence.ReferenceType: + return e.encodeReferenceType(typ, tids) + + case *cadence.RestrictedType: + return e.encodeRestrictedType(typ, tids) + + case *cadence.CapabilityType: + return e.encodeCapabilityType(typ, tids) + + case *cadence.FunctionType: + return e.encodeSimpleType(TypeFunction) + + default: + panic(cadenceErrors.NewUnexpectedError("unsupported type %s (%T)", typ.ID(), typ)) + } +} + +func (e *Encoder) encodeNullableInlineType(typ cadence.Type, tids ccfTypeIDByCadenceType) error { + if typ == nil { + return e.enc.EncodeNil() + } + return e.encodeInlineType(typ, tids) +} + +// encodeSimpleType encodes cadence simple type as +// language=CDDL +// simple-type = +// +// ; cbor-tag-simple-type +// #6.137(simple-type-id) +func (e *Encoder) encodeSimpleType(id uint64) error { + rawTagNum := []byte{0xd8, CBORTagSimpleType} + return e.encodeSimpleTypeWithRawTag(id, rawTagNum) +} + +// encodeSimpleTypeWithRawTag encodes simple type with given tag number as +// language=CDDL +// simple-type-id = uint +func (e *Encoder) encodeSimpleTypeWithRawTag(id uint64, rawTagNumber []byte) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode simple-type-id as uint. + return e.enc.EncodeUint64(id) +} + +// encodeOptionalType encodes cadence.OptionalType as +// language=CDDL +// optional-type = +// +// ; cbor-tag-optional-type +// #6.138(inline-type) +func (e *Encoder) encodeOptionalType( + typ *cadence.OptionalType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagOptionalType} + return e.encodeOptionalTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeOptionalTypeWithRawTag encodes cadence.OptionalType +// with given tag number and encode type function. +func (e *Encoder) encodeOptionalTypeWithRawTag( + typ *cadence.OptionalType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode non-optional type with given encodeTypeFn. + return encodeTypeFn(typ.Type, tids) +} + +// encodeVarSizedArrayType encodes cadence.VariableSizedArrayType as +// language=CDDL +// varsized-array-type = +// +// ; cbor-tag-varsized-array-type +// #6.139(inline-type) +func (e *Encoder) encodeVarSizedArrayType( + typ *cadence.VariableSizedArrayType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagVarsizedArrayType} + return e.encodeVarSizedArrayTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeVarSizedArrayTypeWithRawTag encodes cadence.VariableSizedArrayType +// with given tag number and encode type function. +func (e *Encoder) encodeVarSizedArrayTypeWithRawTag( + typ *cadence.VariableSizedArrayType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array element type with given encodeTypeFn. + return encodeTypeFn(typ.ElementType, tids) +} + +// encodeConstantSizedArrayType encodes cadence.ConstantSizedArrayType as +// language=CDDL +// constsized-array-type = +// +// ; cbor-tag-constsized-array-type +// #6.140([ +// array-size: uint, +// element-type: inline-type +// ]) +func (e *Encoder) encodeConstantSizedArrayType( + typ *cadence.ConstantSizedArrayType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagConstsizedArrayType} + return e.encodeConstantSizedArrayTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeConstantSizedArrayTypeWithRawTag encodes cadence.ConstantSizedArrayType +// with given tag number and encode type function as +func (e *Encoder) encodeConstantSizedArrayTypeWithRawTag( + typ *cadence.ConstantSizedArrayType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: array size as uint + err = e.enc.EncodeUint(typ.Size) + if err != nil { + return err + } + + // element 1: array element type with given encodeTypeFn + return encodeTypeFn(typ.ElementType, tids) +} + +// encodeDictType encodes cadence.DictionaryType as +// language=CDDL +// dict-type = +// +// ; cbor-tag-dict-type +// #6.141([ +// key-type: inline-type, +// element-type: inline-type +// ]) +func (e *Encoder) encodeDictType( + typ *cadence.DictionaryType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagDictType} + return e.encodeDictTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeDictTypeWithRawTag encodes cadence.DictionaryType +// with given tag number and encode type function. +func (e *Encoder) encodeDictTypeWithRawTag( + typ *cadence.DictionaryType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: key type with given encodeTypeFn + err = encodeTypeFn(typ.KeyType, tids) + if err != nil { + return err + } + + // element 1: element type with given encodeTypeFn + return encodeTypeFn(typ.ElementType, tids) +} + +// encodeReferenceType encodes cadence.ReferenceType as +// language=CDDL +// reference-type = +// +// ; cbor-tag-reference-type +// #6.142([ +// authorized: bool, +// type: inline-type, +// ]) +func (e *Encoder) encodeReferenceType( + typ *cadence.ReferenceType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagReferenceType} + return e.encodeReferenceTypeWithRawTag( + typ, + tids, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeReferenceTypeWithRawTag encodes cadence.ReferenceType +// with given tag number and encode type function. +func (e *Encoder) encodeReferenceTypeWithRawTag( + typ *cadence.ReferenceType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: authorized as bool + err = e.enc.EncodeBool(typ.Authorized) + if err != nil { + return err + } + + // element 1: referenced type with given encodeTypeFn + return encodeTypeFn(typ.Type, tids) +} + +// encodeRestrictedType encodes cadence.RestrictedType as +// language=CDDL +// restricted-type = +// +// ; cbor-tag-restricted-type +// #6.143([ +// type: inline-type / nil, +// restrictions: [* inline-type] +// ]) +func (e *Encoder) encodeRestrictedType(typ *cadence.RestrictedType, tids ccfTypeIDByCadenceType) error { + rawTagNum := []byte{0xd8, CBORTagRestrictedType} + return e.encodeRestrictedTypeWithRawTag( + typ, + tids, + e.encodeNullableInlineType, + e.encodeInlineType, + rawTagNum, + ) +} + +// encodeRestrictedTypeWithRawTag encodes cadence.RestrictedType +// with given tag number and encode type function. +func (e *Encoder) encodeRestrictedTypeWithRawTag( + typ *cadence.RestrictedType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + encodeRestrictionTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: type with given encodeTypeFn + err = encodeTypeFn(typ.Type, tids) + if err != nil { + return err + } + + // element 1: restrictions as array. + + // Encode array head with number of restrictions. + restrictions := typ.Restrictions + err = e.enc.EncodeArrayHead(uint64(len(restrictions))) + if err != nil { + return err + } + + switch len(restrictions) { + case 0: + // Short-circuit if there is no restriction. + return nil + + case 1: + // Avoid overhead of sorting if there is only one restriction. + // Encode restriction type with given encodeTypeFn. + return encodeTypeFn(restrictions[0], tids) + + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "restricted-type.restrictions MUST be sorted by restriction's cadence-type-id" + // "restricted-type-value.restrictions MUST be sorted by restriction's cadence-type-id." + sorter := newBytewiseCadenceTypeSorter(restrictions) + + sort.Sort(sorter) + + for _, index := range sorter.indexes { + // Encode restriction type with given encodeTypeFn. + err = encodeRestrictionTypeFn(restrictions[index], tids) + if err != nil { + return err + } + } + + return nil + } +} + +// encodeCapabilityType encodes cadence.CapabilityType as +// language=CDDL +// capability-type = +// +// ; cbor-tag-capability-type +// ; use an array as an extension point +// #6.144([ +// ; borrow-type +// inline-type / nil +// ]) +func (e *Encoder) encodeCapabilityType( + typ *cadence.CapabilityType, + tids ccfTypeIDByCadenceType, +) error { + rawTagNum := []byte{0xd8, CBORTagCapabilityType} + return e.encodeCapabilityTypeWithRawTag( + typ, + tids, + e.encodeNullableInlineType, + rawTagNum, + ) +} + +// encodeCapabilityTypeWithRawTag encodes cadence.CapabilityType +// with given tag number and encode type function. +func (e *Encoder) encodeCapabilityTypeWithRawTag( + typ *cadence.CapabilityType, + tids ccfTypeIDByCadenceType, + encodeTypeFn encodeTypeFn, + rawTagNumber []byte, +) error { + // Encode CBOR tag number. + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + + // Encode array head of length 1. + err = e.enc.EncodeArrayHead(1) + if err != nil { + return err + } + + // element 0: borrow type with given encodeTypeFn. + return encodeTypeFn(typ.BorrowType, tids) +} + +// encodeTypeRef encodes CCF type id as +// language=CDDL +// type-ref = +// +// ; cbor-tag-type-ref +// #6.136(id) +func (e *Encoder) encodeTypeRef(ref ccfTypeID) error { + rawTagNum := []byte{0xd8, CBORTagTypeRef} + return e.encodeTypeRefWithRawTag(ref, rawTagNum) +} + +// encodeTypeRefWithRawTag encodes CCF type ID as +// with given tag number. +func (e *Encoder) encodeTypeRefWithRawTag(ref ccfTypeID, rawTagNumber []byte) error { + err := e.enc.EncodeRawBytes(rawTagNumber) + if err != nil { + return err + } + return e.encodeCCFTypeID(ref) +} + +// encodeCCFTypeID encodes CCF type ID as +// language=CDDL +// id = bstr +func (e *Encoder) encodeCCFTypeID(id ccfTypeID) error { + return e.enc.EncodeBytes(id.Bytes()) +} + +// encodeCadenceTypeID encodes Cadence type ID as +// language=CDDL +// cadence-type-id = tstr +func (e *Encoder) encodeCadenceTypeID(id string) error { + return e.enc.EncodeString(id) +} diff --git a/encoding/ccf/encode_typedef.go b/encoding/ccf/encode_typedef.go new file mode 100644 index 0000000000..a0fc21f206 --- /dev/null +++ b/encoding/ccf/encode_typedef.go @@ -0,0 +1,255 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "github.com/onflow/cadence" + cadenceErrors "github.com/onflow/cadence/runtime/errors" +) + +// encodeCompositeType encodes cadence.CompositeType in type definition as +// language=CDDL +// struct-type = +// +// ; cbor-tag-struct-type +// #6.160(composite-type) +// +// resource-type = +// +// ; cbor-tag-resource-type +// #6.161(composite-type) +// +// event-type = +// +// ; cbor-tag-event-type +// #6.162(composite-type) +// +// contract-type = +// +// ; cbor-tag-contract-type +// #6.163(composite-type) +// +// enum-type = +// +// ; cbor-tag-enum-type +// #6.164(composite-type) +// +// composite-type = [ +// +// id: id, +// cadence-type-id: cadence-type-id, +// fields: [ +// * [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +// +// ] +func (e *Encoder) encodeCompositeType(typ cadence.CompositeType, tids ccfTypeIDByCadenceType) error { + ccfID, err := tids.id(typ) + if err != nil { + panic(cadenceErrors.NewUnexpectedError("CCF type ID not found for composite type %s (%T)", typ.ID(), typ)) + } + + var cborTagNum uint64 + + switch typ := typ.(type) { + case *cadence.StructType: + cborTagNum = CBORTagStructType + + case *cadence.ResourceType: + cborTagNum = CBORTagResourceType + + case *cadence.EventType: + cborTagNum = CBORTagEventType + + case *cadence.ContractType: + cborTagNum = CBORTagContractType + + case *cadence.EnumType: + cborTagNum = CBORTagEnumType + + default: + panic(cadenceErrors.NewUnexpectedError("unexpected composite type %s (%T)", typ.ID(), typ)) + } + + // Encode tag number indicating composite type. + err = e.enc.EncodeTagHead(cborTagNum) + if err != nil { + return err + } + + // Encode array head of length 3. + err = e.enc.EncodeArrayHead(3) + if err != nil { + return err + } + + // element 0: CCF type id + err = e.encodeCCFTypeID(ccfID) + if err != nil { + return err + } + + // element 1: cadence-type-id + err = e.encodeCadenceTypeID(typ.ID()) + if err != nil { + return err + } + + // element 2: fields as array + return e.encodeCompositeTypeFields(typ, tids) +} + +// encodeCompositeTypeFields encodes field types as +// language=CDDL +// +// fields: [ +// * [ +// field-name: tstr, +// field-type: inline-type +// ] +// ] +func (e *Encoder) encodeCompositeTypeFields(typ cadence.CompositeType, tids ccfTypeIDByCadenceType) error { + fieldTypes := typ.CompositeFields() + + // Encode array head with number of fields. + err := e.enc.EncodeArrayHead(uint64(len(fieldTypes))) + if err != nil { + return err + } + + switch len(fieldTypes) { + case 0: + // Short-circuit if there is no field type. + return nil + + case 1: + // Avoid overhead of sorting if there is only one field. + return e.encodeCompositeTypeField(fieldTypes[0], tids) + + default: + // "Deterministic CCF Encoding Requirements" in CCF specs: + // + // "composite-type.fields MUST be sorted by name" + sortedIndexes := e.getSortedFieldIndex(typ) + + for _, index := range sortedIndexes { + // Encode field + err = e.encodeCompositeTypeField(fieldTypes[index], tids) + if err != nil { + return err + } + } + + return nil + } +} + +// encodeCompositeTypeField encodes field type as +// language=CDDL +// +// [ +// field-name: tstr, +// field-type: inline-type +// ] +func (e *Encoder) encodeCompositeTypeField(typ cadence.Field, tids ccfTypeIDByCadenceType) error { + // Encode array head of length 2. + err := e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: field identifier as tstr + err = e.enc.EncodeString(typ.Identifier) + if err != nil { + return err + } + + // element 1: field type as inline-type + return e.encodeInlineType(typ.Type, tids) +} + +// encodeInterfaceType encodes cadence.InterfaceType as +// language=CDDL +// struct-interface-type = +// +// ; cbor-tag-struct-interface-type +// #6.176(interface-type) +// +// resource-interface-type = +// +// ; cbor-tag-resource-interface-type +// #6.177(interface-type) +// +// contract-interface-type = +// +// ; cbor-tag-contract-interface-type +// #6.178(interface-type) +// +// interface-type = [ +// +// id: id, +// cadence-type-id: tstr, +// +// ] +func (e *Encoder) encodeInterfaceType(typ cadence.InterfaceType, tids ccfTypeIDByCadenceType) error { + ccfID, err := tids.id(typ) + if err != nil { + panic(cadenceErrors.NewUnexpectedError("CCF type ID not found for interface type %s (%T)", typ.ID(), typ)) + } + + var cborTagNum uint64 + + switch typ := typ.(type) { + case *cadence.StructInterfaceType: + cborTagNum = CBORTagStructInterfaceType + + case *cadence.ResourceInterfaceType: + cborTagNum = CBORTagResourceInterfaceType + + case *cadence.ContractInterfaceType: + cborTagNum = CBORTagContractInterfaceType + + default: + panic(cadenceErrors.NewUnexpectedError("unexpected interface type %s (%T)", typ.ID(), typ)) + } + + // Encode tag number indicating interface type. + err = e.enc.EncodeTagHead(cborTagNum) + if err != nil { + return err + } + + // Encode array head with length 2. + err = e.enc.EncodeArrayHead(2) + if err != nil { + return err + } + + // element 0: CCF type ID + err = e.encodeCCFTypeID(ccfID) + if err != nil { + return err + } + + // element 1: cadence-type-id + return e.encodeCadenceTypeID(typ.ID()) +} diff --git a/encoding/ccf/simple_type_utils.go b/encoding/ccf/simple_type_utils.go new file mode 100644 index 0000000000..66531e94b3 --- /dev/null +++ b/encoding/ccf/simple_type_utils.go @@ -0,0 +1,247 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import "github.com/onflow/cadence" + +// IMPORTANT: +// +// Don't change existing simple type IDs. +// +// When new simple cadence.Type is added, +// - add new ID to the end of existing IDs, +// - add new simple cadence.Type and its ID in simpleTypeIDByType() + +const ( // Cadence simple type IDs + TypeBool = iota + TypeString + TypeCharacter + TypeAddress + TypeInt + TypeInt8 + TypeInt16 + TypeInt32 + TypeInt64 + TypeInt128 + TypeInt256 + TypeUInt + TypeUInt8 + TypeUInt16 + TypeUInt32 + TypeUInt64 + TypeUInt128 + TypeUInt256 + TypeWord8 + TypeWord16 + TypeWord32 + TypeWord64 + TypeFix64 + TypeUFix64 + TypePath + TypeCapabilityPath + TypeStoragePath + TypePublicPath + TypePrivatePath + TypeAuthAccount + TypePublicAccount + TypeAuthAccountKeys + TypePublicAccountKeys + TypeAuthAccountContracts + TypePublicAccountContracts + TypeDeployedContract + TypeAccountKey + TypeBlock + TypeAny + TypeAnyStruct + TypeAnyResource + TypeMetaType + TypeNever + TypeNumber + TypeSignedNumber + TypeInteger + TypeSignedInteger + TypeFixedPoint + TypeSignedFixedPoint + TypeBytes + TypeVoid + TypeFunction +) + +// NOTE: cadence.FunctionType isn't included in simpleTypeIDByType +// because this function is used by both inline-type and type-value. +// cadence.FunctionType needs to be handled differently when this +// function is used by inline-type and type-value. +func simpleTypeIDByType(typ cadence.Type) (uint64, bool) { + switch typ.(type) { + case cadence.AnyType: + return TypeAny, true + + case cadence.AnyStructType: + return TypeAnyStruct, true + + case cadence.AnyResourceType: + return TypeAnyResource, true + + case cadence.AddressType: + return TypeAddress, true + + case cadence.MetaType: + return TypeMetaType, true + + case cadence.VoidType: + return TypeVoid, true + + case cadence.NeverType: + return TypeNever, true + + case cadence.BoolType: + return TypeBool, true + + case cadence.StringType: + return TypeString, true + + case cadence.CharacterType: + return TypeCharacter, true + + case cadence.BytesType: + return TypeBytes, true + + case cadence.NumberType: + return TypeNumber, true + + case cadence.SignedNumberType: + return TypeSignedNumber, true + + case cadence.IntegerType: + return TypeInteger, true + + case cadence.SignedIntegerType: + return TypeSignedInteger, true + + case cadence.FixedPointType: + return TypeFixedPoint, true + + case cadence.SignedFixedPointType: + return TypeSignedFixedPoint, true + + case cadence.IntType: + return TypeInt, true + + case cadence.Int8Type: + return TypeInt8, true + + case cadence.Int16Type: + return TypeInt16, true + + case cadence.Int32Type: + return TypeInt32, true + + case cadence.Int64Type: + return TypeInt64, true + + case cadence.Int128Type: + return TypeInt128, true + + case cadence.Int256Type: + return TypeInt256, true + + case cadence.UIntType: + return TypeUInt, true + + case cadence.UInt8Type: + return TypeUInt8, true + + case cadence.UInt16Type: + return TypeUInt16, true + + case cadence.UInt32Type: + return TypeUInt32, true + + case cadence.UInt64Type: + return TypeUInt64, true + + case cadence.UInt128Type: + return TypeUInt128, true + + case cadence.UInt256Type: + return TypeUInt256, true + + case cadence.Word8Type: + return TypeWord8, true + + case cadence.Word16Type: + return TypeWord16, true + + case cadence.Word32Type: + return TypeWord32, true + + case cadence.Word64Type: + return TypeWord64, true + + case cadence.Fix64Type: + return TypeFix64, true + + case cadence.UFix64Type: + return TypeUFix64, true + + case cadence.BlockType: + return TypeBlock, true + + case cadence.PathType: + return TypePath, true + + case cadence.CapabilityPathType: + return TypeCapabilityPath, true + + case cadence.StoragePathType: + return TypeStoragePath, true + + case cadence.PublicPathType: + return TypePublicPath, true + + case cadence.PrivatePathType: + return TypePrivatePath, true + + case cadence.AccountKeyType: + return TypeAccountKey, true + + case cadence.AuthAccountContractsType: + return TypeAuthAccountContracts, true + + case cadence.AuthAccountKeysType: + return TypeAuthAccountKeys, true + + case cadence.AuthAccountType: + return TypeAuthAccount, true + + case cadence.PublicAccountContractsType: + return TypePublicAccountContracts, true + + case cadence.PublicAccountKeysType: + return TypePublicAccountKeys, true + + case cadence.PublicAccountType: + return TypePublicAccount, true + + case cadence.DeployedContractType: + return TypeDeployedContract, true + } + + return 0, false +} diff --git a/encoding/ccf/sort.go b/encoding/ccf/sort.go new file mode 100644 index 0000000000..d0165ac7c9 --- /dev/null +++ b/encoding/ccf/sort.go @@ -0,0 +1,167 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "bytes" + + "github.com/onflow/cadence" +) + +// bytewiseFieldSorter + +// bytewiseFieldSorter is used to sort fields by identifier. +// NOTE: in order to avoid making a copy of fields for sorting, +// - create sorter by calling newBytewiseFieldIdentifierSorter() +// - sort by calling sort.Sort(sorter) +// - iterate sorted fields by +// for _, index := range sorter.indexes { +// // process sorted field at fields[index] +// } +type bytewiseFieldSorter struct { + // NOTE: DON'T sort fields in place because it isn't a copy. + // Instead, sort indexes by field identifier. + fields []cadence.Field + // indexes represents sorted indexes of fields + indexes []int +} + +func newBytewiseFieldSorter(types []cadence.Field) bytewiseFieldSorter { + indexes := make([]int, len(types)) + for i := 0; i < len(indexes); i++ { + indexes[i] = i + } + return bytewiseFieldSorter{fields: types, indexes: indexes} +} + +func (x bytewiseFieldSorter) Len() int { + return len(x.indexes) +} + +func (x bytewiseFieldSorter) Swap(i, j int) { + x.indexes[i], x.indexes[j] = x.indexes[j], x.indexes[i] +} + +func (x bytewiseFieldSorter) Less(i, j int) bool { + i = x.indexes[i] + j = x.indexes[j] + + iIdentifier := x.fields[i].Identifier + jIdentifier := x.fields[j].Identifier + + if len(iIdentifier) != len(jIdentifier) { + return len(iIdentifier) < len(jIdentifier) + } + return iIdentifier <= jIdentifier +} + +// bytewiseKeyValuePairSorter + +type encodedKeyValuePair struct { + encodedKey []byte + encodedPair []byte + keyLength, pairLength int +} + +type bytewiseKeyValuePairSorter []encodedKeyValuePair + +func (x bytewiseKeyValuePairSorter) Len() int { + return len(x) +} + +func (x bytewiseKeyValuePairSorter) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x bytewiseKeyValuePairSorter) Less(i, j int) bool { + return bytes.Compare(x[i].encodedKey, x[j].encodedKey) <= 0 +} + +// bytewiseCadenceTypeInPlaceSorter + +// bytewiseCadenceTypeInPlaceSorter is used to sort Cadence types by Cadence type ID. +type bytewiseCadenceTypeInPlaceSorter []cadence.Type + +func (t bytewiseCadenceTypeInPlaceSorter) Len() int { + return len(t) +} + +func (t bytewiseCadenceTypeInPlaceSorter) Swap(i, j int) { + t[i], t[j] = t[j], t[i] +} + +func (t bytewiseCadenceTypeInPlaceSorter) Less(i, j int) bool { + iID := t[i].ID() + jID := t[j].ID() + if len(iID) != len(jID) { + return len(iID) < len(jID) + } + return iID <= jID +} + +// bytewiseCadenceTypeSorter + +// bytewiseCadenceTypeSorter is used to sort Cadence types by Cadence type ID. +type bytewiseCadenceTypeSorter struct { + // NOTE: DON'T sort types in place because it isn't a copy. + // Instead, sort indexes by Cadence type ID. + types []cadence.Type + // indexes represents sorted indexes of fields + indexes []int +} + +func newBytewiseCadenceTypeSorter(types []cadence.Type) bytewiseCadenceTypeSorter { + indexes := make([]int, len(types)) + for i := 0; i < len(indexes); i++ { + indexes[i] = i + } + return bytewiseCadenceTypeSorter{types: types, indexes: indexes} +} + +func (t bytewiseCadenceTypeSorter) Len() int { + return len(t.indexes) +} + +func (t bytewiseCadenceTypeSorter) Swap(i, j int) { + t.indexes[i], t.indexes[j] = t.indexes[j], t.indexes[i] +} + +func (t bytewiseCadenceTypeSorter) Less(i, j int) bool { + i = t.indexes[i] + j = t.indexes[j] + + iID := t.types[i].ID() + jID := t.types[j].ID() + + if len(iID) != len(jID) { + return len(iID) < len(jID) + } + return iID <= jID +} + +// Utility sort functions + +func stringsAreSortedBytewise(s1, s2 string) bool { + return len(s1) < len(s2) || + (len(s1) == len(s2) && s1 < s2) +} + +func bytesAreSortedBytewise(b1, b2 []byte) bool { + return bytes.Compare(b1, b2) <= 0 +} diff --git a/encoding/ccf/traverse_value.go b/encoding/ccf/traverse_value.go new file mode 100644 index 0000000000..bc8e3ec500 --- /dev/null +++ b/encoding/ccf/traverse_value.go @@ -0,0 +1,235 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ccf + +import ( + "sort" + + "github.com/onflow/cadence" +) + +type compositeTypes struct { + ids ccfTypeIDByCadenceType + abstractTypes map[string]bool + types []cadence.Type +} + +// compositeTypesFromValue returns all composite/interface types for value v. +// Returned types are sorted unique list of static and runtime composite/interface types. +// NOTE: nested composite/interface types are included in the returned types. +func compositeTypesFromValue(v cadence.Value) ([]cadence.Type, ccfTypeIDByCadenceType) { + ct := &compositeTypes{ + ids: make(ccfTypeIDByCadenceType), + abstractTypes: make(map[string]bool), + types: make([]cadence.Type, 0, 1), + } + + // Traverse v to get all unique: + // - static composite types + // - static interface types + // - runtime composite types + // - runtime interface types + ct.traverseValue(v) + + if len(ct.ids) < 2 { + // No need to reassign ccf id, nor sort types. + return ct.types, ct.ids + } + + // Sort Cadence types by Cadence type ID. + sort.Sort(bytewiseCadenceTypeInPlaceSorter(ct.types)) + + // Assign sorted array index as local ccf ID. + for i, typ := range ct.types { + ct.ids[typ.ID()] = ccfTypeID(i) + } + + return ct.types, ct.ids +} + +func (ct *compositeTypes) traverseValue(v cadence.Value) { + + // Traverse type for composite/interface types. + checkRuntimeType := ct.traverseType(v.Type()) + + if !checkRuntimeType { + // Return without traversing value for runtime types. + return + } + + // Traverse v's elements for runtime types. + // Note: don't need to traverse fields of cadence.Enum + // because enum's field is an integer subtype. + switch v := v.(type) { + + case cadence.Optional: + ct.traverseValue(v.Value) + + case cadence.Array: + for _, element := range v.Values { + ct.traverseValue(element) + } + + case cadence.Dictionary: + for _, pair := range v.Pairs { + ct.traverseValue(pair.Key) + ct.traverseValue(pair.Value) + } + + case cadence.Struct: + for _, field := range v.Fields { + ct.traverseValue(field) + } + + case cadence.Resource: + for _, field := range v.Fields { + ct.traverseValue(field) + } + + case cadence.Event: + for _, field := range v.Fields { + ct.traverseValue(field) + } + + case cadence.Contract: + for _, field := range v.Fields { + ct.traverseValue(field) + } + + } +} + +// traverseType traverses cadence type typ to find composite/interface types and +// returns true if typ contains any abstract type and runtime type needs to be checked. +// It recurisvely traverse cadence.Type if the type contains other cadence.Type, +// such as OptionalType. +// Runtime needs to be checked when typ contains any abstract type. +func (ct *compositeTypes) traverseType(typ cadence.Type) (checkRuntimeType bool) { + switch typ := typ.(type) { + + case *cadence.OptionalType: + return ct.traverseType(typ.Type) + + case cadence.ArrayType: + return ct.traverseType(typ.Element()) + + case *cadence.DictionaryType: + checkKeyRuntimeType := ct.traverseType(typ.KeyType) + checkValueRuntimeType := ct.traverseType(typ.ElementType) + return checkKeyRuntimeType || checkValueRuntimeType + + case *cadence.CapabilityType: + return ct.traverseType(typ.BorrowType) + + case *cadence.ReferenceType: + return ct.traverseType(typ.Type) + + case *cadence.RestrictedType: + check := ct.traverseType(typ.Type) + for _, restriction := range typ.Restrictions { + checkRestriction := ct.traverseType(restriction) + check = check || checkRestriction + } + return check + + case cadence.CompositeType: // struct, resource, event, contract, enum + newType := ct.add(typ) + if !newType { + return ct.abstractTypes[typ.ID()] + } + + check := false + fields := typ.CompositeFields() + for _, field := range fields { + checkField := ct.traverseType(field.Type) + check = check || checkField + } + + // Don't need to traverse initializers because + // they are not encoded and their types aren't needed. + + ct.abstractTypes[typ.ID()] = check + + return check + + case cadence.InterfaceType: // struct interface, resource interface, contract interface + ct.add(typ) + // Don't need to traverse fields or initializers because + // they are not encoded and their types aren't needed. + + // Return true to check runtime type. + return true + + case cadence.VoidType, + cadence.BoolType, + cadence.NeverType, + cadence.CharacterType, + cadence.StringType, + cadence.BytesType, + cadence.AddressType, + cadence.IntType, + cadence.Int8Type, + cadence.Int16Type, + cadence.Int32Type, + cadence.Int64Type, + cadence.Int128Type, + cadence.Int256Type, + cadence.UIntType, + cadence.UInt8Type, + cadence.UInt16Type, + cadence.UInt32Type, + cadence.UInt64Type, + cadence.UInt128Type, + cadence.UInt256Type, + cadence.Word8Type, + cadence.Word16Type, + cadence.Word32Type, + cadence.Word64Type, + cadence.Fix64Type, + cadence.UFix64Type, + cadence.PathType, + cadence.StoragePathType, + cadence.PublicPathType, + cadence.PrivatePathType, + cadence.MetaType, + *cadence.FunctionType, + cadence.NumberType, + cadence.SignedNumberType, + cadence.IntegerType, + cadence.SignedIntegerType, + cadence.FixedPointType, + cadence.SignedFixedPointType: + // TODO: Maybe there are more types that we can skip checking runtime type for composite type. + + return false + + default: + return true + } +} + +func (ct *compositeTypes) add(t cadence.Type) bool { + cadenceTypeID := t.ID() + if _, ok := ct.ids[cadenceTypeID]; ok { + return false + } + ct.ids[cadenceTypeID] = 0 + ct.types = append(ct.types, t) + return true +} diff --git a/encoding/json/decode.go b/encoding/json/decode.go index 6ea90c3933..9f6c2d912b 100644 --- a/encoding/json/decode.go +++ b/encoding/json/decode.go @@ -111,29 +111,31 @@ func (d *Decoder) Decode() (value cadence.Value, err error) { } const ( - typeKey = "type" - kindKey = "kind" - valueKey = "value" - keyKey = "key" - nameKey = "name" - fieldsKey = "fields" - initializersKey = "initializers" - idKey = "id" - targetPathKey = "targetPath" - borrowTypeKey = "borrowType" - domainKey = "domain" - identifierKey = "identifier" - staticTypeKey = "staticType" - addressKey = "address" - pathKey = "path" - authorizedKey = "authorized" - sizeKey = "size" - typeIDKey = "typeID" - restrictionsKey = "restrictions" - labelKey = "label" - parametersKey = "parameters" - returnKey = "return" - purityKey = "purity" + typeKey = "type" + kindKey = "kind" + valueKey = "value" + keyKey = "key" + nameKey = "name" + fieldsKey = "fields" + initializersKey = "initializers" + idKey = "id" + targetPathKey = "targetPath" + borrowTypeKey = "borrowType" + domainKey = "domain" + identifierKey = "identifier" + staticTypeKey = "staticType" + addressKey = "address" + pathKey = "path" + authorizedKey = "authorized" + sizeKey = "size" + typeIDKey = "typeID" + restrictionsKey = "restrictions" + labelKey = "label" + parametersKey = "parameters" + typeParametersKey = "typeParameters" + returnKey = "return" + typeBoundKey = "typeBound" + purityKey = "purity" ) func (d *Decoder) decodeJSON(v any) cadence.Value { @@ -216,8 +218,6 @@ func (d *Decoder) decodeJSON(v any) cadence.Value { return d.decodeEvent(valueJSON) case contractTypeStr: return d.decodeContract(valueJSON) - case linkTypeStr: - return d.decodeLink(valueJSON) case pathTypeStr: return d.decodePath(valueJSON) case typeTypeStr: @@ -824,57 +824,57 @@ func (d *Decoder) decodeEnum(valueJSON any) cadence.Enum { )) } -func (d *Decoder) decodeLink(valueJSON any) cadence.PathLink { +func (d *Decoder) decodePath(valueJSON any) cadence.Path { obj := toObject(valueJSON) - targetPath, ok := d.decodeJSON(obj.Get(targetPathKey)).(cadence.Path) - if !ok { - panic(errors.NewDefaultUserError("invalid link: missing or invalid target path")) - } - - borrowType := obj.GetString(borrowTypeKey) + domain := common.PathDomainFromIdentifier(obj.GetString(domainKey)) - common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Link struct - Amount: uint64(len(borrowType)), - }) + identifier := obj.GetString(identifierKey) + common.UseMemory(d.gauge, common.NewRawStringMemoryUsage(len(identifier))) - return cadence.NewMeteredLink( + path, err := cadence.NewMeteredPath( d.gauge, - targetPath, - borrowType, + domain, + identifier, ) + if err != nil { + panic(errors.NewDefaultUserError("failed to decode path: %w", err)) + } + return path } -func (d *Decoder) decodePath(valueJSON any) cadence.Path { +func (d *Decoder) decodeTypeParameter(valueJSON any, results typeDecodingResults) cadence.TypeParameter { obj := toObject(valueJSON) + // Unmetered because decodeTypeParameter is metered in decodeTypeParameters and called nowhere else + typeBoundObj, ok := obj[typeBoundKey] + var typeBound cadence.Type + if ok { + typeBound = d.decodeType(typeBoundObj, results) + } - domain := obj.GetString(domainKey) + return cadence.NewTypeParameter( + toString(obj.Get(nameKey)), + typeBound, + ) +} +func (d *Decoder) decodeTypeParameters(typeParams []any, results typeDecodingResults) []cadence.TypeParameter { common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Path struct - Amount: uint64(len(domain)), + Kind: common.MemoryKindCadenceTypeParameter, + Amount: uint64(len(typeParams)), }) + typeParameters := make([]cadence.TypeParameter, 0, len(typeParams)) - identifier := obj.GetString(identifierKey) - common.UseMemory(d.gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Path struct - Amount: uint64(len(identifier)), - }) + for _, param := range typeParams { + typeParameters = append(typeParameters, d.decodeTypeParameter(param, results)) + } - return cadence.NewMeteredPath( - d.gauge, - domain, - identifier, - ) + return typeParameters } -func (d *Decoder) decodeParamType(valueJSON any, results typeDecodingResults) cadence.Parameter { +func (d *Decoder) decodeParameter(valueJSON any, results typeDecodingResults) cadence.Parameter { obj := toObject(valueJSON) - // Unmetered because decodeParamType is metered in decodeParamTypes and called nowhere else + // Unmetered because decodeParameter is metered in decodeParameters and called nowhere else return cadence.NewParameter( toString(obj.Get(labelKey)), toString(obj.Get(idKey)), @@ -882,7 +882,7 @@ func (d *Decoder) decodeParamType(valueJSON any, results typeDecodingResults) ca ) } -func (d *Decoder) decodeParamTypes(params []any, results typeDecodingResults) []cadence.Parameter { +func (d *Decoder) decodeParameters(params []any, results typeDecodingResults) []cadence.Parameter { common.UseMemory(d.gauge, common.MemoryUsage{ Kind: common.MemoryKindCadenceParameter, Amount: uint64(len(params)), @@ -890,7 +890,7 @@ func (d *Decoder) decodeParamTypes(params []any, results typeDecodingResults) [] parameters := make([]cadence.Parameter, 0, len(params)) for _, param := range params { - parameters = append(parameters, d.decodeParamType(param, results)) + parameters = append(parameters, d.decodeParameter(param, results)) } return parameters @@ -928,18 +928,19 @@ func (d *Decoder) decodePurity(purity any) cadence.FunctionPurity { return cadence.FunctionPurityUnspecified } -func (d *Decoder) decodeFunctionType(returnValue, parametersValue, id any, purity any, results typeDecodingResults) cadence.Type { - parameters := d.decodeParamTypes(toSlice(parametersValue), results) +func (d *Decoder) decodeFunctionType(typeParametersValue, parametersValue, returnValue any, purity any, results typeDecodingResults) cadence.Type { + typeParameters := d.decodeTypeParameters(toSlice(typeParametersValue), results) + parameters := d.decodeParameters(toSlice(parametersValue), results) returnType := d.decodeType(returnValue, results) functionPurity := d.decodePurity(purity) return cadence.NewMeteredFunctionType( d.gauge, - "", functionPurity, + typeParameters, parameters, returnType, - ).WithID(toString(id)) + ) } func (d *Decoder) decodeNominalType( @@ -954,7 +955,7 @@ func (d *Decoder) decodeNominalType( for _, params := range initializers { inits = append( inits, - d.decodeParamTypes(toSlice(params), results), + d.decodeParameters(toSlice(params), results), ) } @@ -1062,10 +1063,10 @@ func (d *Decoder) decodeNominalType( func (d *Decoder) decodeRestrictedType( typeValue any, restrictionsValue []any, - typeIDValue string, results typeDecodingResults, ) cadence.Type { typ := d.decodeType(typeValue, results) + restrictions := make([]cadence.Type, 0, len(restrictionsValue)) for _, restriction := range restrictionsValue { restrictions = append(restrictions, d.decodeType(restriction, results)) @@ -1073,10 +1074,9 @@ func (d *Decoder) decodeRestrictedType( return cadence.NewMeteredRestrictedType( d.gauge, - "", typ, restrictions, - ).WithID(typeIDValue) + ) } type typeDecodingResults map[string]cadence.Type @@ -1103,22 +1103,20 @@ func (d *Decoder) decodeType(valueJSON any, results typeDecodingResults) cadence switch kindValue { case "Function": - returnValue := obj.Get(returnKey) + typeParametersValue := obj.Get(typeParametersKey) parametersValue := obj.Get(parametersKey) - idValue := obj.Get(typeIDKey) + returnValue := obj.Get(returnKey) purity, hasPurity := obj[purityKey] if !hasPurity { purity = "impure" } - return d.decodeFunctionType(returnValue, parametersValue, idValue, purity, results) + return d.decodeFunctionType(typeParametersValue, parametersValue, returnValue, purity, results) case "Restriction": restrictionsValue := obj.Get(restrictionsKey) - typeIDValue := toString(obj.Get(typeIDKey)) typeValue := obj.Get(typeKey) return d.decodeRestrictedType( typeValue, toSlice(restrictionsValue), - typeIDValue, results, ) case "Optional": diff --git a/encoding/json/encode.go b/encoding/json/encode.go index e58c51478e..9b33a040f1 100644 --- a/encoding/json/encode.go +++ b/encoding/json/encode.go @@ -178,11 +178,15 @@ type jsonReferenceType struct { type jsonRestrictedType struct { Kind string `json:"kind"` - TypeID string `json:"typeID"` Type jsonValue `json:"type"` Restrictions []jsonValue `json:"restrictions"` } +type jsonTypeParameter struct { + Name string `json:"name"` + TypeBound jsonValue `json:"typeBound"` +} + type jsonParameterType struct { Type jsonValue `json:"type"` Label string `json:"label"` @@ -190,11 +194,11 @@ type jsonParameterType struct { } type jsonFunctionType struct { - Return jsonValue `json:"return"` - Kind string `json:"kind"` - TypeID string `json:"typeID"` - Purity string `json:"purity"` - Parameters []jsonParameterType `json:"parameters"` + Kind string `json:"kind"` + TypeParameters []jsonTypeParameter `json:"typeParameters"` + Parameters []jsonParameterType `json:"parameters"` + Return jsonValue `json:"return"` + Purity string `json:"purity"` } type jsonTypeValue struct { @@ -212,44 +216,45 @@ type jsonFunctionValue struct { } const ( - voidTypeStr = "Void" - optionalTypeStr = "Optional" - boolTypeStr = "Bool" - characterTypeStr = "Character" - stringTypeStr = "String" - addressTypeStr = "Address" - intTypeStr = "Int" - int8TypeStr = "Int8" - int16TypeStr = "Int16" - int32TypeStr = "Int32" - int64TypeStr = "Int64" - int128TypeStr = "Int128" - int256TypeStr = "Int256" - uintTypeStr = "UInt" - uint8TypeStr = "UInt8" - uint16TypeStr = "UInt16" - uint32TypeStr = "UInt32" - uint64TypeStr = "UInt64" - uint128TypeStr = "UInt128" - uint256TypeStr = "UInt256" - word8TypeStr = "Word8" - word16TypeStr = "Word16" - word32TypeStr = "Word32" - word64TypeStr = "Word64" - fix64TypeStr = "Fix64" - ufix64TypeStr = "UFix64" - arrayTypeStr = "Array" - dictionaryTypeStr = "Dictionary" - structTypeStr = "Struct" - resourceTypeStr = "Resource" - eventTypeStr = "Event" - contractTypeStr = "Contract" - linkTypeStr = "Link" - pathTypeStr = "Path" - typeTypeStr = "Type" - capabilityTypeStr = "Capability" - enumTypeStr = "Enum" - functionTypeStr = "Function" + voidTypeStr = "Void" + optionalTypeStr = "Optional" + boolTypeStr = "Bool" + characterTypeStr = "Character" + stringTypeStr = "String" + addressTypeStr = "Address" + intTypeStr = "Int" + int8TypeStr = "Int8" + int16TypeStr = "Int16" + int32TypeStr = "Int32" + int64TypeStr = "Int64" + int128TypeStr = "Int128" + int256TypeStr = "Int256" + uintTypeStr = "UInt" + uint8TypeStr = "UInt8" + uint16TypeStr = "UInt16" + uint32TypeStr = "UInt32" + uint64TypeStr = "UInt64" + uint128TypeStr = "UInt128" + uint256TypeStr = "UInt256" + word8TypeStr = "Word8" + word16TypeStr = "Word16" + word32TypeStr = "Word32" + word64TypeStr = "Word64" + fix64TypeStr = "Fix64" + ufix64TypeStr = "UFix64" + arrayTypeStr = "Array" + dictionaryTypeStr = "Dictionary" + structTypeStr = "Struct" + resourceTypeStr = "Resource" + eventTypeStr = "Event" + contractTypeStr = "Contract" + linkTypeStr = "Link" + accountLinkTypeStr = "AccountLink" + pathTypeStr = "Path" + typeTypeStr = "Type" + capabilityTypeStr = "Capability" + enumTypeStr = "Enum" + functionTypeStr = "Function" ) // Prepare traverses the object graph of the provided value and constructs @@ -321,7 +326,9 @@ func Prepare(v cadence.Value) jsonValue { case cadence.Contract: return prepareContract(x) case cadence.PathLink: - return prepareLink(x) + return preparePathLink(x) + case cadence.AccountLink: + return prepareAccountLink() case cadence.Path: return preparePath(x) case cadence.TypeValue: @@ -601,7 +608,7 @@ func prepareComposite(kind, id string, fieldTypes []cadence.Field, fields []cade } } -func prepareLink(x cadence.PathLink) jsonValue { +func preparePathLink(x cadence.PathLink) jsonValue { return jsonValueObject{ Type: linkTypeStr, Value: jsonPathLinkValue{ @@ -611,17 +618,35 @@ func prepareLink(x cadence.PathLink) jsonValue { } } +func prepareAccountLink() jsonValue { + return jsonEmptyValueObject{ + Type: accountLinkTypeStr, + } +} + func preparePath(x cadence.Path) jsonValue { return jsonValueObject{ Type: pathTypeStr, Value: jsonPathValue{ - Domain: x.Domain, + Domain: x.Domain.Identifier(), Identifier: x.Identifier, }, } } -func prepareParameterType(parameterType cadence.Parameter, results typePreparationResults) jsonParameterType { +func prepareTypeParameter(typeParameter cadence.TypeParameter, results typePreparationResults) jsonTypeParameter { + typeBound := typeParameter.TypeBound + var preparedTypeBound jsonValue + if typeBound != nil { + preparedTypeBound = prepareType(typeBound, results) + } + return jsonTypeParameter{ + Name: typeParameter.Name, + TypeBound: preparedTypeBound, + } +} + +func prepareParameter(parameterType cadence.Parameter, results typePreparationResults) jsonParameterType { return jsonParameterType{ Label: parameterType.Label, Id: parameterType.Identifier, @@ -637,27 +662,35 @@ func prepareFieldType(fieldType cadence.Field, results typePreparationResults) j } func prepareFields(fieldTypes []cadence.Field, results typePreparationResults) []jsonFieldType { - fields := make([]jsonFieldType, 0) - for _, field := range fieldTypes { - fields = append(fields, prepareFieldType(field, results)) + fields := make([]jsonFieldType, len(fieldTypes)) + for i, fieldType := range fieldTypes { + fields[i] = prepareFieldType(fieldType, results) } return fields } -func prepareParameters(parameterTypes []cadence.Parameter, results typePreparationResults) []jsonParameterType { - parameters := make([]jsonParameterType, 0) - for _, param := range parameterTypes { - parameters = append(parameters, prepareParameterType(param, results)) +func prepareTypeParameters(typeParameters []cadence.TypeParameter, results typePreparationResults) []jsonTypeParameter { + result := make([]jsonTypeParameter, len(typeParameters)) + for i, typeParameter := range typeParameters { + result[i] = prepareTypeParameter(typeParameter, results) + } + return result +} + +func prepareParameters(parameters []cadence.Parameter, results typePreparationResults) []jsonParameterType { + result := make([]jsonParameterType, len(parameters)) + for i, param := range parameters { + result[i] = prepareParameter(param, results) } - return parameters + return result } -func prepareInitializers(initializerTypes [][]cadence.Parameter, results typePreparationResults) [][]jsonParameterType { - initializers := make([][]jsonParameterType, 0) - for _, params := range initializerTypes { - initializers = append(initializers, prepareParameters(params, results)) +func prepareInitializers(initializers [][]cadence.Parameter, results typePreparationResults) [][]jsonParameterType { + result := make([][]jsonParameterType, len(initializers)) + for i, params := range initializers { + result[i] = prepareParameters(params, results) } - return initializers + return result } func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { @@ -817,10 +850,10 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { } case *cadence.FunctionType: typeJson := jsonFunctionType{ - Kind: "Function", - TypeID: typ.ID(), - Return: prepareType(typ.ReturnType, results), - Parameters: prepareParameters(typ.Parameters, results), + Kind: "Function", + TypeParameters: prepareTypeParameters(typ.TypeParameters, results), + Parameters: prepareParameters(typ.Parameters, results), + Return: prepareType(typ.ReturnType, results), } if typ.Purity == cadence.FunctionPurityView { typeJson.Purity = "view" @@ -833,13 +866,12 @@ func prepareType(typ cadence.Type, results typePreparationResults) jsonValue { Type: prepareType(typ.Type, results), } case *cadence.RestrictedType: - restrictions := make([]jsonValue, 0) - for _, restriction := range typ.Restrictions { - restrictions = append(restrictions, prepareType(restriction, results)) + restrictions := make([]jsonValue, len(typ.Restrictions)) + for i, restriction := range typ.Restrictions { + restrictions[i] = prepareType(restriction, results) } return jsonRestrictedType{ Kind: "Restriction", - TypeID: typ.ID(), Type: prepareType(typ.Type, results), Restrictions: restrictions, } diff --git a/encoding/json/encoding_test.go b/encoding/json/encoding_test.go index f8c42b7fb5..8c9db9cf76 100644 --- a/encoding/json/encoding_test.go +++ b/encoding/json/encoding_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence/runtime" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/tests/checker" @@ -1645,14 +1646,17 @@ func TestEncodeContract(t *testing.T) { testAllEncodeAndDecode(t, simpleContract, resourceContract) } -func TestEncodeLink(t *testing.T) { +func TestEncodePathLink(t *testing.T) { t.Parallel() - testEncodeAndDecode( + testEncode( t, cadence.NewPathLink( - cadence.NewPath("storage", "foo"), + cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, "Bar", ), // language=json @@ -1674,6 +1678,22 @@ func TestEncodeLink(t *testing.T) { ) } +func TestEncodeAccountLink(t *testing.T) { + + t.Parallel() + + testEncode( + t, + cadence.NewAccountLink(), + // language=json + ` + { + "type": "AccountLink" + } + `, + ) +} + func TestEncodeSimpleTypes(t *testing.T) { t.Parallel() @@ -2383,12 +2403,15 @@ func TestEncodeType(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: (&cadence.FunctionType{ + StaticType: &cadence.FunctionType{ + TypeParameters: []cadence.TypeParameter{ + {Name: "T", TypeBound: cadence.AnyStructType{}}, + }, Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, ReturnType: cadence.IntType{}, - }).WithID("Foo"), + }, }, // language=json ` @@ -2397,11 +2420,18 @@ func TestEncodeType(t *testing.T) { "value": { "staticType": { "kind": "Function", - "typeID": "Foo", "purity": "", "return": { "kind": "Int" }, + "typeParameters": [ + { + "name": "T", + "typeBound": { + "kind": "AnyStruct" + } + } + ], "parameters": [ { "label": "qux", @@ -2424,20 +2454,21 @@ func TestEncodeType(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: (&cadence.FunctionType{ + StaticType: &cadence.FunctionType{ Purity: cadence.FunctionPurityView, Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, - ReturnType: cadence.IntType{}, - }).WithID("Foo"), + ReturnType: cadence.IntType{}, + TypeParameters: []cadence.TypeParameter{}, + }, }, `{"type":"Type","value":{"staticType": { "kind" : "Function", - "typeID":"Foo", "purity": "view", - "return" : {"kind" : "Int"}, + "return" : {"kind" : "Int"}, + "typeParameters": [], "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} @@ -2452,8 +2483,8 @@ func TestEncodeType(t *testing.T) { encodedValue := `{"type":"Type","value":{"staticType": { "kind" : "Function", - "typeID":"Foo", - "return" : {"kind" : "Int"}, + "return" : {"kind" : "Int"}, + "typeParameters": [], "parameters" : [ {"label" : "qux", "id" : "baz", "type": {"kind" : "String"}} ]} @@ -2461,12 +2492,13 @@ func TestEncodeType(t *testing.T) { }` value := cadence.TypeValue{ - StaticType: (&cadence.FunctionType{ + StaticType: &cadence.FunctionType{ Parameters: []cadence.Parameter{ {Label: "qux", Identifier: "baz", Type: cadence.StringType{}}, }, - ReturnType: cadence.IntType{}, - }).WithID("Foo"), + ReturnType: cadence.IntType{}, + TypeParameters: []cadence.TypeParameter{}, + }, } decodedValue, err := json.Decode(nil, []byte(encodedValue)) @@ -2506,12 +2538,12 @@ func TestEncodeType(t *testing.T) { testEncodeAndDecode( t, cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ + StaticType: &cadence.RestrictedType{ Restrictions: []cadence.Type{ cadence.StringType{}, }, Type: cadence.IntType{}, - }).WithID("Int{String}"), + }, }, // language=json ` @@ -2520,7 +2552,6 @@ func TestEncodeType(t *testing.T) { "value": { "staticType": { "kind": "Restriction", - "typeID": "Int{String}", "type": { "kind": "Int" }, @@ -2557,7 +2588,10 @@ func TestEncodeCapability(t *testing.T) { testEncodeAndDecode( t, cadence.StorageCapability{ - Path: cadence.NewPath("storage", "foo"), + Path: cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, Address: cadence.BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: cadence.IntType{}, }, @@ -2981,12 +3015,57 @@ func TestEncodePath(t *testing.T) { t.Parallel() - testEncodeAndDecode( - t, - cadence.NewPath("storage", "foo"), - // language=json - `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, - ) + t.Run("storage", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, + // language=json + `{"type":"Path","value":{"domain":"storage","identifier":"foo"}}`, + ) + }) + + t.Run("private", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, + // language=json + `{"type":"Path","value":{"domain":"private","identifier":"foo"}}`, + ) + }) + + t.Run("public", func(t *testing.T) { + t.Parallel() + + testEncodeAndDecode( + t, + cadence.Path{ + Domain: common.PathDomainPublic, + Identifier: "foo", + }, + // language=json + `{"type":"Path","value":{"domain":"public","identifier":"foo"}}`, + ) + }) + + t.Run("invalid", func(t *testing.T) { + t.Parallel() + + _, err := json.Decode(nil, []byte( + // language=json + `{"type":"Path","value":{"domain":"Storage","identifier":"foo"}}`, + )) + require.ErrorContains(t, err, "unknown domain in path") + }) } func testAllEncodeAndDecode(t *testing.T, tests ...encodeTest) { @@ -3297,10 +3376,10 @@ func TestExportFunctionValue(t *testing.T) { testEncode( t, cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Parameters: []cadence.Parameter{}, ReturnType: cadence.VoidType{}, - }).WithID("fun():Void"), + }, }, // language=json ` @@ -3309,8 +3388,8 @@ func TestExportFunctionValue(t *testing.T) { "value": { "functionType": { "kind": "Function", - "typeID": "fun():Void", "parameters": [], + "typeParameters": [], "purity":"", "return": { "kind": "Void" diff --git a/go.mod b/go.mod index 9f69b8797a..de4ec6fead 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,11 @@ go 1.18 require ( github.com/bits-and-blooms/bitset v1.2.2 - github.com/bytecodealliance/wasmtime-go v0.40.0 github.com/c-bata/go-prompt v0.2.5 - github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f + github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c github.com/go-test/deep v1.0.5 github.com/google/go-cmp v0.5.9 // indirect - github.com/k0kubun/pp v3.0.1+incompatible github.com/leanovate/gopter v0.2.9 - github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 github.com/onflow/atree v0.5.0 github.com/rivo/uniseg v0.2.1-0.20211004051800-57c86be7915a github.com/schollz/progressbar/v3 v3.8.3 @@ -32,17 +29,21 @@ require ( github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c ) -require github.com/SaveTheRbtz/mph v0.1.2 +require ( + github.com/SaveTheRbtz/mph v0.1.2 + github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 + github.com/k0kubun/pp/v3 v3.2.0 + github.com/logrusorgru/aurora/v4 v4.0.0 +) require github.com/zeebo/xxh3 v1.0.2 // indirect require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fxamacker/circlehash v0.3.0 // indirect - github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect github.com/klauspost/cpuid/v2 v2.2.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect @@ -54,6 +55,5 @@ require ( golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/term v0.1.0 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d07665e9f8..1036526b32 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/SaveTheRbtz/mph v0.1.2 h1:5l3W496Up+7BNOVJQnJhzcGBh+wWfxWdmPUAkx3WmaM github.com/SaveTheRbtz/mph v0.1.2/go.mod h1:V4+WtKQPe2+dEA5os1WnGsEB0NR9qgqqgIiSt73+sT4= github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk= github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bytecodealliance/wasmtime-go v0.40.0 h1:7cGLQEctJf09JWBl3Ai0eMl1PTrXVAjkAb27+KHfIq0= -github.com/bytecodealliance/wasmtime-go v0.40.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= +github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 h1:/rBNjgFju2HCZnkPb1eL+W4GBwP8DMbaQu7i+GR9DH4= +github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA= github.com/c-bata/go-prompt v0.2.5 h1:3zg6PecEywxNn0xiqcXHD96fkbxghD+gdB2tbsYfl+Y= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc= @@ -12,40 +12,38 @@ github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f h1:dxTR4AaxCwuQv9LAVTAC2r1szlS+epeuPT5ClLKT6ZY= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= +github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= github.com/fxamacker/circlehash v0.3.0/go.mod h1:3aq3OfVvsWtkWMb6A1owjOQFA+TLsD5FgJflnaQwtMM= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.0 h1:4ZexSFt8agMNzNisrsilL6RClWDC5YJnLHNIfTy4iuc= github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= +github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -54,6 +52,7 @@ github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/onflow/atree v0.5.0 h1:y3lh8hY2fUo8KVE2ALVcz0EiNTq0tXJ6YTXKYVDA+3E= github.com/onflow/atree v0.5.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= @@ -128,8 +127,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -152,8 +151,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/npm-packages/cadence-parser/package.json b/npm-packages/cadence-parser/package.json index 970092b0f6..325ca99a4b 100644 --- a/npm-packages/cadence-parser/package.json +++ b/npm-packages/cadence-parser/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/cadence-parser", - "version": "0.37.0", + "version": "0.38.0", "description": "The Cadence parser", "homepage": "https://github.com/onflow/cadence", "repository": { diff --git a/runtime/account_test.go b/runtime/account_test.go index 3adf6d918e..6c3496c2aa 100644 --- a/runtime/account_test.go +++ b/runtime/account_test.go @@ -2521,7 +2521,10 @@ func TestRuntimeAccountLink(t *testing.T) { require.Equal(t, []cadence.Value{ cadence.NewAddress(common.MustBytesToAddress([]byte{0x1})), - cadence.NewPath("private", "foo"), + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, }, events[0].Fields, ) @@ -2762,7 +2765,10 @@ func TestRuntimeAccountLink(t *testing.T) { require.Equal(t, []cadence.Value{ cadence.NewAddress(common.MustBytesToAddress([]byte{0x1})), - cadence.NewPath("private", "foo"), + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, }, events[0].Fields, ) @@ -2869,7 +2875,10 @@ func TestRuntimeAccountLink(t *testing.T) { require.Equal(t, []cadence.Value{ cadence.NewAddress(common.MustBytesToAddress([]byte{0x1})), - cadence.NewPath("private", "foo"), + cadence.Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, }, events[0].Fields, ) diff --git a/runtime/ast/expression.go b/runtime/ast/expression.go index b6fa3fbfe5..3356f0dc9b 100644 --- a/runtime/ast/expression.go +++ b/runtime/ast/expression.go @@ -268,7 +268,7 @@ func (e *IntegerExpression) String() string { func (e *IntegerExpression) Doc() prettier.Doc { var b strings.Builder if e.Value.Sign() < 0 { - b.WriteRune('-') + b.WriteByte('-') } b.Write(e.PositiveLiteral) return prettier.Text(b.String()) @@ -359,7 +359,7 @@ func (e *FixedPointExpression) Doc() prettier.Doc { builder.WriteByte('.') fractional := e.Fractional.String() for i := uint(0); i < (e.Scale - uint(len(fractional))); i++ { - builder.WriteRune('0') + builder.WriteByte('0') } builder.WriteString(fractional) } diff --git a/runtime/attachments_test.go b/runtime/attachments_test.go index 8a5cadfaac..2b807cfc0e 100644 --- a/runtime/attachments_test.go +++ b/runtime/attachments_test.go @@ -223,7 +223,7 @@ func TestAccountAttachmentExportFailure(t *testing.T) { }, ) require.Error(t, err) - require.ErrorAs(t, err, &interpreter.InvalidatedResourceError{}) + require.ErrorAs(t, err, &interpreter.DestroyedResourceError{}) } func TestAccountAttachmentExport(t *testing.T) { diff --git a/runtime/cmd/execute/colors.go b/runtime/cmd/execute/colors.go index 8d6b2bbc9c..ed4e6988ef 100644 --- a/runtime/cmd/execute/colors.go +++ b/runtime/cmd/execute/colors.go @@ -19,7 +19,7 @@ package execute import ( - "github.com/logrusorgru/aurora" + "github.com/logrusorgru/aurora/v4" "github.com/onflow/cadence/runtime/interpreter" ) diff --git a/runtime/cmd/json-cdc/main.go b/runtime/cmd/json-cdc/main.go index 330527ef17..784909165c 100644 --- a/runtime/cmd/json-cdc/main.go +++ b/runtime/cmd/json-cdc/main.go @@ -25,7 +25,7 @@ import ( "io" "os" - "github.com/k0kubun/pp" + "github.com/k0kubun/pp/v3" jsoncdc "github.com/onflow/cadence/encoding/json" ) diff --git a/runtime/cmd/minifier/minifier.go b/runtime/cmd/minifier/minifier.go index 0616f37da1..735280393c 100644 --- a/runtime/cmd/minifier/minifier.go +++ b/runtime/cmd/minifier/minifier.go @@ -98,7 +98,7 @@ func minify(inputFile, outputFile string) error { } if !eof { - _, err = writer.WriteRune('\n') + _ = writer.WriteByte('\n') if err != nil { return err } diff --git a/runtime/common/memorykind.go b/runtime/common/memorykind.go index 1924f6591e..04ea030df6 100644 --- a/runtime/common/memorykind.go +++ b/runtime/common/memorykind.go @@ -101,6 +101,7 @@ const ( MemoryKindCadenceEnumValueBase MemoryKindCadenceEnumValueSize MemoryKindCadencePathLinkValue + MemoryKindCadenceAccountLinkValue MemoryKindCadencePathValue MemoryKindCadenceTypeValue MemoryKindCadenceStorageCapabilityValue @@ -113,6 +114,7 @@ const ( MemoryKindCadenceDictionaryType MemoryKindCadenceField MemoryKindCadenceParameter + MemoryKindCadenceTypeParameter MemoryKindCadenceStructType MemoryKindCadenceResourceType MemoryKindCadenceAttachmentType diff --git a/runtime/common/memorykind_string.go b/runtime/common/memorykind_string.go index 81146edb94..922940626b 100644 --- a/runtime/common/memorykind_string.go +++ b/runtime/common/memorykind_string.go @@ -76,130 +76,132 @@ func _() { _ = x[MemoryKindCadenceEnumValueBase-65] _ = x[MemoryKindCadenceEnumValueSize-66] _ = x[MemoryKindCadencePathLinkValue-67] - _ = x[MemoryKindCadencePathValue-68] - _ = x[MemoryKindCadenceTypeValue-69] - _ = x[MemoryKindCadenceStorageCapabilityValue-70] - _ = x[MemoryKindCadenceFunctionValue-71] - _ = x[MemoryKindCadenceOptionalType-72] - _ = x[MemoryKindCadenceVariableSizedArrayType-73] - _ = x[MemoryKindCadenceConstantSizedArrayType-74] - _ = x[MemoryKindCadenceDictionaryType-75] - _ = x[MemoryKindCadenceField-76] - _ = x[MemoryKindCadenceParameter-77] - _ = x[MemoryKindCadenceStructType-78] - _ = x[MemoryKindCadenceResourceType-79] - _ = x[MemoryKindCadenceAttachmentType-80] - _ = x[MemoryKindCadenceEventType-81] - _ = x[MemoryKindCadenceContractType-82] - _ = x[MemoryKindCadenceStructInterfaceType-83] - _ = x[MemoryKindCadenceResourceInterfaceType-84] - _ = x[MemoryKindCadenceContractInterfaceType-85] - _ = x[MemoryKindCadenceFunctionType-86] - _ = x[MemoryKindCadenceReferenceType-87] - _ = x[MemoryKindCadenceRestrictedType-88] - _ = x[MemoryKindCadenceCapabilityType-89] - _ = x[MemoryKindCadenceEnumType-90] - _ = x[MemoryKindRawString-91] - _ = x[MemoryKindAddressLocation-92] - _ = x[MemoryKindBytes-93] - _ = x[MemoryKindVariable-94] - _ = x[MemoryKindCompositeTypeInfo-95] - _ = x[MemoryKindCompositeField-96] - _ = x[MemoryKindInvocation-97] - _ = x[MemoryKindStorageMap-98] - _ = x[MemoryKindStorageKey-99] - _ = x[MemoryKindTypeToken-100] - _ = x[MemoryKindErrorToken-101] - _ = x[MemoryKindSpaceToken-102] - _ = x[MemoryKindProgram-103] - _ = x[MemoryKindIdentifier-104] - _ = x[MemoryKindArgument-105] - _ = x[MemoryKindBlock-106] - _ = x[MemoryKindFunctionBlock-107] - _ = x[MemoryKindParameter-108] - _ = x[MemoryKindParameterList-109] - _ = x[MemoryKindTypeParameter-110] - _ = x[MemoryKindTypeParameterList-111] - _ = x[MemoryKindTransfer-112] - _ = x[MemoryKindMembers-113] - _ = x[MemoryKindTypeAnnotation-114] - _ = x[MemoryKindDictionaryEntry-115] - _ = x[MemoryKindFunctionDeclaration-116] - _ = x[MemoryKindCompositeDeclaration-117] - _ = x[MemoryKindAttachmentDeclaration-118] - _ = x[MemoryKindInterfaceDeclaration-119] - _ = x[MemoryKindEnumCaseDeclaration-120] - _ = x[MemoryKindFieldDeclaration-121] - _ = x[MemoryKindTransactionDeclaration-122] - _ = x[MemoryKindImportDeclaration-123] - _ = x[MemoryKindVariableDeclaration-124] - _ = x[MemoryKindSpecialFunctionDeclaration-125] - _ = x[MemoryKindPragmaDeclaration-126] - _ = x[MemoryKindAssignmentStatement-127] - _ = x[MemoryKindBreakStatement-128] - _ = x[MemoryKindContinueStatement-129] - _ = x[MemoryKindEmitStatement-130] - _ = x[MemoryKindExpressionStatement-131] - _ = x[MemoryKindForStatement-132] - _ = x[MemoryKindIfStatement-133] - _ = x[MemoryKindReturnStatement-134] - _ = x[MemoryKindSwapStatement-135] - _ = x[MemoryKindSwitchStatement-136] - _ = x[MemoryKindWhileStatement-137] - _ = x[MemoryKindRemoveStatement-138] - _ = x[MemoryKindBooleanExpression-139] - _ = x[MemoryKindVoidExpression-140] - _ = x[MemoryKindNilExpression-141] - _ = x[MemoryKindStringExpression-142] - _ = x[MemoryKindIntegerExpression-143] - _ = x[MemoryKindFixedPointExpression-144] - _ = x[MemoryKindArrayExpression-145] - _ = x[MemoryKindDictionaryExpression-146] - _ = x[MemoryKindIdentifierExpression-147] - _ = x[MemoryKindInvocationExpression-148] - _ = x[MemoryKindMemberExpression-149] - _ = x[MemoryKindIndexExpression-150] - _ = x[MemoryKindConditionalExpression-151] - _ = x[MemoryKindUnaryExpression-152] - _ = x[MemoryKindBinaryExpression-153] - _ = x[MemoryKindFunctionExpression-154] - _ = x[MemoryKindCastingExpression-155] - _ = x[MemoryKindCreateExpression-156] - _ = x[MemoryKindDestroyExpression-157] - _ = x[MemoryKindReferenceExpression-158] - _ = x[MemoryKindForceExpression-159] - _ = x[MemoryKindPathExpression-160] - _ = x[MemoryKindAttachExpression-161] - _ = x[MemoryKindConstantSizedType-162] - _ = x[MemoryKindDictionaryType-163] - _ = x[MemoryKindFunctionType-164] - _ = x[MemoryKindInstantiationType-165] - _ = x[MemoryKindNominalType-166] - _ = x[MemoryKindOptionalType-167] - _ = x[MemoryKindReferenceType-168] - _ = x[MemoryKindRestrictedType-169] - _ = x[MemoryKindVariableSizedType-170] - _ = x[MemoryKindPosition-171] - _ = x[MemoryKindRange-172] - _ = x[MemoryKindElaboration-173] - _ = x[MemoryKindActivation-174] - _ = x[MemoryKindActivationEntries-175] - _ = x[MemoryKindVariableSizedSemaType-176] - _ = x[MemoryKindConstantSizedSemaType-177] - _ = x[MemoryKindDictionarySemaType-178] - _ = x[MemoryKindOptionalSemaType-179] - _ = x[MemoryKindRestrictedSemaType-180] - _ = x[MemoryKindReferenceSemaType-181] - _ = x[MemoryKindCapabilitySemaType-182] - _ = x[MemoryKindOrderedMap-183] - _ = x[MemoryKindOrderedMapEntryList-184] - _ = x[MemoryKindOrderedMapEntry-185] - _ = x[MemoryKindLast-186] + _ = x[MemoryKindCadenceAccountLinkValue-68] + _ = x[MemoryKindCadencePathValue-69] + _ = x[MemoryKindCadenceTypeValue-70] + _ = x[MemoryKindCadenceStorageCapabilityValue-71] + _ = x[MemoryKindCadenceFunctionValue-72] + _ = x[MemoryKindCadenceOptionalType-73] + _ = x[MemoryKindCadenceVariableSizedArrayType-74] + _ = x[MemoryKindCadenceConstantSizedArrayType-75] + _ = x[MemoryKindCadenceDictionaryType-76] + _ = x[MemoryKindCadenceField-77] + _ = x[MemoryKindCadenceParameter-78] + _ = x[MemoryKindCadenceTypeParameter-79] + _ = x[MemoryKindCadenceStructType-80] + _ = x[MemoryKindCadenceResourceType-81] + _ = x[MemoryKindCadenceAttachmentType-82] + _ = x[MemoryKindCadenceEventType-83] + _ = x[MemoryKindCadenceContractType-84] + _ = x[MemoryKindCadenceStructInterfaceType-85] + _ = x[MemoryKindCadenceResourceInterfaceType-86] + _ = x[MemoryKindCadenceContractInterfaceType-87] + _ = x[MemoryKindCadenceFunctionType-88] + _ = x[MemoryKindCadenceReferenceType-89] + _ = x[MemoryKindCadenceRestrictedType-90] + _ = x[MemoryKindCadenceCapabilityType-91] + _ = x[MemoryKindCadenceEnumType-92] + _ = x[MemoryKindRawString-93] + _ = x[MemoryKindAddressLocation-94] + _ = x[MemoryKindBytes-95] + _ = x[MemoryKindVariable-96] + _ = x[MemoryKindCompositeTypeInfo-97] + _ = x[MemoryKindCompositeField-98] + _ = x[MemoryKindInvocation-99] + _ = x[MemoryKindStorageMap-100] + _ = x[MemoryKindStorageKey-101] + _ = x[MemoryKindTypeToken-102] + _ = x[MemoryKindErrorToken-103] + _ = x[MemoryKindSpaceToken-104] + _ = x[MemoryKindProgram-105] + _ = x[MemoryKindIdentifier-106] + _ = x[MemoryKindArgument-107] + _ = x[MemoryKindBlock-108] + _ = x[MemoryKindFunctionBlock-109] + _ = x[MemoryKindParameter-110] + _ = x[MemoryKindParameterList-111] + _ = x[MemoryKindTypeParameter-112] + _ = x[MemoryKindTypeParameterList-113] + _ = x[MemoryKindTransfer-114] + _ = x[MemoryKindMembers-115] + _ = x[MemoryKindTypeAnnotation-116] + _ = x[MemoryKindDictionaryEntry-117] + _ = x[MemoryKindFunctionDeclaration-118] + _ = x[MemoryKindCompositeDeclaration-119] + _ = x[MemoryKindAttachmentDeclaration-120] + _ = x[MemoryKindInterfaceDeclaration-121] + _ = x[MemoryKindEnumCaseDeclaration-122] + _ = x[MemoryKindFieldDeclaration-123] + _ = x[MemoryKindTransactionDeclaration-124] + _ = x[MemoryKindImportDeclaration-125] + _ = x[MemoryKindVariableDeclaration-126] + _ = x[MemoryKindSpecialFunctionDeclaration-127] + _ = x[MemoryKindPragmaDeclaration-128] + _ = x[MemoryKindAssignmentStatement-129] + _ = x[MemoryKindBreakStatement-130] + _ = x[MemoryKindContinueStatement-131] + _ = x[MemoryKindEmitStatement-132] + _ = x[MemoryKindExpressionStatement-133] + _ = x[MemoryKindForStatement-134] + _ = x[MemoryKindIfStatement-135] + _ = x[MemoryKindReturnStatement-136] + _ = x[MemoryKindSwapStatement-137] + _ = x[MemoryKindSwitchStatement-138] + _ = x[MemoryKindWhileStatement-139] + _ = x[MemoryKindRemoveStatement-140] + _ = x[MemoryKindBooleanExpression-141] + _ = x[MemoryKindVoidExpression-142] + _ = x[MemoryKindNilExpression-143] + _ = x[MemoryKindStringExpression-144] + _ = x[MemoryKindIntegerExpression-145] + _ = x[MemoryKindFixedPointExpression-146] + _ = x[MemoryKindArrayExpression-147] + _ = x[MemoryKindDictionaryExpression-148] + _ = x[MemoryKindIdentifierExpression-149] + _ = x[MemoryKindInvocationExpression-150] + _ = x[MemoryKindMemberExpression-151] + _ = x[MemoryKindIndexExpression-152] + _ = x[MemoryKindConditionalExpression-153] + _ = x[MemoryKindUnaryExpression-154] + _ = x[MemoryKindBinaryExpression-155] + _ = x[MemoryKindFunctionExpression-156] + _ = x[MemoryKindCastingExpression-157] + _ = x[MemoryKindCreateExpression-158] + _ = x[MemoryKindDestroyExpression-159] + _ = x[MemoryKindReferenceExpression-160] + _ = x[MemoryKindForceExpression-161] + _ = x[MemoryKindPathExpression-162] + _ = x[MemoryKindAttachExpression-163] + _ = x[MemoryKindConstantSizedType-164] + _ = x[MemoryKindDictionaryType-165] + _ = x[MemoryKindFunctionType-166] + _ = x[MemoryKindInstantiationType-167] + _ = x[MemoryKindNominalType-168] + _ = x[MemoryKindOptionalType-169] + _ = x[MemoryKindReferenceType-170] + _ = x[MemoryKindRestrictedType-171] + _ = x[MemoryKindVariableSizedType-172] + _ = x[MemoryKindPosition-173] + _ = x[MemoryKindRange-174] + _ = x[MemoryKindElaboration-175] + _ = x[MemoryKindActivation-176] + _ = x[MemoryKindActivationEntries-177] + _ = x[MemoryKindVariableSizedSemaType-178] + _ = x[MemoryKindConstantSizedSemaType-179] + _ = x[MemoryKindDictionarySemaType-180] + _ = x[MemoryKindOptionalSemaType-181] + _ = x[MemoryKindRestrictedSemaType-182] + _ = x[MemoryKindReferenceSemaType-183] + _ = x[MemoryKindCapabilitySemaType-184] + _ = x[MemoryKindOrderedMap-185] + _ = x[MemoryKindOrderedMapEntryList-186] + _ = x[MemoryKindOrderedMapEntry-187] + _ = x[MemoryKindLast-188] } -const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" +const _MemoryKind_name = "UnknownAddressValueStringValueCharacterValueNumberValueArrayValueBaseDictionaryValueBaseCompositeValueBaseSimpleCompositeValueBaseOptionalValueTypeValuePathValueStorageCapabilityValuePathLinkValueAccountLinkValueStorageReferenceValueAccountReferenceValueEphemeralReferenceValueInterpretedFunctionValueHostFunctionValueBoundFunctionValueBigIntSimpleCompositeValuePublishedValueAtreeArrayDataSlabAtreeArrayMetaDataSlabAtreeArrayElementOverheadAtreeMapDataSlabAtreeMapMetaDataSlabAtreeMapElementOverheadAtreeMapPreAllocatedElementAtreeEncodedSlabPrimitiveStaticTypeCompositeStaticTypeInterfaceStaticTypeVariableSizedStaticTypeConstantSizedStaticTypeDictionaryStaticTypeOptionalStaticTypeRestrictedStaticTypeReferenceStaticTypeCapabilityStaticTypeFunctionStaticTypeCadenceVoidValueCadenceOptionalValueCadenceBoolValueCadenceStringValueCadenceCharacterValueCadenceAddressValueCadenceIntValueCadenceNumberValueCadenceArrayValueBaseCadenceArrayValueLengthCadenceDictionaryValueCadenceKeyValuePairCadenceStructValueBaseCadenceStructValueSizeCadenceResourceValueBaseCadenceAttachmentValueBaseCadenceResourceValueSizeCadenceAttachmentValueSizeCadenceEventValueBaseCadenceEventValueSizeCadenceContractValueBaseCadenceContractValueSizeCadenceEnumValueBaseCadenceEnumValueSizeCadencePathLinkValueCadenceAccountLinkValueCadencePathValueCadenceTypeValueCadenceStorageCapabilityValueCadenceFunctionValueCadenceOptionalTypeCadenceVariableSizedArrayTypeCadenceConstantSizedArrayTypeCadenceDictionaryTypeCadenceFieldCadenceParameterCadenceTypeParameterCadenceStructTypeCadenceResourceTypeCadenceAttachmentTypeCadenceEventTypeCadenceContractTypeCadenceStructInterfaceTypeCadenceResourceInterfaceTypeCadenceContractInterfaceTypeCadenceFunctionTypeCadenceReferenceTypeCadenceRestrictedTypeCadenceCapabilityTypeCadenceEnumTypeRawStringAddressLocationBytesVariableCompositeTypeInfoCompositeFieldInvocationStorageMapStorageKeyTypeTokenErrorTokenSpaceTokenProgramIdentifierArgumentBlockFunctionBlockParameterParameterListTypeParameterTypeParameterListTransferMembersTypeAnnotationDictionaryEntryFunctionDeclarationCompositeDeclarationAttachmentDeclarationInterfaceDeclarationEnumCaseDeclarationFieldDeclarationTransactionDeclarationImportDeclarationVariableDeclarationSpecialFunctionDeclarationPragmaDeclarationAssignmentStatementBreakStatementContinueStatementEmitStatementExpressionStatementForStatementIfStatementReturnStatementSwapStatementSwitchStatementWhileStatementRemoveStatementBooleanExpressionVoidExpressionNilExpressionStringExpressionIntegerExpressionFixedPointExpressionArrayExpressionDictionaryExpressionIdentifierExpressionInvocationExpressionMemberExpressionIndexExpressionConditionalExpressionUnaryExpressionBinaryExpressionFunctionExpressionCastingExpressionCreateExpressionDestroyExpressionReferenceExpressionForceExpressionPathExpressionAttachExpressionConstantSizedTypeDictionaryTypeFunctionTypeInstantiationTypeNominalTypeOptionalTypeReferenceTypeRestrictedTypeVariableSizedTypePositionRangeElaborationActivationActivationEntriesVariableSizedSemaTypeConstantSizedSemaTypeDictionarySemaTypeOptionalSemaTypeRestrictedSemaTypeReferenceSemaTypeCapabilitySemaTypeOrderedMapOrderedMapEntryListOrderedMapEntryLast" -var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 394, 416, 441, 457, 477, 500, 527, 543, 562, 581, 600, 623, 646, 666, 684, 704, 723, 743, 761, 777, 797, 813, 831, 852, 871, 886, 904, 925, 948, 970, 989, 1011, 1033, 1057, 1083, 1107, 1133, 1154, 1175, 1199, 1223, 1243, 1263, 1283, 1299, 1315, 1344, 1364, 1383, 1412, 1441, 1462, 1474, 1490, 1507, 1526, 1547, 1563, 1582, 1608, 1636, 1664, 1683, 1703, 1724, 1745, 1760, 1769, 1784, 1789, 1797, 1814, 1828, 1838, 1848, 1858, 1867, 1877, 1887, 1894, 1904, 1912, 1917, 1930, 1939, 1952, 1965, 1982, 1990, 1997, 2011, 2026, 2045, 2065, 2086, 2106, 2125, 2141, 2163, 2180, 2199, 2225, 2242, 2261, 2275, 2292, 2305, 2324, 2336, 2347, 2362, 2375, 2390, 2404, 2419, 2436, 2450, 2463, 2479, 2496, 2516, 2531, 2551, 2571, 2591, 2607, 2622, 2643, 2658, 2674, 2692, 2709, 2725, 2742, 2761, 2776, 2790, 2806, 2823, 2837, 2849, 2866, 2877, 2889, 2902, 2916, 2933, 2941, 2946, 2957, 2967, 2984, 3005, 3026, 3044, 3060, 3078, 3095, 3113, 3123, 3142, 3157, 3161} +var _MemoryKind_index = [...]uint16{0, 7, 19, 30, 44, 55, 69, 88, 106, 130, 143, 152, 161, 183, 196, 212, 233, 254, 277, 301, 318, 336, 342, 362, 376, 394, 416, 441, 457, 477, 500, 527, 543, 562, 581, 600, 623, 646, 666, 684, 704, 723, 743, 761, 777, 797, 813, 831, 852, 871, 886, 904, 925, 948, 970, 989, 1011, 1033, 1057, 1083, 1107, 1133, 1154, 1175, 1199, 1223, 1243, 1263, 1283, 1306, 1322, 1338, 1367, 1387, 1406, 1435, 1464, 1485, 1497, 1513, 1533, 1550, 1569, 1590, 1606, 1625, 1651, 1679, 1707, 1726, 1746, 1767, 1788, 1803, 1812, 1827, 1832, 1840, 1857, 1871, 1881, 1891, 1901, 1910, 1920, 1930, 1937, 1947, 1955, 1960, 1973, 1982, 1995, 2008, 2025, 2033, 2040, 2054, 2069, 2088, 2108, 2129, 2149, 2168, 2184, 2206, 2223, 2242, 2268, 2285, 2304, 2318, 2335, 2348, 2367, 2379, 2390, 2405, 2418, 2433, 2447, 2462, 2479, 2493, 2506, 2522, 2539, 2559, 2574, 2594, 2614, 2634, 2650, 2665, 2686, 2701, 2717, 2735, 2752, 2768, 2785, 2804, 2819, 2833, 2849, 2866, 2880, 2892, 2909, 2920, 2932, 2945, 2959, 2976, 2984, 2989, 3000, 3010, 3027, 3048, 3069, 3087, 3103, 3121, 3138, 3156, 3166, 3185, 3200, 3204} func (i MemoryKind) String() string { if i >= MemoryKind(len(_MemoryKind_index)-1) { diff --git a/runtime/common/metering.go b/runtime/common/metering.go index 6babf16b1c..c36ff7ed9c 100644 --- a/runtime/common/metering.go +++ b/runtime/common/metering.go @@ -201,6 +201,7 @@ var ( CadenceFunctionValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceFunctionValue) CadenceKeyValuePairMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceKeyValuePair) CadencePathLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) + CadenceAccountLinkValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathLinkValue) CadenceOptionalValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceOptionalValue) CadencePathValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadencePathValue) CadenceVoidValueMemoryUsage = NewConstantMemoryUsage(MemoryKindCadenceVoidValue) diff --git a/runtime/config.go b/runtime/config.go index b0bde580e4..3b904f4887 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -33,8 +33,8 @@ type Config struct { TracingEnabled bool // ResourceOwnerChangeCallbackEnabled configures if the resource owner change callback is enabled ResourceOwnerChangeHandlerEnabled bool - // CoverageReportingEnabled configures if coverage reporting is enabled - CoverageReportingEnabled bool + // CoverageReport enables and collects coverage reporting metrics + CoverageReport *CoverageReport // AccountLinkingEnabled specifies if account linking is enabled AccountLinkingEnabled bool // AttachmentsEnabled specifies if attachments are enabled diff --git a/runtime/contract_update_validation_test.go b/runtime/contract_update_validation_test.go index 2048097a42..a3626be577 100644 --- a/runtime/contract_update_validation_test.go +++ b/runtime/contract_update_validation_test.go @@ -59,7 +59,7 @@ func newContractAddTransaction(name string, code string) string { func newContractUpdateTransaction(name string, code string) string { return newContractDeployTransaction( - sema.AuthAccountContractsTypeUpdateExperimentalFunctionName, + sema.AuthAccountContractsTypeUpdate__experimentalFunctionName, name, code, ) diff --git a/runtime/convertTypes.go b/runtime/convertTypes.go index 692451c4be..c7448e603b 100644 --- a/runtime/convertTypes.go +++ b/runtime/convertTypes.go @@ -403,32 +403,63 @@ func exportFunctionType( t *sema.FunctionType, results map[sema.TypeID]cadence.Type, ) cadence.Type { + // Type parameters + typeParameterCount := len(t.TypeParameters) common.UseMemory(gauge, common.MemoryUsage{ - Kind: common.MemoryKindCadenceParameter, - Amount: uint64(len(t.Parameters)), + Kind: common.MemoryKindCadenceTypeParameter, + Amount: uint64(typeParameterCount), }) - convertedParameters := make([]cadence.Parameter, len(t.Parameters)) + var convertedTypeParameters []cadence.TypeParameter + if typeParameterCount > 0 { + convertedTypeParameters = make([]cadence.TypeParameter, typeParameterCount) - for i, parameter := range t.Parameters { - convertedParameterType := ExportMeteredType(gauge, parameter.TypeAnnotation.Type, results) + for i, typeParameter := range t.TypeParameters { - // Metered above - convertedParameters[i] = cadence.NewParameter( - parameter.Label, - parameter.Identifier, - convertedParameterType, - ) + typeBound := typeParameter.TypeBound + var convertedParameterTypeBound cadence.Type + if typeBound != nil { + convertedParameterTypeBound = ExportMeteredType(gauge, typeBound, results) + } + + // Metered above + convertedTypeParameters[i] = cadence.NewTypeParameter( + typeParameter.Name, + convertedParameterTypeBound, + ) + } + } + + // Parameters + parameterCount := len(t.Parameters) + common.UseMemory(gauge, common.MemoryUsage{ + Kind: common.MemoryKindCadenceParameter, + Amount: uint64(parameterCount), + }) + var convertedParameters []cadence.Parameter + if parameterCount > 0 { + convertedParameters = make([]cadence.Parameter, parameterCount) + + for i, parameter := range t.Parameters { + convertedParameterType := ExportMeteredType(gauge, parameter.TypeAnnotation.Type, results) + + // Metered above + convertedParameters[i] = cadence.NewParameter( + parameter.Label, + parameter.Identifier, + convertedParameterType, + ) + } } convertedReturnType := ExportMeteredType(gauge, t.ReturnTypeAnnotation.Type, results) return cadence.NewMeteredFunctionType( gauge, - "", cadence.FunctionPurity(t.Purity), + convertedTypeParameters, convertedParameters, convertedReturnType, - ).WithID(string(t.ID())) + ) } func exportReferenceType( @@ -461,10 +492,9 @@ func exportRestrictedType( return cadence.NewMeteredRestrictedType( gauge, - "", convertedType, restrictions, - ).WithID(string(t.ID())) + ) } func exportCapabilityType( diff --git a/runtime/convertValues.go b/runtime/convertValues.go index e5393c31d1..025ca12f73 100644 --- a/runtime/convertValues.go +++ b/runtime/convertValues.go @@ -61,7 +61,7 @@ func ExportValue( // NOTE: Do not generalize to map[interpreter.Value], // as not all values are Go hashable, i.e. this might lead to run-time panics -type seenReferences map[*interpreter.EphemeralReferenceValue]struct{} +type seenReferences map[interpreter.ReferenceValue]struct{} // exportValueWithInterpreter exports the given internal (interpreter) value to an external value. // @@ -212,27 +212,39 @@ func exportValueWithInterpreter( case interpreter.AddressValue: return cadence.NewMeteredAddress(inter, v), nil case interpreter.PathLinkValue: - return exportPathLinkValue(v, inter), nil + return exportPathLinkValue(v, inter) + case interpreter.AccountLinkValue: + return exportAccountLinkValue(inter), nil case interpreter.PathValue: - return exportPathValue(inter, v), nil + return exportPathValue(inter, v) case interpreter.TypeValue: return exportTypeValue(v, inter), nil case *interpreter.StorageCapabilityValue: - return exportStorageCapabilityValue(v, inter), nil + return exportStorageCapabilityValue(v, inter) case *interpreter.EphemeralReferenceValue: - // Break recursion through ephemeral references + // Break recursion through references if _, ok := seenReferences[v]; ok { return nil, nil } defer delete(seenReferences, v) seenReferences[v] = struct{}{} + + referencedValue := v.MustReferencedValue(inter, locationRange) + return exportValueWithInterpreter( - v.Value, + referencedValue, inter, locationRange, seenReferences, ) case *interpreter.StorageReferenceValue: + // Break recursion through references + if _, ok := seenReferences[v]; ok { + return nil, nil + } + defer delete(seenReferences, v) + seenReferences[v] = struct{}{} + referencedValue := v.ReferencedValue(inter, interpreter.EmptyLocationRange, true) if referencedValue == nil { return nil, nil @@ -585,23 +597,23 @@ func exportDictionaryValue( return dictionary.WithType(exportType), err } -func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) cadence.PathLink { - path := exportPathValue(inter, v.TargetPath) +func exportPathLinkValue(v interpreter.PathLinkValue, inter *interpreter.Interpreter) (cadence.PathLink, error) { + path, err := exportPathValue(inter, v.TargetPath) + if err != nil { + return cadence.PathLink{}, err + } ty := string(inter.MustConvertStaticToSemaType(v.Type).ID()) - return cadence.NewMeteredLink(inter, path, ty) + return cadence.NewMeteredPathLink(inter, path, ty), nil } -func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) cadence.Path { - domain := v.Domain.Identifier() - common.UseMemory(gauge, common.MemoryUsage{ - Kind: common.MemoryKindRawString, - // no need to add 1 to account for empty string: string is metered in Path struct - Amount: uint64(len(domain)), - }) +func exportAccountLinkValue(inter *interpreter.Interpreter) cadence.AccountLink { + return cadence.NewMeteredAccountLink(inter) +} +func exportPathValue(gauge common.MemoryGauge, v interpreter.PathValue) (cadence.Path, error) { return cadence.NewMeteredPath( gauge, - domain, + v.Domain, v.Identifier, ) } @@ -617,18 +629,26 @@ func exportTypeValue(v interpreter.TypeValue, inter *interpreter.Interpreter) ca ) } -func exportStorageCapabilityValue(v *interpreter.StorageCapabilityValue, inter *interpreter.Interpreter) cadence.StorageCapability { +func exportStorageCapabilityValue( + v *interpreter.StorageCapabilityValue, + inter *interpreter.Interpreter, +) (cadence.StorageCapability, error) { var borrowType sema.Type if v.BorrowType != nil { borrowType = inter.MustConvertStaticToSemaType(v.BorrowType) } + path, err := exportPathValue(inter, v.Path) + if err != nil { + return cadence.StorageCapability{}, err + } + return cadence.NewMeteredStorageCapability( inter, - exportPathValue(inter, v.Path), + path, cadence.NewMeteredAddress(inter, v.Address), ExportMeteredType(inter, borrowType, map[sema.TypeID]cadence.Type{}), - ) + ), nil } // exportEvent converts a runtime event to its native Go representation. @@ -811,7 +831,9 @@ func (i valueImporter) importValue(value cadence.Value, expectedType sema.Type) case cadence.Function: return nil, errors.NewDefaultUserError("cannot import function") case cadence.PathLink: - return nil, errors.NewDefaultUserError("cannot import link") + return nil, errors.NewDefaultUserError("cannot import path link") + case cadence.AccountLink: + return nil, errors.NewDefaultUserError("cannot import account link") default: // This means the implementation has unhandled types. // Hence, return an internal error @@ -1044,7 +1066,7 @@ func (i valueImporter) importPathValue(v cadence.Path) interpreter.PathValue { return interpreter.NewPathValue( inter, - common.PathDomainFromIdentifier(v.Domain), + v.Domain, v.Identifier, ) } diff --git a/runtime/convertValues_test.go b/runtime/convertValues_test.go index cde658a38f..731aa9f525 100644 --- a/runtime/convertValues_test.go +++ b/runtime/convertValues_test.go @@ -136,9 +136,9 @@ func TestExportValue(t *testing.T) { } testFunctionType := cadence.NewFunctionType( - "fun():Void", sema.FunctionPurityImpure, - []cadence.Parameter{}, + nil, + nil, cadence.VoidType{}, ) @@ -386,7 +386,7 @@ func TestExportValue(t *testing.T) { Identifier: "foo", }, expected: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, }, @@ -816,7 +816,7 @@ func TestImportValue(t *testing.T) { { label: "Path", value: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, expected: interpreter.PathValue{ @@ -825,21 +825,26 @@ func TestImportValue(t *testing.T) { }, }, { - label: "Link (invalid)", + label: "Path Link (invalid)", value: cadence.PathLink{ TargetPath: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, BorrowType: "Int", }, expected: nil, }, + { + label: "Account Link (invalid)", + value: cadence.AccountLink{}, + expected: nil, + }, { label: "Capability (invalid)", value: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "test", }, BorrowType: cadence.IntType{}, @@ -1829,6 +1834,78 @@ func TestExportReferenceValue(t *testing.T) { assert.Equal(t, expected, actual) }) + + t.Run("storage, recursive, same reference", func(t *testing.T) { + + t.Parallel() + + script := ` + pub fun main(): &AnyStruct { + var acct = getAuthAccount(0x01) + var v:[AnyStruct] = [] + acct.save(v, to: /storage/x) + + var ref = acct.borrow<&[AnyStruct]>(from: /storage/x)! + ref.append(ref) + return ref + } + ` + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + } + + _, err := rt.ExecuteScript( + Script{ + Source: []byte(script), + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot store non-storable value") + }) + + t.Run("storage, recursive, two references", func(t *testing.T) { + + t.Parallel() + + script := ` + pub fun main(): &AnyStruct { + var acct = getAuthAccount(0x01) + var v:[AnyStruct] = [] + acct.save(v, to: /storage/x) + + var ref1 = acct.borrow<&[AnyStruct]>(from: /storage/x)! + var ref2 = acct.borrow<&[AnyStruct]>(from: /storage/x)! + + ref1.append(ref2) + return ref1 + } + ` + + rt := newTestInterpreterRuntime() + + runtimeInterface := &testRuntimeInterface{ + storage: newTestLedger(nil, nil), + } + + _, err := rt.ExecuteScript( + Script{ + Source: []byte(script), + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot store non-storable value") + }) } func TestExportTypeValue(t *testing.T) { @@ -1961,7 +2038,7 @@ func TestExportTypeValue(t *testing.T) { assert.Equal(t, cadence.TypeValue{ - StaticType: (&cadence.RestrictedType{ + StaticType: &cadence.RestrictedType{ Type: &cadence.StructType{ QualifiedIdentifier: "S", Location: TestLocation, @@ -1974,7 +2051,7 @@ func TestExportTypeValue(t *testing.T) { Fields: []cadence.Field{}, }, }, - }).WithID("S.test.S{S.test.SI}"), + }, }, actual, ) @@ -2007,7 +2084,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, Address: cadence.Address{0x1}, @@ -2061,7 +2138,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, Address: cadence.Address{0x1}, @@ -2095,7 +2172,7 @@ func TestExportStorageCapabilityValue(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, Address: cadence.Address{0x1}, @@ -2129,7 +2206,7 @@ func TestExportPathLinkValue(t *testing.T) { expected := cadence.PathLink{ TargetPath: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, BorrowType: "Int", @@ -2180,7 +2257,7 @@ func TestExportPathLinkValue(t *testing.T) { expected := cadence.PathLink{ TargetPath: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, BorrowType: "S.test.S", @@ -2190,6 +2267,25 @@ func TestExportPathLinkValue(t *testing.T) { }) } +func TestExportAccountLinkValue(t *testing.T) { + + t.Parallel() + + link := interpreter.AccountLinkValue{} + + actual, err := exportValueWithInterpreter( + link, + newTestInterpreter(t), + interpreter.EmptyLocationRange, + seenReferences{}, + ) + require.NoError(t, err) + + expected := cadence.AccountLink{} + + assert.Equal(t, expected, actual) +} + func TestExportCompositeValueWithFunctionValueField(t *testing.T) { t.Parallel() @@ -2220,10 +2316,9 @@ func TestExportCompositeValueWithFunctionValueField(t *testing.T) { }, { Identifier: "f", - Type: (&cadence.FunctionType{ - Parameters: []cadence.Parameter{}, + Type: &cadence.FunctionType{ ReturnType: cadence.VoidType{}, - }).WithID("fun():Void"), + }, }, }, } @@ -2232,10 +2327,9 @@ func TestExportCompositeValueWithFunctionValueField(t *testing.T) { expected := cadence.NewStruct([]cadence.Value{ cadence.NewInt(42), cadence.Function{ - FunctionType: (&cadence.FunctionType{ - Parameters: []cadence.Parameter{}, + FunctionType: &cadence.FunctionType{ ReturnType: cadence.VoidType{}, - }).WithID("fun():Void"), + }, }, }).WithType(fooStructType) @@ -2593,7 +2687,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { label: "StoragePath", typeSignature: "StoragePath", exportedValue: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, skipExport: true, @@ -2602,7 +2696,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { label: "PrivatePath", typeSignature: "PrivatePath", exportedValue: cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, skipExport: true, @@ -2611,7 +2705,7 @@ func TestRuntimeArgumentPassing(t *testing.T) { label: "PublicPath", typeSignature: "PublicPath", exportedValue: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, skipExport: true, @@ -2758,15 +2852,15 @@ func TestRuntimeComplexStructArgumentPassing(t *testing.T) { cadence.NewAddress([8]byte{0, 0, 0, 0, 0, 1, 0, 2}), cadence.NewBool(true), cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, cadence.String("foo"), @@ -2889,7 +2983,7 @@ func TestRuntimeComplexStructWithAnyStructFields(t *testing.T) { Size: 2, }), cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, }, @@ -3924,7 +4018,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPublic.Identifier(), + Domain: common.PathDomainPublic, Identifier: "foo", }, } @@ -3978,7 +4072,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: cadence.IntType{}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPublic.Identifier(), + Domain: common.PathDomainPublic, Identifier: "foo", }, } @@ -4025,7 +4119,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPrivate.Identifier(), + Domain: common.PathDomainPrivate, Identifier: "foo", }, } @@ -4072,7 +4166,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: &cadence.ReferenceType{Type: cadence.IntType{}}, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainStorage.Identifier(), + Domain: common.PathDomainStorage, Identifier: "foo", }, } @@ -4128,7 +4222,7 @@ func TestStorageCapabilityValueImport(t *testing.T) { BorrowType: borrowType, Address: cadence.Address{0x1}, Path: cadence.Path{ - Domain: common.PathDomainPublic.Identifier(), + Domain: common.PathDomainPublic, Identifier: "foo", }, } @@ -5235,3 +5329,44 @@ func TestNestedStructArgPassing(t *testing.T) { require.ErrorAs(t, err, &argErr) }) } + +func TestDestroyedResourceReferenceExport(t *testing.T) { + t.Parallel() + + rt := newTestInterpreterRuntimeWithAttachments() + + script := []byte(` + pub resource S {} + + pub fun main(): &S { + var s <- create S() + var ref = &s as &S + + // Just to trick the checker, + // and get pass the static referenced resource invalidation analysis. + var ref2 = getRef(ref) + + destroy s + return ref2! + } + + pub fun getRef(_ ref: &S): &S { + return ref + } + `) + + runtimeInterface := &testRuntimeInterface{} + + nextScriptLocation := newScriptLocationGenerator() + _, err := rt.ExecuteScript( + Script{ + Source: script, + }, + Context{ + Interface: runtimeInterface, + Location: nextScriptLocation(), + }, + ) + require.Error(t, err) + require.ErrorAs(t, err, &interpreter.DestroyedResourceError{}) +} diff --git a/runtime/coverage_test.go b/runtime/coverage_test.go index 9d4a36a7d4..59fe97cb7b 100644 --- a/runtime/coverage_test.go +++ b/runtime/coverage_test.go @@ -906,6 +906,10 @@ func TestCoverageReportMerge(t *testing.T) { otherLocation := common.StringLocation("Factorial") otherCoverageReport.InspectProgram(otherLocation, otherProgram) + // We add `IntegerTraits` to both coverage reports, to test that their + // line hits are properly merged. + coverageReport.InspectProgram(location, program) + coverageReport.AddLineHit(location, 9) excludedLocation := common.StringLocation("FooContract") otherCoverageReport.ExcludeLocation(excludedLocation) @@ -945,11 +949,11 @@ func TestCoverageReportMerge(t *testing.T) { "25": 0, "26": 0, "29": 0, - "9": 0 + "9": 1 }, - "missed_lines": [9, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 26, 29], + "missed_lines": [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 26, 29], "statements": 14, - "percentage": "0.0%" + "percentage": "7.1%" } }, "excluded_locations": ["S.FooContract"] @@ -1146,10 +1150,6 @@ func TestRuntimeCoverage(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - CoverageReportingEnabled: true, - }) - importedScript := []byte(` pub let specialNumbers: {Int: String} = { 1729: "Harshad", @@ -1230,6 +1230,7 @@ func TestRuntimeCoverage(t *testing.T) { } `) + coverageReport := NewCoverageReport() runtimeInterface := &testRuntimeInterface{ getCode: func(location Location) (bytes []byte, err error) { switch location { @@ -1240,8 +1241,9 @@ func TestRuntimeCoverage(t *testing.T) { } }, } - - coverageReport := NewCoverageReport() + runtime := NewInterpreterRuntime(Config{ + CoverageReport: coverageReport, + }) value, err := runtime.ExecuteScript( Script{ @@ -1322,10 +1324,6 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { t.Parallel() - runtime := NewInterpreterRuntime(Config{ - CoverageReportingEnabled: true, - }) - importedScript := []byte(` pub let specialNumbers: {Int: String} = { 1729: "Harshad", @@ -1386,6 +1384,10 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { } `) + coverageReport := NewCoverageReport() + scriptlocation := common.ScriptLocation{} + coverageReport.ExcludeLocation(scriptlocation) + runtimeInterface := &testRuntimeInterface{ getCode: func(location Location) (bytes []byte, err error) { switch location { @@ -1396,10 +1398,9 @@ func TestRuntimeCoverageWithExcludedLocation(t *testing.T) { } }, } - - coverageReport := NewCoverageReport() - scriptlocation := common.ScriptLocation{} - coverageReport.ExcludeLocation(scriptlocation) + runtime := NewInterpreterRuntime(Config{ + CoverageReport: coverageReport, + }) value, err := runtime.ExecuteScript( Script{ diff --git a/runtime/deployment_test.go b/runtime/deployment_test.go index e29292c155..783dc04da8 100644 --- a/runtime/deployment_test.go +++ b/runtime/deployment_test.go @@ -338,4 +338,17 @@ func TestRuntimeTransactionWithContractDeployment(t *testing.T) { }) }) + t.Run("Path subtype", func(t *testing.T) { + test(t, testCase{ + contract: ` + pub contract Test { + init(_ path: StoragePath) {} + } + `, + arguments: []argument{ + interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "test"), + }, + check: expectSuccess, + }) + }) } diff --git a/runtime/environment.go b/runtime/environment.go index 7f38f901b2..85c231ca17 100644 --- a/runtime/environment.go +++ b/runtime/environment.go @@ -602,7 +602,7 @@ func (e *interpreterEnvironment) newInterpreter( } func (e *interpreterEnvironment) newOnStatementHandler() interpreter.OnStatementFunc { - if !e.config.CoverageReportingEnabled { + if e.config.CoverageReport == nil { return nil } diff --git a/runtime/imported_values_memory_metering_test.go b/runtime/imported_values_memory_metering_test.go index d527152c86..7349b3c08f 100644 --- a/runtime/imported_values_memory_metering_test.go +++ b/runtime/imported_values_memory_metering_test.go @@ -419,16 +419,16 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { MemoryKind: common.MemoryKindPathValue, Weight: 1, TypeInstance: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "id3", }, }, { TypeName: "Path", MemoryKind: common.MemoryKindRawString, - Weight: 3 + 1 + 10, + Weight: (1 + 3) + (1 + 3), TypeInstance: cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "id3", }, }, @@ -440,7 +440,7 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { Weight: 1, TypeInstance: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foobarrington", }, Address: cadence.Address{}, @@ -456,7 +456,7 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { Weight: 1, TypeInstance: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foobarrington", }, Address: cadence.Address{}, @@ -469,10 +469,10 @@ func TestImportedValueMemoryMeteringForSimpleTypes(t *testing.T) { { TypeName: "Capability", MemoryKind: common.MemoryKindRawString, - Weight: 13 + 1 + 19, + Weight: (1 + 13) + (1 + 13), TypeInstance: cadence.StorageCapability{ Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foobarrington", }, Address: cadence.Address{}, diff --git a/runtime/interpreter/account.go b/runtime/interpreter/account.go index c76635aafb..e97a7693ea 100644 --- a/runtime/interpreter/account.go +++ b/runtime/interpreter/account.go @@ -105,6 +105,7 @@ func NewAuthAccountValue( case sema.AuthAccountTypeForEachPublicFunctionName: if forEachPublicFunction == nil { forEachPublicFunction = inter.newStorageIterationFunction( + sema.AuthAccountTypeForEachPublicFunctionType, address, common.PathDomainPublic, sema.PublicPathType, @@ -115,6 +116,7 @@ func NewAuthAccountValue( case sema.AuthAccountTypeForEachPrivateFunctionName: if forEachPrivateFunction == nil { forEachPrivateFunction = inter.newStorageIterationFunction( + sema.AuthAccountTypeForEachPrivateFunctionType, address, common.PathDomainPrivate, sema.PrivatePathType, @@ -125,6 +127,7 @@ func NewAuthAccountValue( case sema.AuthAccountTypeForEachStoredFunctionName: if forEachStoredFunction == nil { forEachStoredFunction = inter.newStorageIterationFunction( + sema.AuthAccountTypeForEachStoredFunctionType, address, common.PathDomainStorage, sema.StoragePathType, @@ -194,7 +197,10 @@ func NewAuthAccountValue( case sema.AuthAccountTypeGetLinkTargetFunctionName: if getLinkTargetFunction == nil { - getLinkTargetFunction = inter.accountGetLinkTargetFunction(address) + getLinkTargetFunction = inter.accountGetLinkTargetFunction( + sema.AuthAccountTypeGetLinkTargetFunctionType, + address, + ) } return getLinkTargetFunction @@ -249,7 +255,7 @@ func NewPublicAccountValue( fields := map[string]Value{ sema.PublicAccountTypeAddressFieldName: address, - sema.PublicAccountTypeGetCapabilityFieldName: accountGetCapabilityFunction( + sema.PublicAccountTypeGetCapabilityFunctionName: accountGetCapabilityFunction( gauge, address, sema.PublicPathType, @@ -276,12 +282,13 @@ func NewPublicAccountValue( } return contracts - case sema.PublicAccountTypePathsFieldName: + case sema.PublicAccountTypePublicPathsFieldName: return inter.publicAccountPaths(address, locationRange) - case sema.PublicAccountTypeForEachPublicFieldName: + case sema.PublicAccountTypeForEachPublicFunctionName: if forEachPublicFunction == nil { forEachPublicFunction = inter.newStorageIterationFunction( + sema.PublicAccountTypeForEachPublicFunctionType, address, common.PathDomainPublic, sema.PublicPathType, @@ -301,9 +308,12 @@ func NewPublicAccountValue( case sema.PublicAccountTypeStorageCapacityFieldName: return storageCapacityGet(inter) - case sema.PublicAccountTypeGetTargetLinkFieldName: + case sema.PublicAccountTypeGetLinkTargetFunctionName: if getLinkTargetFunction == nil { - getLinkTargetFunction = inter.accountGetLinkTargetFunction(address) + getLinkTargetFunction = inter.accountGetLinkTargetFunction( + sema.PublicAccountTypeGetLinkTargetFunctionType, + address, + ) } return getLinkTargetFunction } diff --git a/runtime/interpreter/accountcontracts.go b/runtime/interpreter/accountcontracts.go index 94e5228680..57a58ad01d 100644 --- a/runtime/interpreter/accountcontracts.go +++ b/runtime/interpreter/accountcontracts.go @@ -45,11 +45,11 @@ func NewAuthAccountContractsValue( ) Value { fields := map[string]Value{ - sema.AuthAccountContractsTypeAddFunctionName: addFunction, - sema.AccountContractsTypeGetFunctionName: getFunction, - sema.AccountContractsTypeBorrowFunctionName: borrowFunction, - sema.AuthAccountContractsTypeRemoveFunctionName: removeFunction, - sema.AuthAccountContractsTypeUpdateExperimentalFunctionName: updateFunction, + sema.AuthAccountContractsTypeAddFunctionName: addFunction, + sema.AuthAccountContractsTypeGetFunctionName: getFunction, + sema.AuthAccountContractsTypeBorrowFunctionName: borrowFunction, + sema.AuthAccountContractsTypeRemoveFunctionName: removeFunction, + sema.AuthAccountContractsTypeUpdate__experimentalFunctionName: updateFunction, } computeField := func( @@ -58,7 +58,7 @@ func NewAuthAccountContractsValue( locationRange LocationRange, ) Value { switch name { - case sema.AccountContractsTypeNamesFieldName: + case sema.AuthAccountContractsTypeNamesFieldName: return namesGetter(interpreter, locationRange) } return nil @@ -100,8 +100,8 @@ func NewPublicAccountContractsValue( ) Value { fields := map[string]Value{ - sema.AccountContractsTypeGetFunctionName: getFunction, - sema.AccountContractsTypeBorrowFunctionName: borrowFunction, + sema.PublicAccountContractsTypeGetFunctionName: getFunction, + sema.PublicAccountContractsTypeBorrowFunctionName: borrowFunction, } computeField := func( @@ -110,7 +110,7 @@ func NewPublicAccountContractsValue( locationRange LocationRange, ) Value { switch name { - case sema.AccountContractsTypeNamesFieldName: + case sema.PublicAccountContractsTypeNamesFieldName: return namesGetter(interpreter, locationRange) } return nil diff --git a/runtime/interpreter/accountinbox.go b/runtime/interpreter/accountinbox.go index 1f48bb7356..61bfa7c89d 100644 --- a/runtime/interpreter/accountinbox.go +++ b/runtime/interpreter/accountinbox.go @@ -40,9 +40,9 @@ func NewAuthAccountInboxValue( ) Value { fields := map[string]Value{ - sema.AuthAccountTypeInboxPublishFunctionName: publishFunction, - sema.AuthAccountTypeInboxUnpublishFunctionName: unpublishFunction, - sema.AuthAccountTypeInboxClaimFunctionName: claimFunction, + sema.AuthAccountInboxTypePublishFunctionName: publishFunction, + sema.AuthAccountInboxTypeUnpublishFunctionName: unpublishFunction, + sema.AuthAccountInboxTypeClaimFunctionName: claimFunction, } var str string diff --git a/runtime/interpreter/accountkeys.go b/runtime/interpreter/accountkeys.go index ea6dd30d7d..8f5bbe52e1 100644 --- a/runtime/interpreter/accountkeys.go +++ b/runtime/interpreter/accountkeys.go @@ -42,15 +42,15 @@ func NewAuthAccountKeysValue( ) Value { fields := map[string]Value{ - sema.AccountKeysTypeAddFunctionName: addFunction, - sema.AccountKeysTypeGetFunctionName: getFunction, - sema.AccountKeysTypeRevokeFunctionName: revokeFunction, - sema.AccountKeysTypeForEachFunctionName: forEachFunction, + sema.AuthAccountKeysTypeAddFunctionName: addFunction, + sema.AuthAccountKeysTypeGetFunctionName: getFunction, + sema.AuthAccountKeysTypeRevokeFunctionName: revokeFunction, + sema.AuthAccountKeysTypeForEachFunctionName: forEachFunction, } computeField := func(name string, _ *Interpreter, _ LocationRange) Value { switch name { - case sema.AccountKeysTypeCountFieldName: + case sema.AuthAccountKeysTypeCountFieldName: return getKeysCount() } return nil @@ -93,13 +93,13 @@ func NewPublicAccountKeysValue( ) Value { fields := map[string]Value{ - sema.AccountKeysTypeGetFunctionName: getFunction, - sema.AccountKeysTypeForEachFunctionName: forEachFunction, + sema.PublicAccountKeysTypeGetFunctionName: getFunction, + sema.PublicAccountKeysTypeForEachFunctionName: forEachFunction, } computeField := func(name string, _ *Interpreter, _ LocationRange) Value { switch name { - case sema.AccountKeysTypeCountFieldName: + case sema.PublicAccountKeysTypeCountFieldName: return getKeysCount() } return nil diff --git a/runtime/interpreter/interpreter.go b/runtime/interpreter/interpreter.go index 9e3393d525..0b0c7be0dc 100644 --- a/runtime/interpreter/interpreter.go +++ b/runtime/interpreter/interpreter.go @@ -749,16 +749,9 @@ func (interpreter *Interpreter) visitFunctionBody( } // If there is a return type, declare the constant `result`. - // If it is a resource type, the constant has the same type as a reference to the return type. - // If it is not a resource type, the constant has the same type as the return type. if returnType != sema.VoidType { - var resultValue Value - if returnType.IsResourceType() { - resultValue = NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) - } else { - resultValue = returnValue - } + resultValue := interpreter.resultValue(returnValue, returnType) interpreter.declareVariable( sema.ResultIdentifier, resultValue, @@ -770,6 +763,41 @@ func (interpreter *Interpreter) visitFunctionBody( return returnValue } +// resultValue returns the value for the `result` constant. +// If the return type is not a resource: +// - The constant has the same type as the return type. +// - `result` value is the same as the return value. +// +// If the return type is a resource: +// - The constant has the same type as a reference to the return type. +// - `result` value is a reference to the return value. +func (interpreter *Interpreter) resultValue(returnValue Value, returnType sema.Type) Value { + if !returnType.IsResourceType() { + return returnValue + } + + if optionalType, ok := returnType.(*sema.OptionalType); ok { + switch returnValue := returnValue.(type) { + // If this value is an optional value (T?), then transform it into an optional reference (&T)?. + case *SomeValue: + innerValue := NewEphemeralReferenceValue( + interpreter, + false, + returnValue.value, + optionalType.Type, + ) + + interpreter.maybeTrackReferencedResourceKindedValue(returnValue.value) + return NewSomeValueNonCopying(interpreter, innerValue) + case NilValue: + return NilValue{} + } + } + + interpreter.maybeTrackReferencedResourceKindedValue(returnValue) + return NewEphemeralReferenceValue(interpreter, false, returnValue, returnType) +} + func (interpreter *Interpreter) visitConditions(conditions []*ast.Condition) { for _, condition := range conditions { interpreter.visitCondition(condition) @@ -3350,6 +3378,7 @@ func (interpreter *Interpreter) recordStorageMutation() { } func (interpreter *Interpreter) newStorageIterationFunction( + functionType *sema.FunctionType, addressValue AddressValue, domain common.PathDomain, pathType sema.Type, @@ -3360,7 +3389,7 @@ func (interpreter *Interpreter) newStorageIterationFunction( return NewHostFunctionValue( interpreter, - sema.AccountForEachFunctionType(pathType), + functionType, func(invocation Invocation) Value { interpreter := invocation.Interpreter @@ -3843,14 +3872,17 @@ func (interpreter *Interpreter) authAccountLinkAccountFunction(addressValue Addr ) } -func (interpreter *Interpreter) accountGetLinkTargetFunction(addressValue AddressValue) *HostFunctionValue { +func (interpreter *Interpreter) accountGetLinkTargetFunction( + functionType *sema.FunctionType, + addressValue AddressValue, +) *HostFunctionValue { // Converted addresses can be cached and don't have to be recomputed on each function invocation address := addressValue.ToAddress() return NewHostFunctionValue( interpreter, - sema.AccountTypeGetLinkTargetFunctionType, + functionType, func(invocation Invocation) Value { interpreter := invocation.Interpreter @@ -4607,6 +4639,12 @@ func (interpreter *Interpreter) ValidateAtreeValue(value atree.Value) { } } +func (interpreter *Interpreter) maybeTrackReferencedResourceKindedValue(value Value) { + if value, ok := value.(ReferenceTrackedResourceKindedValue); ok { + interpreter.trackReferencedResourceKindedValue(value.StorageID(), value) + } +} + func (interpreter *Interpreter) trackReferencedResourceKindedValue( id atree.StorageID, value ReferenceTrackedResourceKindedValue, diff --git a/runtime/interpreter/interpreter_expression.go b/runtime/interpreter/interpreter_expression.go index 0772112698..d4f6fb109f 100644 --- a/runtime/interpreter/interpreter_expression.go +++ b/runtime/interpreter/interpreter_expression.go @@ -1108,9 +1108,7 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as result := interpreter.evalExpression(referenceExpression.Expression) - if result, ok := result.(ReferenceTrackedResourceKindedValue); ok { - interpreter.trackReferencedResourceKindedValue(result.StorageID(), result) - } + interpreter.maybeTrackReferencedResourceKindedValue(result) switch typ := borrowType.(type) { case *sema.OptionalType: @@ -1131,9 +1129,7 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as } innerValue := result.InnerValue(interpreter, locationRange) - if result, ok := innerValue.(ReferenceTrackedResourceKindedValue); ok { - interpreter.trackReferencedResourceKindedValue(result.StorageID(), result) - } + interpreter.maybeTrackReferencedResourceKindedValue(innerValue) return NewSomeValueNonCopying( interpreter, @@ -1225,7 +1221,7 @@ func (interpreter *Interpreter) VisitAttachExpression(attachExpression *ast.Atta attachTarget := interpreter.evalExpression(attachExpression.Base) base, ok := attachTarget.(*CompositeValue) - // we enforce this in the checker, but check defensively anyways + // we enforce this in the checker, but check defensively anyway if !ok || !base.Kind.SupportsAttachments() { panic(InvalidAttachmentOperationTargetError{ Value: attachTarget, @@ -1241,8 +1237,8 @@ func (interpreter *Interpreter) VisitAttachExpression(attachExpression *ast.Atta } // the `base` value must be accessible during the attachment's constructor, but we cannot - // set it on the attachment's `CompositeValue` yet, because the value does not exist. Instead - // we create an implicit constructor argument containing a reference to the base + // set it on the attachment's `CompositeValue` yet, because the value does not exist. + // Instead, we create an implicit constructor argument containing a reference to the base. var baseValue Value = NewEphemeralReferenceValue( interpreter, false, diff --git a/runtime/interpreter/storagemap.go b/runtime/interpreter/storagemap.go index f6e33edc9d..8c329ffb22 100644 --- a/runtime/interpreter/storagemap.go +++ b/runtime/interpreter/storagemap.go @@ -19,6 +19,8 @@ package interpreter import ( + goerrors "errors" + "github.com/onflow/atree" "github.com/onflow/cadence/runtime/common" @@ -71,7 +73,8 @@ func (s StorageMap) ValueExists(key string) bool { StringAtreeValue(key), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return false } panic(errors.NewExternalError(err)) @@ -89,7 +92,8 @@ func (s StorageMap) ReadValue(gauge common.MemoryGauge, key string) Value { StringAtreeValue(key), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil } panic(errors.NewExternalError(err)) @@ -139,7 +143,8 @@ func (s StorageMap) RemoveValue(interpreter *Interpreter, key string) { StringAtreeValue(key), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return } panic(errors.NewExternalError(err)) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 10fb8bd831..6f0931fbb9 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -21,6 +21,7 @@ package interpreter import ( "encoding/binary" "encoding/hex" + goerrors "errors" "fmt" "math" "math/big" @@ -351,7 +352,7 @@ func (v TypeValue) Equal(_ *Interpreter, _ LocationRange, other Value) bool { func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name string) Value { switch name { - case "identifier": + case sema.MetaTypeIdentifierFieldName: var typeID string staticType := v.Type if staticType != nil { @@ -364,7 +365,8 @@ func (v TypeValue) GetMember(interpreter *Interpreter, _ LocationRange, name str return NewStringValue(interpreter, memoryUsage, func() string { return typeID }) - case "isSubtype": + + case sema.MetaTypeIsSubtypeFunctionName: return NewHostFunctionValue( interpreter, sema.MetaTypeIsSubtypeFunctionType, @@ -1122,14 +1124,14 @@ func (*StringValue) RemoveKey(_ *Interpreter, _ LocationRange, _ Value) Value { func (v *StringValue) GetMember(interpreter *Interpreter, locationRange LocationRange, name string) Value { switch name { - case "length": + case sema.StringTypeLengthFieldName: length := v.Length() return NewIntValueFromInt64(interpreter, int64(length)) - case "utf8": + case sema.StringTypeUtf8FieldName: return ByteSliceToByteArrayValue(interpreter, []byte(v.Str)) - case "concat": + case sema.StringTypeConcatFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeConcatFunctionType, @@ -1143,7 +1145,7 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location }, ) - case "slice": + case sema.StringTypeSliceFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeSliceFunctionType, @@ -1162,7 +1164,7 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location }, ) - case "decodeHex": + case sema.StringTypeDecodeHexFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeDecodeHexFunctionType, @@ -1174,7 +1176,7 @@ func (v *StringValue) GetMember(interpreter *Interpreter, locationRange Location }, ) - case "toLower": + case sema.StringTypeToLowerFunctionName: return NewHostFunctionValue( interpreter, sema.StringTypeToLowerFunctionType, @@ -1733,7 +1735,8 @@ func (v *ArrayValue) GetKey(interpreter *Interpreter, locationRange LocationRang } func (v *ArrayValue) handleIndexOutOfBoundsError(err error, index int, locationRange LocationRange) { - if _, ok := err.(*atree.IndexOutOfBoundsError); ok { + var indexOutOfBoundsError *atree.IndexOutOfBoundsError + if goerrors.As(err, &indexOutOfBoundsError) { panic(ArrayIndexOutOfBoundsError{ Index: index, Size: v.Count(), @@ -2636,16 +2639,18 @@ func (v *ArrayValue) Slice( iterator, err := v.array.RangeIterator(uint64(fromIndex), uint64(toIndex)) if err != nil { - switch err.(type) { - case *atree.SliceOutOfBoundsError: + var sliceOutOfBoundsError *atree.SliceOutOfBoundsError + if goerrors.As(err, &sliceOutOfBoundsError) { panic(ArraySliceIndicesError{ FromIndex: fromIndex, UpToIndex: toIndex, Size: v.Count(), LocationRange: locationRange, }) + } - case *atree.InvalidSliceIndexError: + var invalidSliceIndexError *atree.InvalidSliceIndexError + if goerrors.As(err, &invalidSliceIndexError) { panic(InvalidSliceIndexError{ FromIndex: fromIndex, UpToIndex: toIndex, @@ -14172,7 +14177,8 @@ func (v *CompositeValue) GetMember(interpreter *Interpreter, locationRange Locat StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); !ok { + var keyNotFoundError *atree.KeyNotFoundError + if !goerrors.As(err, &keyNotFoundError) { panic(errors.NewExternalError(err)) } } @@ -14319,7 +14325,8 @@ func (v *CompositeValue) RemoveMember( StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil } panic(errors.NewExternalError(err)) @@ -14497,7 +14504,8 @@ func (v *CompositeValue) GetField(interpreter *Interpreter, locationRange Locati StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil } panic(errors.NewExternalError(err)) @@ -15054,7 +15062,8 @@ func (v *CompositeValue) RemoveField( StringAtreeValue(name), ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return } panic(errors.NewExternalError(err)) @@ -15128,6 +15137,8 @@ func (v *CompositeValue) setBaseValue(interpreter *Interpreter, base *CompositeV // the base reference can only be borrowed with the declared type of the attachment's base v.base = NewEphemeralReferenceValue(interpreter, false, base, baseType) + + interpreter.trackReferencedResourceKindedValue(base.StorageID(), base) } func attachmentMemberName(ty sema.Type) string { @@ -15161,6 +15172,7 @@ func attachmentBaseAndSelfValues( base = v.getBaseValue(interpreter, locationRange) // in attachment functions, self is a reference value self = NewEphemeralReferenceValue(interpreter, false, v, interpreter.MustSemaTypeOfValue(v)) + interpreter.trackReferencedResourceKindedValue(v.StorageID(), v) return } @@ -15210,8 +15222,10 @@ func (v *CompositeValue) GetTypeKey( // dynamically set the attachment's base to this composite attachment.setBaseValue(interpreter, v) + attachmentRef := NewEphemeralReferenceValue(interpreter, false, attachment, ty) interpreter.trackReferencedResourceKindedValue(attachment.StorageID(), attachment) - return NewSomeValueNonCopying(interpreter, NewEphemeralReferenceValue(interpreter, false, attachment, ty)) + + return NewSomeValueNonCopying(interpreter, attachmentRef) } func (v *CompositeValue) SetTypeKey( @@ -15572,7 +15586,8 @@ func (v *DictionaryValue) ContainsKey( keyValue, ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return FalseValue } panic(errors.NewExternalError(err)) @@ -15596,7 +15611,8 @@ func (v *DictionaryValue) Get( keyValue, ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return nil, false } panic(errors.NewExternalError(err)) @@ -15921,7 +15937,8 @@ func (v *DictionaryValue) Remove( keyValue, ) if err != nil { - if _, ok := err.(*atree.KeyNotFoundError); ok { + var keyNotFoundError *atree.KeyNotFoundError + if goerrors.As(err, &keyNotFoundError) { return NilOptionalValue } panic(errors.NewExternalError(err)) @@ -16964,6 +16981,10 @@ func (s SomeStorable) ChildStorables() []atree.Storable { } } +type ReferenceValue interface { + isReference() +} + // StorageReferenceValue type StorageReferenceValue struct { @@ -16978,6 +16999,7 @@ var _ EquatableValue = &StorageReferenceValue{} var _ ValueIndexableValue = &StorageReferenceValue{} var _ TypeIndexableValue = &StorageReferenceValue{} var _ MemberAccessibleValue = &StorageReferenceValue{} +var _ ReferenceValue = &StorageReferenceValue{} func NewUnmeteredStorageReferenceValue( authorized bool, @@ -17313,6 +17335,8 @@ func (*StorageReferenceValue) DeepRemove(_ *Interpreter) { // NO-OP } +func (*StorageReferenceValue) isReference() {} + // EphemeralReferenceValue type EphemeralReferenceValue struct { @@ -17326,6 +17350,7 @@ var _ EquatableValue = &EphemeralReferenceValue{} var _ ValueIndexableValue = &EphemeralReferenceValue{} var _ TypeIndexableValue = &EphemeralReferenceValue{} var _ MemberAccessibleValue = &EphemeralReferenceValue{} +var _ ReferenceValue = &EphemeralReferenceValue{} func NewUnmeteredEphemeralReferenceValue( authorized bool, @@ -17420,7 +17445,7 @@ func (v *EphemeralReferenceValue) ReferencedValue( } } -func (v *EphemeralReferenceValue) mustReferencedValue( +func (v *EphemeralReferenceValue) MustReferencedValue( interpreter *Interpreter, locationRange LocationRange, ) Value { @@ -17443,7 +17468,7 @@ func (v *EphemeralReferenceValue) GetMember( locationRange LocationRange, name string, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return interpreter.getMember(self, locationRange, name) } @@ -17453,7 +17478,7 @@ func (v *EphemeralReferenceValue) RemoveMember( locationRange LocationRange, identifier string, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) if memberAccessibleValue, ok := self.(MemberAccessibleValue); ok { return memberAccessibleValue.RemoveMember(interpreter, locationRange, identifier) @@ -17468,7 +17493,7 @@ func (v *EphemeralReferenceValue) SetMember( name string, value Value, ) bool { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return interpreter.setMember(self, locationRange, name, value) } @@ -17478,7 +17503,7 @@ func (v *EphemeralReferenceValue) GetKey( locationRange LocationRange, key Value, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(ValueIndexableValue). GetKey(interpreter, locationRange, key) @@ -17490,7 +17515,7 @@ func (v *EphemeralReferenceValue) SetKey( key Value, value Value, ) { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) self.(ValueIndexableValue). SetKey(interpreter, locationRange, key, value) @@ -17502,7 +17527,7 @@ func (v *EphemeralReferenceValue) InsertKey( key Value, value Value, ) { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) self.(ValueIndexableValue). InsertKey(interpreter, locationRange, key, value) @@ -17513,7 +17538,7 @@ func (v *EphemeralReferenceValue) RemoveKey( locationRange LocationRange, key Value, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(ValueIndexableValue). RemoveKey(interpreter, locationRange, key) @@ -17524,7 +17549,7 @@ func (v *EphemeralReferenceValue) GetTypeKey( locationRange LocationRange, key sema.Type, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(TypeIndexableValue). GetTypeKey(interpreter, locationRange, key) @@ -17536,7 +17561,7 @@ func (v *EphemeralReferenceValue) SetTypeKey( key sema.Type, value Value, ) { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) self.(TypeIndexableValue). SetTypeKey(interpreter, locationRange, key, value) @@ -17547,7 +17572,7 @@ func (v *EphemeralReferenceValue) RemoveTypeKey( locationRange LocationRange, key sema.Type, ) Value { - self := v.mustReferencedValue(interpreter, locationRange) + self := v.MustReferencedValue(interpreter, locationRange) return self.(TypeIndexableValue). RemoveTypeKey(interpreter, locationRange, key) @@ -17650,6 +17675,8 @@ func (*EphemeralReferenceValue) DeepRemove(_ *Interpreter) { // NO-OP } +func (*EphemeralReferenceValue) isReference() {} + // AddressValue type AddressValue common.Address diff --git a/runtime/interpreter/value_test.go b/runtime/interpreter/value_test.go index e2340a0946..3e442647a3 100644 --- a/runtime/interpreter/value_test.go +++ b/runtime/interpreter/value_test.go @@ -1075,7 +1075,7 @@ func TestStringer(t *testing.T) { }(), expected: "y --> bar", }, - "Link": { + "PathLink": { value: PathLinkValue{ TargetPath: PathValue{ Domain: common.PathDomainStorage, @@ -1085,6 +1085,10 @@ func TestStringer(t *testing.T) { }, expected: "PathLink(/storage/foo)", }, + "AccountLink": { + value: AccountLinkValue{}, + expected: "AccountLink()", + }, "Path": { value: PathValue{ Domain: common.PathDomainStorage, diff --git a/runtime/literal.go b/runtime/literal.go index e6b8056da8..6184456fc2 100644 --- a/runtime/literal.go +++ b/runtime/literal.go @@ -137,15 +137,25 @@ func arrayLiteralValue(inter *interpreter.Interpreter, elements []ast.Expression }) } -func pathLiteralValue(memoryGauge common.MemoryGauge, expression ast.Expression, ty sema.Type) (result cadence.Value, errResult error) { +func pathLiteralValue( + memoryGauge common.MemoryGauge, + expression ast.Expression, + ty sema.Type, +) ( + cadence.Value, + error, +) { pathExpression, ok := expression.(*ast.PathExpression) if !ok { return nil, LiteralExpressionTypeError } + pathDomain := pathExpression.Domain.Identifier + pathIdentifier := pathExpression.Identifier.Identifier + pathType, err := sema.CheckPathLiteral( - pathExpression.Domain.Identifier, - pathExpression.Identifier.Identifier, + pathDomain, + pathIdentifier, func() ast.Range { return ast.NewRangeFromPositioned(memoryGauge, pathExpression.Domain) }, @@ -167,9 +177,9 @@ func pathLiteralValue(memoryGauge common.MemoryGauge, expression ast.Expression, return cadence.NewMeteredPath( memoryGauge, - pathExpression.Domain.Identifier, - pathExpression.Identifier.Identifier, - ), nil + common.PathDomainFromIdentifier(pathDomain), + pathIdentifier, + ) } func integerLiteralValue( diff --git a/runtime/literal_test.go b/runtime/literal_test.go index ff4349aa83..9c86ce73fc 100644 --- a/runtime/literal_test.go +++ b/runtime/literal_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" . "github.com/onflow/cadence/runtime/tests/utils" ) @@ -240,7 +241,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, value, @@ -256,7 +257,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, value, @@ -272,7 +273,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, value, @@ -299,7 +300,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, value, @@ -348,7 +349,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, value, @@ -360,7 +361,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, value, @@ -386,7 +387,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "foo", }, value, @@ -419,7 +420,7 @@ func TestLiteralValue(t *testing.T) { require.NoError(t, err) require.Equal(t, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "foo", }, value, diff --git a/runtime/parser/parser.go b/runtime/parser/parser.go index 737f099623..069c808da3 100644 --- a/runtime/parser/parser.go +++ b/runtime/parser/parser.go @@ -592,6 +592,24 @@ func ParseStatements( ) } +func ParseStatementsFromTokenStream( + memoryGauge common.MemoryGauge, + tokens lexer.TokenStream, + config Config, +) ( + statements []ast.Statement, + errs []error, +) { + return ParseTokenStream( + memoryGauge, + tokens, + func(p *parser) ([]ast.Statement, error) { + return parseStatements(p, nil) + }, + config, + ) +} + func ParseType(memoryGauge common.MemoryGauge, input []byte, config Config) (ty ast.Type, errs []error) { return Parse( memoryGauge, diff --git a/runtime/pretty/print.go b/runtime/pretty/print.go index 33fc731ee5..077030537c 100644 --- a/runtime/pretty/print.go +++ b/runtime/pretty/print.go @@ -26,7 +26,7 @@ import ( "strconv" "strings" - "github.com/logrusorgru/aurora" + "github.com/logrusorgru/aurora/v4" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" diff --git a/runtime/repl.go b/runtime/repl.go index e0b0f528fd..90c0b8b4a9 100644 --- a/runtime/repl.go +++ b/runtime/repl.go @@ -32,6 +32,7 @@ import ( "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/parser" + "github.com/onflow/cadence/runtime/parser/lexer" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/stdlib" ) @@ -113,6 +114,45 @@ func (r *REPL) handleCheckerError() error { return err } +func isInputComplete(tokens lexer.TokenStream) bool { + var unmatchedBrackets, unmatchedParens, unmatchedBraces int + + for { + + token := tokens.Next() + + switch token.Type { + case lexer.TokenBracketOpen: + unmatchedBrackets++ + + case lexer.TokenBracketClose: + unmatchedBrackets-- + + case lexer.TokenParenOpen: + unmatchedParens++ + + case lexer.TokenParenClose: + unmatchedParens-- + + case lexer.TokenBraceOpen: + unmatchedBraces++ + + case lexer.TokenBraceClose: + unmatchedBraces-- + } + + if token.Is(lexer.TokenEOF) { + break + } + } + + tokens.Revert(0) + + return unmatchedBrackets <= 0 && + unmatchedParens <= 0 && + unmatchedBraces <= 0 +} + var lineSep = []byte{'\n'} func (r *REPL) Accept(code []byte) (inputIsComplete bool, err error) { @@ -188,10 +228,16 @@ func (r *REPL) Accept(code []byte) (inputIsComplete bool, err error) { code = prefixedCode } - // TODO: detect if the input is complete - inputIsComplete = true + tokens := lexer.Lex(code, nil) + defer tokens.Reclaim() + + inputIsComplete = isInputComplete(tokens) + + if !inputIsComplete { + return + } - result, errs := parser.ParseStatements(nil, code, parser.Config{}) + result, errs := parser.ParseStatementsFromTokenStream(nil, tokens, parser.Config{}) if len(errs) > 0 { err = parser.Error{ Code: code, @@ -199,10 +245,6 @@ func (r *REPL) Accept(code []byte) (inputIsComplete bool, err error) { } } - if !inputIsComplete { - return - } - if err != nil { r.onError(err, r.checker.Location, r.codes) return diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index ffa1ccdf51..72d79dc8ac 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -1706,6 +1706,23 @@ func TestRuntimeScriptArguments(t *testing.T) { }, expectedLogs: []string{`"bar"`}, }, + { + name: "Path subtype", + script: ` + pub fun main(x: StoragePath) { + log(x) + } + `, + args: [][]byte{ + jsoncdc.MustEncode(cadence.Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }), + }, + expectedLogs: []string{ + "/storage/foo", + }, + }, } test := func(tt testCase) { @@ -2736,10 +2753,9 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { } `, expected: cadence.Function{ - FunctionType: (&cadence.FunctionType{ - Parameters: []cadence.Parameter{}, + FunctionType: &cadence.FunctionType{ ReturnType: cadence.IntType{}, - }).WithID("fun():Int"), + }, }, }, ) @@ -2757,7 +2773,7 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { } `, expected: cadence.Function{ - FunctionType: (&cadence.FunctionType{ + FunctionType: &cadence.FunctionType{ Purity: sema.FunctionPurityView, Parameters: []cadence.Parameter{ { @@ -2767,7 +2783,7 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { }, }, ReturnType: cadence.NeverType{}, - }).WithID("view fun(String):Never"), + }, }, }, ) @@ -2790,10 +2806,9 @@ func TestRuntimeScriptReturnSpecial(t *testing.T) { } `, expected: cadence.Function{ - FunctionType: (&cadence.FunctionType{ - Parameters: []cadence.Parameter{}, + FunctionType: &cadence.FunctionType{ ReturnType: cadence.VoidType{}, - }).WithID("fun():Void"), + }, }, }, ) @@ -7028,7 +7043,7 @@ func TestRuntimeGetCapability(t *testing.T) { cadence.StorageCapability{ Address: cadence.BytesToAddress([]byte{0x1}), Path: cadence.Path{ - Domain: "public", + Domain: common.PathDomainPublic, Identifier: "xxx", }, }, @@ -7358,7 +7373,7 @@ func TestRuntimeInternalErrors(t *testing.T) { _, err = runtime.ReadStored( address, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, Context{ @@ -7391,7 +7406,7 @@ func TestRuntimeInternalErrors(t *testing.T) { _, err = runtime.ReadLinked( address, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, Context{ diff --git a/runtime/sema/account_contracts.go b/runtime/sema/account_contracts.go deleted file mode 100644 index 08c9d17003..0000000000 --- a/runtime/sema/account_contracts.go +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -const AccountContractsTypeGetFunctionName = "get" -const AccountContractsTypeBorrowFunctionName = "borrow" -const AccountContractsTypeNamesFieldName = "names" - -const accountContractsTypeGetFunctionDocString = ` -Returns the deployed contract for the contract/contract interface with the given name in the account, if any. - -Returns nil if no contract/contract interface with the given name exists in the account. -` - -var AccountContractsTypeGetFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: DeployedContractType, - }, - ), -} - -const accountContractsTypeBorrowFunctionDocString = ` -Returns a reference of the given type to the contract with the given name in the account, if any. - -Returns nil if no contract with the given name exists in the account, or if the contract does not conform to the given type. -` - -var AccountContractsTypeBorrowFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - } - - return &FunctionType{ - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -var accountContractsTypeNamesFieldType = &VariableSizedType{ - Type: StringType, -} - -const accountContractsTypeNamesFieldDocString = ` -Names of all contracts deployed in the account. -` diff --git a/runtime/sema/authaccount.cdc b/runtime/sema/authaccount.cdc new file mode 100644 index 0000000000..3b2ca673ad --- /dev/null +++ b/runtime/sema/authaccount.cdc @@ -0,0 +1,285 @@ + +pub struct AuthAccount { + + /// The address of the account. + pub let address: Address + + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 + + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 + + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 + + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 + + /// The contracts deployed to the account. + pub let contracts: AuthAccount.Contracts + + /// The keys assigned to the account. + pub let keys: AuthAccount.Keys + + /// The inbox allows bootstrapping (sending and receiving) capabilities. + pub let inbox: AuthAccount.Inbox + + /// All public paths of this account. + pub let publicPaths: [PublicPath] + + /// All private paths of this account. + pub let privatePaths: [PrivatePath] + + /// All storage paths of this account. + pub let storagePaths: [StoragePath] + + /// Saves the given object into the account's storage at the given path. + /// + /// Resources are moved into storage, and structures are copied. + /// + /// If there is already an object stored under the given path, the program aborts. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun save(_ value: T, to: StoragePath) + + /// Reads the type of an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, the type of the object is returned without modifying the stored object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub view fun type(at path: StoragePath): Type? + + /// Loads an object from the account's storage which is stored under the given path, + /// or nil if no object is stored under the given path. + /// + /// If there is an object stored, + /// the stored resource or structure is moved out of storage and returned as an optional. + /// + /// When the function returns, the storage no longer contains an object under the given path. + /// + /// The given type must be a supertype of the type of the loaded object. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the loaded object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun load(from: StoragePath): T? + + /// Returns a copy of a structure stored in account storage under the given path, + /// without removing it from storage, + /// or nil if no object is stored under the given path. + /// + /// If there is a structure stored, it is copied. + /// The structure stays stored in storage after the function returns. + /// + /// The given type must be a supertype of the type of the copied structure. + /// If it is not, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the copied structure. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed. + pub fun copy(from: StoragePath): T? + + /// Returns a reference to an object in storage without removing it from storage. + /// + /// If no object is stored under the given path, the function returns nil. + /// If there is an object stored, a reference is returned as an optional, + /// provided it can be borrowed using the given type. + /// If the stored object cannot be borrowed using the given type, the function panics. + /// + /// The given type must not necessarily be exactly the same as the type of the borrowed object. + /// + /// The path must be a storage path, i.e., only the domain `storage` is allowed + pub fun borrow(from: StoragePath): T? + + /// Creates a capability at the given public or private path, + /// which targets the given public, private, or storage path. + /// + /// The target path leads to the object that will provide the functionality defined by this capability. + /// + /// The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. + /// + /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. + /// + /// It is not necessary for the target path to lead to a valid object; the target path could be empty, + /// or could lead to an object which does not provide the necessary type interface: + /// The link function does **not** check if the target path is valid/exists at the time the capability is created + /// and does **not** check if the target value conforms to the given type. + /// + /// The link is latent. + /// + /// The target value might be stored after the link is created, + /// and the target value might be moved out after the link has been created. + pub fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? + + /// Creates a capability at the given public or private path which targets this account. + /// + /// Returns nil if a link for the given capability path already exists, or the newly created capability if not. + pub fun linkAccount(_ newCapabilityPath: PrivatePath): Capability<&AuthAccount>? + + /// Returns the capability at the given private or public path. + pub fun getCapability(_ path: CapabilityPath): Capability + + /// Returns the target path of the capability at the given public or private path, + /// or nil if there exists no capability at the given path. + pub fun getLinkTarget(_ path: CapabilityPath): Path? + + /// Removes the capability at the given public or private path. + pub fun unlink(_ path: CapabilityPath) + + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: fun(PublicPath, Type): Bool) + + /// Iterate over all the private paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPrivate(_ function: fun(PrivatePath, Type): Bool) + + /// Iterate over all the stored paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachStored(_ function: fun(StoragePath, Type): Bool) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Adds the given contract to the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// All additional arguments that are given are passed further to the initializer + /// of the contract that is being deployed. + /// + /// The function fails if a contract/contract interface with the given name already exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract. + pub fun add( + name: String, + code: [UInt8] + ): DeployedContract + + /// **Experimental** + /// + /// Updates the code for the contract/contract interface in the account. + /// + /// The `code` parameter is the UTF-8 encoded representation of the source code. + /// The code must contain exactly one contract or contract interface, + /// which must have the same name as the `name` parameter. + /// + /// Does **not** run the initializer of the contract/contract interface again. + /// The contract instance in the world state stays as is. + /// + /// Fails if no contract/contract interface with the given name exists in the account, + /// if the given code does not declare exactly one contract or contract interface, + /// or if the given name does not match the name of the contract/contract interface declaration in the code. + /// + /// Returns the deployed contract for the updated contract. + pub fun update__experimental(name: String, code: [UInt8]): DeployedContract + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Removes the contract/contract interface from the account which has the given name, if any. + /// + /// Returns the removed deployed contract, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun remove(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? + } + + pub struct Keys { + + /// Adds a new key with the given hashing algorithm and a weight. + /// + /// Returns the added key. + pub fun add( + publicKey: PublicKey, + hashAlgorithm: HashAlgorithm, + weight: UFix64 + ): AccountKey + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? + + /// Marks the key at the given index revoked, but does not delete it. + /// + /// Returns the revoked key if it exists, or nil otherwise. + pub fun revoke(keyIndex: Int): AccountKey? + + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: fun(AccountKey): Bool) + + /// The total number of unrevoked keys in this account. + pub let count: UInt64 + } + + pub struct Inbox { + + /// Publishes a new Capability under the given name, + /// to be claimed by the specified recipient. + pub fun publish(_ value: Capability, name: String, recipient: Address) + + /// Unpublishes a Capability previously published by this account. + /// + /// Returns `nil` if no Capability is published under the given name. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun unpublish(_ name: String): Capability? + + /// Claims a Capability previously published by the specified provider. + /// + /// Returns `nil` if no Capability is published under the given name, + /// or if this account is not its intended recipient. + /// + /// Errors if the Capability under that name does not match the provided type. + pub fun claim(_ name: String, provider: Address): Capability? + } +} diff --git a/runtime/sema/authaccount.gen.go b/runtime/sema/authaccount.gen.go new file mode 100644 index 0000000000..71d82a8e18 --- /dev/null +++ b/runtime/sema/authaccount.gen.go @@ -0,0 +1,1298 @@ +// Code generated from authaccount.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import "github.com/onflow/cadence/runtime/common" + +const AuthAccountTypeAddressFieldName = "address" + +var AuthAccountTypeAddressFieldType = TheAddressType + +const AuthAccountTypeAddressFieldDocString = ` +The address of the account. +` + +const AuthAccountTypeBalanceFieldName = "balance" + +var AuthAccountTypeBalanceFieldType = UFix64Type + +const AuthAccountTypeBalanceFieldDocString = ` +The FLOW balance of the default vault of this account. +` + +const AuthAccountTypeAvailableBalanceFieldName = "availableBalance" + +var AuthAccountTypeAvailableBalanceFieldType = UFix64Type + +const AuthAccountTypeAvailableBalanceFieldDocString = ` +The FLOW balance of the default vault of this account that is available to be moved. +` + +const AuthAccountTypeStorageUsedFieldName = "storageUsed" + +var AuthAccountTypeStorageUsedFieldType = UInt64Type + +const AuthAccountTypeStorageUsedFieldDocString = ` +The current amount of storage used by the account in bytes. +` + +const AuthAccountTypeStorageCapacityFieldName = "storageCapacity" + +var AuthAccountTypeStorageCapacityFieldType = UInt64Type + +const AuthAccountTypeStorageCapacityFieldDocString = ` +The storage capacity of the account in bytes. +` + +const AuthAccountTypeContractsFieldName = "contracts" + +var AuthAccountTypeContractsFieldType = AuthAccountContractsType + +const AuthAccountTypeContractsFieldDocString = ` +The contracts deployed to the account. +` + +const AuthAccountTypeKeysFieldName = "keys" + +var AuthAccountTypeKeysFieldType = AuthAccountKeysType + +const AuthAccountTypeKeysFieldDocString = ` +The keys assigned to the account. +` + +const AuthAccountTypeInboxFieldName = "inbox" + +var AuthAccountTypeInboxFieldType = AuthAccountInboxType + +const AuthAccountTypeInboxFieldDocString = ` +The inbox allows bootstrapping (sending and receiving) capabilities. +` + +const AuthAccountTypePublicPathsFieldName = "publicPaths" + +var AuthAccountTypePublicPathsFieldType = &VariableSizedType{ + Type: PublicPathType, +} + +const AuthAccountTypePublicPathsFieldDocString = ` +All public paths of this account. +` + +const AuthAccountTypePrivatePathsFieldName = "privatePaths" + +var AuthAccountTypePrivatePathsFieldType = &VariableSizedType{ + Type: PrivatePathType, +} + +const AuthAccountTypePrivatePathsFieldDocString = ` +All private paths of this account. +` + +const AuthAccountTypeStoragePathsFieldName = "storagePaths" + +var AuthAccountTypeStoragePathsFieldType = &VariableSizedType{ + Type: StoragePathType, +} + +const AuthAccountTypeStoragePathsFieldDocString = ` +All storage paths of this account. +` + +const AuthAccountTypeSaveFunctionName = "save" + +var AuthAccountTypeSaveFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: StorableType, +} + +var AuthAccountTypeSaveFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeSaveFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: NewTypeAnnotation(&GenericType{ + TypeParameter: AuthAccountTypeSaveFunctionTypeParameterT, + }), + }, + { + Identifier: "to", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeSaveFunctionDocString = ` +Saves the given object into the account's storage at the given path. + +Resources are moved into storage, and structures are copied. + +If there is already an object stored under the given path, the program aborts. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeTypeFunctionName = "type" + +var AuthAccountTypeTypeFunctionType = &FunctionType{ + Purity: FunctionPurityView, + Parameters: []Parameter{ + { + Label: "at", + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MetaType, + }, + ), +} + +const AuthAccountTypeTypeFunctionDocString = ` +Reads the type of an object from the account's storage which is stored under the given path, +or nil if no object is stored under the given path. + +If there is an object stored, the type of the object is returned without modifying the stored object. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeLoadFunctionName = "load" + +var AuthAccountTypeLoadFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: StorableType, +} + +var AuthAccountTypeLoadFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeLoadFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "from", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountTypeLoadFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountTypeLoadFunctionDocString = ` +Loads an object from the account's storage which is stored under the given path, +or nil if no object is stored under the given path. + +If there is an object stored, +the stored resource or structure is moved out of storage and returned as an optional. + +When the function returns, the storage no longer contains an object under the given path. + +The given type must be a supertype of the type of the loaded object. +If it is not, the function panics. + +The given type must not necessarily be exactly the same as the type of the loaded object. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeCopyFunctionName = "copy" + +var AuthAccountTypeCopyFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: AnyStructType, +} + +var AuthAccountTypeCopyFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeCopyFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "from", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountTypeCopyFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountTypeCopyFunctionDocString = ` +Returns a copy of a structure stored in account storage under the given path, +without removing it from storage, +or nil if no object is stored under the given path. + +If there is a structure stored, it is copied. +The structure stays stored in storage after the function returns. + +The given type must be a supertype of the type of the copied structure. +If it is not, the function panics. + +The given type must not necessarily be exactly the same as the type of the copied structure. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed. +` + +const AuthAccountTypeBorrowFunctionName = "borrow" + +var AuthAccountTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "from", + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountTypeBorrowFunctionDocString = ` +Returns a reference to an object in storage without removing it from storage. + +If no object is stored under the given path, the function returns nil. +If there is an object stored, a reference is returned as an optional, +provided it can be borrowed using the given type. +If the stored object cannot be borrowed using the given type, the function panics. + +The given type must not necessarily be exactly the same as the type of the borrowed object. + +The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed +` + +const AuthAccountTypeLinkFunctionName = "link" + +var AuthAccountTypeLinkFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountTypeLinkFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeLinkFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "newCapabilityPath", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + { + Identifier: "target", + TypeAnnotation: NewTypeAnnotation(PathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountTypeLinkFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountTypeLinkFunctionDocString = ` +Creates a capability at the given public or private path, +which targets the given public, private, or storage path. + +The target path leads to the object that will provide the functionality defined by this capability. + +The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. + +Returns nil if a link for the given capability path already exists, or the newly created capability if not. + +It is not necessary for the target path to lead to a valid object; the target path could be empty, +or could lead to an object which does not provide the necessary type interface: +The link function does **not** check if the target path is valid/exists at the time the capability is created +and does **not** check if the target value conforms to the given type. + +The link is latent. + +The target value might be stored after the link is created, +and the target value might be moved out after the link has been created. +` + +const AuthAccountTypeLinkAccountFunctionName = "linkAccount" + +var AuthAccountTypeLinkAccountFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "newCapabilityPath", + TypeAnnotation: NewTypeAnnotation(PrivatePathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &ReferenceType{ + Type: AuthAccountType, + }, + ), + }, + ), +} + +const AuthAccountTypeLinkAccountFunctionDocString = ` +Creates a capability at the given public or private path which targets this account. + +Returns nil if a link for the given capability path already exists, or the newly created capability if not. +` + +const AuthAccountTypeGetCapabilityFunctionName = "getCapability" + +var AuthAccountTypeGetCapabilityFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountTypeGetCapabilityFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountTypeGetCapabilityFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountTypeGetCapabilityFunctionTypeParameterT, + }, + ), + ), +} + +const AuthAccountTypeGetCapabilityFunctionDocString = ` +Returns the capability at the given private or public path. +` + +const AuthAccountTypeGetLinkTargetFunctionName = "getLinkTarget" + +var AuthAccountTypeGetLinkTargetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: PathType, + }, + ), +} + +const AuthAccountTypeGetLinkTargetFunctionDocString = ` +Returns the target path of the capability at the given public or private path, +or nil if there exists no capability at the given path. +` + +const AuthAccountTypeUnlinkFunctionName = "unlink" + +var AuthAccountTypeUnlinkFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeUnlinkFunctionDocString = ` +Removes the capability at the given public or private path. +` + +const AuthAccountTypeForEachPublicFunctionName = "forEachPublic" + +var AuthAccountTypeForEachPublicFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeForEachPublicFunctionDocString = ` +Iterate over all the public paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const AuthAccountTypeForEachPrivateFunctionName = "forEachPrivate" + +var AuthAccountTypeForEachPrivateFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(PrivatePathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeForEachPrivateFunctionDocString = ` +Iterate over all the private paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const AuthAccountTypeForEachStoredFunctionName = "forEachStored" + +var AuthAccountTypeForEachStoredFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(StoragePathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountTypeForEachStoredFunctionDocString = ` +Iterate over all the stored paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const AuthAccountContractsTypeNamesFieldName = "names" + +var AuthAccountContractsTypeNamesFieldType = &VariableSizedType{ + Type: StringType, +} + +const AuthAccountContractsTypeNamesFieldDocString = ` +The names of all contracts deployed in the account. +` + +const AuthAccountContractsTypeAddFunctionName = "add" + +var AuthAccountContractsTypeAddFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "code", + TypeAnnotation: NewTypeAnnotation(&VariableSizedType{ + Type: UInt8Type, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + DeployedContractType, + ), +} + +const AuthAccountContractsTypeAddFunctionDocString = ` +Adds the given contract to the account. + +The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. +The code must contain exactly one contract or contract interface, +which must have the same name as the ` + "`name`" + ` parameter. + +All additional arguments that are given are passed further to the initializer +of the contract that is being deployed. + +The function fails if a contract/contract interface with the given name already exists in the account, +if the given code does not declare exactly one contract or contract interface, +or if the given name does not match the name of the contract/contract interface declaration in the code. + +Returns the deployed contract. +` + +const AuthAccountContractsTypeUpdate__experimentalFunctionName = "update__experimental" + +var AuthAccountContractsTypeUpdate__experimentalFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "code", + TypeAnnotation: NewTypeAnnotation(&VariableSizedType{ + Type: UInt8Type, + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + DeployedContractType, + ), +} + +const AuthAccountContractsTypeUpdate__experimentalFunctionDocString = ` +**Experimental** + +Updates the code for the contract/contract interface in the account. + +The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. +The code must contain exactly one contract or contract interface, +which must have the same name as the ` + "`name`" + ` parameter. + +Does **not** run the initializer of the contract/contract interface again. +The contract instance in the world state stays as is. + +Fails if no contract/contract interface with the given name exists in the account, +if the given code does not declare exactly one contract or contract interface, +or if the given name does not match the name of the contract/contract interface declaration in the code. + +Returns the deployed contract for the updated contract. +` + +const AuthAccountContractsTypeGetFunctionName = "get" + +var AuthAccountContractsTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: DeployedContractType, + }, + ), +} + +const AuthAccountContractsTypeGetFunctionDocString = ` +Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + +Returns nil if no contract/contract interface with the given name exists in the account. +` + +const AuthAccountContractsTypeRemoveFunctionName = "remove" + +var AuthAccountContractsTypeRemoveFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: DeployedContractType, + }, + ), +} + +const AuthAccountContractsTypeRemoveFunctionDocString = ` +Removes the contract/contract interface from the account which has the given name, if any. + +Returns the removed deployed contract, if any. + +Returns nil if no contract/contract interface with the given name exists in the account. +` + +const AuthAccountContractsTypeBorrowFunctionName = "borrow" + +var AuthAccountContractsTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountContractsTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountContractsTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: AuthAccountContractsTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const AuthAccountContractsTypeBorrowFunctionDocString = ` +Returns a reference of the given type to the contract with the given name in the account, if any. + +Returns nil if no contract with the given name exists in the account, +or if the contract does not conform to the given type. +` + +const AuthAccountContractsTypeName = "Contracts" + +var AuthAccountContractsType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountContractsTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + AuthAccountContractsType, + AuthAccountContractsTypeNamesFieldName, + AuthAccountContractsTypeNamesFieldType, + AuthAccountContractsTypeNamesFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeAddFunctionName, + AuthAccountContractsTypeAddFunctionType, + AuthAccountContractsTypeAddFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeUpdate__experimentalFunctionName, + AuthAccountContractsTypeUpdate__experimentalFunctionType, + AuthAccountContractsTypeUpdate__experimentalFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeGetFunctionName, + AuthAccountContractsTypeGetFunctionType, + AuthAccountContractsTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeRemoveFunctionName, + AuthAccountContractsTypeRemoveFunctionType, + AuthAccountContractsTypeRemoveFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountContractsType, + AuthAccountContractsTypeBorrowFunctionName, + AuthAccountContractsTypeBorrowFunctionType, + AuthAccountContractsTypeBorrowFunctionDocString, + ), + } + + AuthAccountContractsType.Members = MembersAsMap(members) + AuthAccountContractsType.Fields = MembersFieldNames(members) +} + +const AuthAccountKeysTypeAddFunctionName = "add" + +var AuthAccountKeysTypeAddFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "publicKey", + TypeAnnotation: NewTypeAnnotation(PublicKeyType), + }, + { + Identifier: "hashAlgorithm", + TypeAnnotation: NewTypeAnnotation(HashAlgorithmType), + }, + { + Identifier: "weight", + TypeAnnotation: NewTypeAnnotation(UFix64Type), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + AccountKeyType, + ), +} + +const AuthAccountKeysTypeAddFunctionDocString = ` +Adds a new key with the given hashing algorithm and a weight. + +Returns the added key. +` + +const AuthAccountKeysTypeGetFunctionName = "get" + +var AuthAccountKeysTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "keyIndex", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: AccountKeyType, + }, + ), +} + +const AuthAccountKeysTypeGetFunctionDocString = ` +Returns the key at the given index, if it exists, or nil otherwise. + +Revoked keys are always returned, but they have ` + "`isRevoked`" + ` field set to true. +` + +const AuthAccountKeysTypeRevokeFunctionName = "revoke" + +var AuthAccountKeysTypeRevokeFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "keyIndex", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: AccountKeyType, + }, + ), +} + +const AuthAccountKeysTypeRevokeFunctionDocString = ` +Marks the key at the given index revoked, but does not delete it. + +Returns the revoked key if it exists, or nil otherwise. +` + +const AuthAccountKeysTypeForEachFunctionName = "forEach" + +var AuthAccountKeysTypeForEachFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(AccountKeyType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountKeysTypeForEachFunctionDocString = ` +Iterate over all unrevoked keys in this account, +passing each key in turn to the provided function. + +Iteration is stopped early if the function returns ` + "`false`" + `. +The order of iteration is undefined. +` + +const AuthAccountKeysTypeCountFieldName = "count" + +var AuthAccountKeysTypeCountFieldType = UInt64Type + +const AuthAccountKeysTypeCountFieldDocString = ` +The total number of unrevoked keys in this account. +` + +const AuthAccountKeysTypeName = "Keys" + +var AuthAccountKeysType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountKeysTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeAddFunctionName, + AuthAccountKeysTypeAddFunctionType, + AuthAccountKeysTypeAddFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeGetFunctionName, + AuthAccountKeysTypeGetFunctionType, + AuthAccountKeysTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeRevokeFunctionName, + AuthAccountKeysTypeRevokeFunctionType, + AuthAccountKeysTypeRevokeFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountKeysType, + AuthAccountKeysTypeForEachFunctionName, + AuthAccountKeysTypeForEachFunctionType, + AuthAccountKeysTypeForEachFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountKeysType, + AuthAccountKeysTypeCountFieldName, + AuthAccountKeysTypeCountFieldType, + AuthAccountKeysTypeCountFieldDocString, + ), + } + + AuthAccountKeysType.Members = MembersAsMap(members) + AuthAccountKeysType.Fields = MembersFieldNames(members) +} + +const AuthAccountInboxTypePublishFunctionName = "publish" + +var AuthAccountInboxTypePublishFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: NewTypeAnnotation(&CapabilityType{}), + }, + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "recipient", + TypeAnnotation: NewTypeAnnotation(TheAddressType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const AuthAccountInboxTypePublishFunctionDocString = ` +Publishes a new Capability under the given name, +to be claimed by the specified recipient. +` + +const AuthAccountInboxTypeUnpublishFunctionName = "unpublish" + +var AuthAccountInboxTypeUnpublishFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountInboxTypeUnpublishFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountInboxTypeUnpublishFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountInboxTypeUnpublishFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountInboxTypeUnpublishFunctionDocString = ` +Unpublishes a Capability previously published by this account. + +Returns ` + "`nil`" + ` if no Capability is published under the given name. + +Errors if the Capability under that name does not match the provided type. +` + +const AuthAccountInboxTypeClaimFunctionName = "claim" + +var AuthAccountInboxTypeClaimFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var AuthAccountInboxTypeClaimFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + AuthAccountInboxTypeClaimFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + { + Identifier: "provider", + TypeAnnotation: NewTypeAnnotation(TheAddressType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: AuthAccountInboxTypeClaimFunctionTypeParameterT, + }, + ), + }, + ), +} + +const AuthAccountInboxTypeClaimFunctionDocString = ` +Claims a Capability previously published by the specified provider. + +Returns ` + "`nil`" + ` if no Capability is published under the given name, +or if this account is not its intended recipient. + +Errors if the Capability under that name does not match the provided type. +` + +const AuthAccountInboxTypeName = "Inbox" + +var AuthAccountInboxType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountInboxTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + AuthAccountInboxType, + AuthAccountInboxTypePublishFunctionName, + AuthAccountInboxTypePublishFunctionType, + AuthAccountInboxTypePublishFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountInboxType, + AuthAccountInboxTypeUnpublishFunctionName, + AuthAccountInboxTypeUnpublishFunctionType, + AuthAccountInboxTypeUnpublishFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountInboxType, + AuthAccountInboxTypeClaimFunctionName, + AuthAccountInboxTypeClaimFunctionType, + AuthAccountInboxTypeClaimFunctionDocString, + ), + } + + AuthAccountInboxType.Members = MembersAsMap(members) + AuthAccountInboxType.Fields = MembersFieldNames(members) +} + +const AuthAccountTypeName = "AuthAccount" + +var AuthAccountType = func() *CompositeType { + var t = &CompositeType{ + Identifier: AuthAccountTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + t.SetNestedType(AuthAccountContractsTypeName, AuthAccountContractsType) + t.SetNestedType(AuthAccountKeysTypeName, AuthAccountKeysType) + t.SetNestedType(AuthAccountInboxTypeName, AuthAccountInboxType) + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeAddressFieldName, + AuthAccountTypeAddressFieldType, + AuthAccountTypeAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeBalanceFieldName, + AuthAccountTypeBalanceFieldType, + AuthAccountTypeBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeAvailableBalanceFieldName, + AuthAccountTypeAvailableBalanceFieldType, + AuthAccountTypeAvailableBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStorageUsedFieldName, + AuthAccountTypeStorageUsedFieldType, + AuthAccountTypeStorageUsedFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStorageCapacityFieldName, + AuthAccountTypeStorageCapacityFieldType, + AuthAccountTypeStorageCapacityFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeContractsFieldName, + AuthAccountTypeContractsFieldType, + AuthAccountTypeContractsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeKeysFieldName, + AuthAccountTypeKeysFieldType, + AuthAccountTypeKeysFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeInboxFieldName, + AuthAccountTypeInboxFieldType, + AuthAccountTypeInboxFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypePublicPathsFieldName, + AuthAccountTypePublicPathsFieldType, + AuthAccountTypePublicPathsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypePrivatePathsFieldName, + AuthAccountTypePrivatePathsFieldType, + AuthAccountTypePrivatePathsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + AuthAccountType, + AuthAccountTypeStoragePathsFieldName, + AuthAccountTypeStoragePathsFieldType, + AuthAccountTypeStoragePathsFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeSaveFunctionName, + AuthAccountTypeSaveFunctionType, + AuthAccountTypeSaveFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeTypeFunctionName, + AuthAccountTypeTypeFunctionType, + AuthAccountTypeTypeFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeLoadFunctionName, + AuthAccountTypeLoadFunctionType, + AuthAccountTypeLoadFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeCopyFunctionName, + AuthAccountTypeCopyFunctionType, + AuthAccountTypeCopyFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeBorrowFunctionName, + AuthAccountTypeBorrowFunctionType, + AuthAccountTypeBorrowFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeLinkFunctionName, + AuthAccountTypeLinkFunctionType, + AuthAccountTypeLinkFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeLinkAccountFunctionName, + AuthAccountTypeLinkAccountFunctionType, + AuthAccountTypeLinkAccountFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeGetCapabilityFunctionName, + AuthAccountTypeGetCapabilityFunctionType, + AuthAccountTypeGetCapabilityFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeGetLinkTargetFunctionName, + AuthAccountTypeGetLinkTargetFunctionType, + AuthAccountTypeGetLinkTargetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeUnlinkFunctionName, + AuthAccountTypeUnlinkFunctionType, + AuthAccountTypeUnlinkFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeForEachPublicFunctionName, + AuthAccountTypeForEachPublicFunctionType, + AuthAccountTypeForEachPublicFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeForEachPrivateFunctionName, + AuthAccountTypeForEachPrivateFunctionType, + AuthAccountTypeForEachPrivateFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + AuthAccountType, + AuthAccountTypeForEachStoredFunctionName, + AuthAccountTypeForEachStoredFunctionType, + AuthAccountTypeForEachStoredFunctionDocString, + ), + } + + AuthAccountType.Members = MembersAsMap(members) + AuthAccountType.Fields = MembersFieldNames(members) +} diff --git a/runtime/sema/authaccount.go b/runtime/sema/authaccount.go new file mode 100644 index 0000000000..1d8a1166a6 --- /dev/null +++ b/runtime/sema/authaccount.go @@ -0,0 +1,31 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +//go:generate go run ./gen authaccount.cdc authaccount.gen.go + +var AuthAccountTypeLinkAccountFunctionTypePathParameterTypeAnnotation = AuthAccountTypeLinkAccountFunctionType.Parameters[0].TypeAnnotation + +var AuthAccountTypeAnnotation = NewTypeAnnotation(AuthAccountType) + +func init() { + AuthAccountContractsTypeAddFunctionType.RequiredArgumentCount = RequiredArgumentCount(2) + AuthAccountTypeGetCapabilityFunctionType.TypeParameters[0].Optional = true + PublicAccountTypeGetCapabilityFunctionType.TypeParameters[0].Optional = true +} diff --git a/runtime/sema/authaccount_contracts.go b/runtime/sema/authaccount_contracts.go deleted file mode 100644 index b05f7deabc..0000000000 --- a/runtime/sema/authaccount_contracts.go +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const AuthAccountContractsTypeName = "Contracts" -const AuthAccountContractsTypeAddFunctionName = "add" -const AuthAccountContractsTypeUpdateExperimentalFunctionName = "update__experimental" -const AuthAccountContractsTypeRemoveFunctionName = "remove" - -// AuthAccountContractsType represents the type `AuthAccount.Contracts` -var AuthAccountContractsType = func() *CompositeType { - - authAccountContractsType := &CompositeType{ - Identifier: AuthAccountContractsTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AuthAccountContractsTypeAddFunctionName, - AuthAccountContractsTypeAddFunctionType, - authAccountContractsTypeAddFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AuthAccountContractsTypeUpdateExperimentalFunctionName, - AuthAccountContractsTypeUpdateExperimentalFunctionType, - authAccountContractsTypeUpdateExperimentalFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AccountContractsTypeGetFunctionName, - AccountContractsTypeGetFunctionType, - accountContractsTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AccountContractsTypeBorrowFunctionName, - AccountContractsTypeBorrowFunctionType, - accountContractsTypeBorrowFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountContractsType, - AuthAccountContractsTypeRemoveFunctionName, - AuthAccountContractsTypeRemoveFunctionType, - authAccountContractsTypeRemoveFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountContractsType, - AccountContractsTypeNamesFieldName, - accountContractsTypeNamesFieldType, - accountContractsTypeNamesFieldDocString, - ), - } - - authAccountContractsType.Members = GetMembersAsMap(members) - authAccountContractsType.Fields = GetFieldNames(members) - return authAccountContractsType -}() - -func init() { - // Set the container type after initializing the `AuthAccountContractsType`, to avoid initializing loop. - AuthAccountContractsType.SetContainerType(AuthAccountType) -} - -const authAccountContractsTypeAddFunctionDocString = ` -Adds the given contract to the account. - -The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. -The code must contain exactly one contract or contract interface, -which must have the same name as the ` + "`name`" + ` parameter. - -All additional arguments that are given are passed further to the initializer -of the contract that is being deployed. - -Fails if a contract/contract interface with the given name already exists in the account, -if the given code does not declare exactly one contract or contract interface, -or if the given name does not match the name of the contract/contract interface declaration in the code. - -Returns the deployed contract. -` - -var AuthAccountContractsTypeAddFunctionType = &FunctionType{ - Purity: FunctionPurityImpure, - Parameters: []Parameter{ - { - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - { - Identifier: "code", - TypeAnnotation: ByteArrayTypeAnnotation, - }, - }, - ReturnTypeAnnotation: DeployedContractTypeAnnotation, - // additional arguments are passed to the contract initializer - RequiredArgumentCount: RequiredArgumentCount(2), -} - -var OptionalDeployedContractTypeAnnotation = NewTypeAnnotation( - &OptionalType{ - Type: DeployedContractType, - }, -) - -const authAccountContractsTypeUpdateExperimentalFunctionDocString = ` -**Experimental** - -Updates the code for the contract/contract interface in the account. - -The ` + "`code`" + ` parameter is the UTF-8 encoded representation of the source code. -The code must contain exactly one contract or contract interface, -which must have the same name as the ` + "`name`" + ` parameter. - -Does **not** run the initializer of the contract/contract interface again. -The contract instance in the world state stays as is. - -Fails if no contract/contract interface with the given name exists in the account, -if the given code does not declare exactly one contract or contract interface, -or if the given name does not match the name of the contract/contract interface declaration in the code. - -Returns the deployed contract for the updated contract. -` - -var AuthAccountContractsTypeUpdateExperimentalFunctionType = NewSimpleFunctionType( - FunctionPurityImpure, - []Parameter{ - { - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - { - Identifier: "code", - TypeAnnotation: ByteArrayTypeAnnotation, - }, - }, - DeployedContractTypeAnnotation, -) - -const authAccountContractsTypeRemoveFunctionDocString = ` -Removes the contract/contract interface from the account which has the given name, if any. - -Returns the removed deployed contract, if any. - -Returns nil if no contract/contract interface with the given name exists in the account. -` - -var AuthAccountContractsTypeRemoveFunctionType = NewSimpleFunctionType( - FunctionPurityImpure, - []Parameter{ - { - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - }, - OptionalDeployedContractTypeAnnotation, -) diff --git a/runtime/sema/authaccount_type.go b/runtime/sema/authaccount_type.go deleted file mode 100644 index 53606d550b..0000000000 --- a/runtime/sema/authaccount_type.go +++ /dev/null @@ -1,953 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const AuthAccountTypeName = "AuthAccount" - -const AuthAccountTypeAddressFieldName = "address" -const AuthAccountTypeBalanceFieldName = "balance" -const AuthAccountTypeAvailableBalanceFieldName = "availableBalance" -const AuthAccountTypeStorageUsedFieldName = "storageUsed" -const AuthAccountTypeStorageCapacityFieldName = "storageCapacity" -const AuthAccountTypeSaveFunctionName = "save" -const AuthAccountTypeLoadFunctionName = "load" -const AuthAccountTypeTypeFunctionName = "type" -const AuthAccountTypeCopyFunctionName = "copy" -const AuthAccountTypeBorrowFunctionName = "borrow" -const AuthAccountTypeLinkFunctionName = "link" -const AuthAccountTypeLinkAccountFunctionName = "linkAccount" -const AuthAccountTypeUnlinkFunctionName = "unlink" -const AuthAccountTypeGetCapabilityFunctionName = "getCapability" -const AuthAccountTypeGetLinkTargetFunctionName = "getLinkTarget" -const AuthAccountTypeForEachPublicFunctionName = "forEachPublic" -const AuthAccountTypeForEachPrivateFunctionName = "forEachPrivate" -const AuthAccountTypeForEachStoredFunctionName = "forEachStored" -const AuthAccountTypeContractsFieldName = "contracts" -const AuthAccountTypeKeysFieldName = "keys" -const AuthAccountTypeInboxFieldName = "inbox" -const AuthAccountTypePublicPathsFieldName = "publicPaths" -const AuthAccountTypePrivatePathsFieldName = "privatePaths" -const AuthAccountTypeStoragePathsFieldName = "storagePaths" -const AuthAccountTypeInboxPublishFunctionName = "publish" -const AuthAccountTypeInboxUnpublishFunctionName = "unpublish" -const AuthAccountTypeInboxClaimFunctionName = "claim" - -var AuthAccountTypeLinkAccountFunctionType *FunctionType - -var AuthAccountTypeLinkAccountFunctionTypePathParameterType = PrivatePathType - -// AuthAccountType represents the authorized access to an account. -// Access to an AuthAccount means having full access to its storage, public keys, and code. -// Only signed transactions can get the AuthAccount for an account. -var AuthAccountType = func() *CompositeType { - - authAccountType := &CompositeType{ - Identifier: AuthAccountTypeName, - Kind: common.CompositeKindStructure, - hasComputedMembers: true, - importable: false, - NestedTypes: func() *StringTypeOrderedMap { - nestedTypes := &StringTypeOrderedMap{} - nestedTypes.Set(AuthAccountContractsTypeName, AuthAccountContractsType) - nestedTypes.Set(AccountKeysTypeName, AuthAccountKeysType) - nestedTypes.Set(AuthAccountInboxTypeName, AuthAccountInboxType) - return nestedTypes - }(), - } - - AuthAccountTypeLinkAccountFunctionType = &FunctionType{ - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "newCapabilityPath", - TypeAnnotation: NewTypeAnnotation( - AuthAccountTypeLinkAccountFunctionTypePathParameterType, - ), - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &ReferenceType{ - Type: authAccountType, - }, - }, - }, - ), - } - - var members = []*Member{ - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeAddressFieldName, - TheAddressType, - accountTypeAddressFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeBalanceFieldName, - UFix64Type, - accountTypeAccountBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeAvailableBalanceFieldName, - UFix64Type, - accountTypeAccountAvailableBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeStorageUsedFieldName, - UInt64Type, - accountTypeStorageUsedFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeStorageCapacityFieldName, - UInt64Type, - accountTypeStorageCapacityFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeSaveFunctionName, - AuthAccountTypeSaveFunctionType, - authAccountTypeSaveFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeTypeFunctionName, - AuthAccountTypeTypeFunctionType, - authAccountTypeTypeFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeLoadFunctionName, - AuthAccountTypeLoadFunctionType, - authAccountTypeLoadFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeCopyFunctionName, - AuthAccountTypeCopyFunctionType, - authAccountTypeCopyFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeBorrowFunctionName, - AuthAccountTypeBorrowFunctionType, - authAccountTypeBorrowFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeLinkFunctionName, - AuthAccountTypeLinkFunctionType, - authAccountTypeLinkFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeLinkAccountFunctionName, - AuthAccountTypeLinkAccountFunctionType, - authAccountTypeLinkAccountFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeUnlinkFunctionName, - AuthAccountTypeUnlinkFunctionType, - authAccountTypeUnlinkFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeGetCapabilityFunctionName, - AuthAccountTypeGetCapabilityFunctionType, - authAccountTypeGetCapabilityFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeGetLinkTargetFunctionName, - AccountTypeGetLinkTargetFunctionType, - accountTypeGetLinkTargetFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeContractsFieldName, - AuthAccountContractsType, - accountTypeContractsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeKeysFieldName, - AuthAccountKeysType, - accountTypeKeysFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeInboxFieldName, - AuthAccountInboxType, - accountInboxDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypePublicPathsFieldName, - AuthAccountPublicPathsType, - authAccountTypePublicPathsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypePrivatePathsFieldName, - AuthAccountPrivatePathsType, - authAccountTypePrivatePathsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - authAccountType, - AuthAccountTypeStoragePathsFieldName, - AuthAccountStoragePathsType, - authAccountTypeStoragePathsFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeForEachPublicFunctionName, - AuthAccountForEachPublicFunctionType, - authAccountForEachPublicDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeForEachPrivateFunctionName, - AuthAccountForEachPrivateFunctionType, - authAccountForEachPrivateDocString, - ), - NewUnmeteredPublicFunctionMember( - authAccountType, - AuthAccountTypeForEachStoredFunctionName, - AuthAccountForEachStoredFunctionType, - authAccountForEachStoredDocString, - ), - } - - authAccountType.Members = GetMembersAsMap(members) - authAccountType.Fields = GetFieldNames(members) - return authAccountType -}() - -var AuthAccountTypeAnnotation = NewTypeAnnotation(AuthAccountType) - -var AuthAccountPublicPathsType = &VariableSizedType{ - Type: PublicPathType, -} - -var AuthAccountPrivatePathsType = &VariableSizedType{ - Type: PrivatePathType, -} - -var AuthAccountStoragePathsType = &VariableSizedType{ - Type: StoragePathType, -} - -const authAccountTypeStoragePathsFieldDocString = ` -All the storage paths of an account -` - -const authAccountTypePublicPathsFieldDocString = ` -All the public paths of an account -` - -const authAccountTypePrivatePathsFieldDocString = ` -All the private paths of an account -` - -const authAccountForEachPublicDocString = ` -Iterate over all the public paths of an account. Takes one argument: the function to be applied to each public path. - -This function parameter takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object. - -The function parameter returns a bool indicating whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. - -The order of iteration, as well as the behavior of adding or removing keys from storage during iteration, is undefined. -` - -const authAccountForEachPrivateDocString = ` -Iterate over all the private paths of an account. Takes one argument: the function to be applied to each private path. - -This function parameter takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object. - -The function parameter returns a bool indicating whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. - -The order of iteration, as well as the behavior of adding or removing keys from storage during iteration, is undefined. -` - -const authAccountForEachStoredDocString = ` -Iterate over all the storage paths of an account. Takes one argument: the function to be applied to each storage path. - -This function parameter takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object. - -The function parameter returns a bool indicating whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. - -The order of iteration, as well as the behavior of adding or removing keys from storage during iteration, is undefined. -` - -var AuthAccountForEachPublicFunctionType = AccountForEachFunctionType(PublicPathType) - -var AuthAccountForEachPrivateFunctionType = AccountForEachFunctionType(PrivatePathType) - -var AuthAccountForEachStoredFunctionType = AccountForEachFunctionType(StoragePathType) - -var AuthAccountTypeSaveFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: StorableType, - } - - return &FunctionType{ - Purity: FunctionPurityImpure, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: NewTypeAnnotation( - &GenericType{ - TypeParameter: typeParameter, - }, - ), - }, - { - Label: "to", - Identifier: "path", - TypeAnnotation: StoragePathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: VoidTypeAnnotation, - } -}() - -const authAccountTypeSaveFunctionDocString = ` -Saves the given object into the account's storage at the given path. -Resources are moved into storage, and structures are copied. - -If there is already an object stored under the given path, the program aborts. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeLoadFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: StorableType, - } - - return &FunctionType{ - Purity: FunctionPurityImpure, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: "from", - Identifier: "path", - TypeAnnotation: StoragePathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeTypeFunctionDocString = ` -Reads the type of an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path. - -If there is an object stored, the type of the object is returned without modifying the stored object. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeTypeFunctionType = NewSimpleFunctionType( - FunctionPurityView, - []Parameter{ - { - Label: "at", - Identifier: "path", - TypeAnnotation: StoragePathTypeAnnotation, - }, - }, - NewTypeAnnotation( - &OptionalType{ - Type: MetaType, - }, - ), -) - -const authAccountTypeLoadFunctionDocString = ` -Loads an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path. - -If there is an object stored, the stored resource or structure is moved out of storage and returned as an optional. - -When the function returns, the storage no longer contains an object under the given path. - -The given type must be a supertype of the type of the loaded object. -If it is not, the function panics. - -The given type must not necessarily be exactly the same as the type of the loaded object. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeCopyFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: AnyStructType, - } - - return &FunctionType{ - Purity: FunctionPurityView, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: "from", - Identifier: "path", - TypeAnnotation: StoragePathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeCopyFunctionDocString = ` -Returns a copy of a structure stored in account storage under the given path, without removing it from storage, or nil if no object is stored under the given path. - -If there is a structure stored, it is copied. -The structure stays stored in storage after the function returns. - -The given type must be a supertype of the type of the copied structure. -If it is not, the function panics. - -The given type must not necessarily be exactly the same as the type of the copied structure. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeBorrowFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - } - - return &FunctionType{ - Purity: FunctionPurityView, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: "from", - Identifier: "path", - TypeAnnotation: StoragePathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeBorrowFunctionDocString = ` -Returns a reference to an object in storage without removing it from storage. - -If no object is stored under the given path, the function returns nil. -If there is an object stored, a reference is returned as an optional, provided it can be borrowed using the given type. -If the stored object cannot be borrowed using the given type, the function panics. - -The given type must not necessarily be exactly the same as the type of the borrowed object. - -The path must be a storage path, i.e., only the domain ` + "`storage`" + ` is allowed -` - -var AuthAccountTypeLinkFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - } - - return &FunctionType{ - Purity: FunctionPurityImpure, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "newCapabilityPath", - TypeAnnotation: CapabilityPathTypeAnnotation, - }, - { - Identifier: "target", - TypeAnnotation: PathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - }, - ), - } -}() - -const authAccountTypeLinkFunctionDocString = ` -Creates a capability at the given public or private path which targets the given public, private, or storage path. -The target path leads to the object that will provide the functionality defined by this capability. - -The given type defines how the capability can be borrowed, i.e., how the stored value can be accessed. - -Returns nil if a link for the given capability path already exists, or the newly created capability if not. - -It is not necessary for the target path to lead to a valid object; the target path could be empty, or could lead to an object which does not provide the necessary type interface: -The link function does **not** check if the target path is valid/exists at the time the capability is created and does **not** check if the target value conforms to the given type. -The link is latent. The target value might be stored after the link is created, and the target value might be moved out after the link has been created. -` - -const authAccountTypeLinkAccountFunctionDocString = ` -Creates a capability at the given public or private path which targets this account. - -Returns nil if a link for the given capability path already exists, or the newly created capability if not. -` - -var AuthAccountTypeUnlinkFunctionType = NewSimpleFunctionType( - FunctionPurityImpure, - []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: CapabilityPathTypeAnnotation, - }, - }, - VoidTypeAnnotation, -) - -const authAccountTypeUnlinkFunctionDocString = ` -Removes the capability at the given public or private path -` - -var AuthAccountTypeGetCapabilityFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - Optional: true, - } - - return &FunctionType{ - Purity: FunctionPurityView, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: CapabilityPathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const authAccountTypeGetCapabilityFunctionDocString = ` -Returns the capability at the given private or public path, or nil if it does not exist -` - -var AccountTypeGetLinkTargetFunctionType = NewSimpleFunctionType( - FunctionPurityView, - []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: CapabilityPathTypeAnnotation, - }, - }, - NewTypeAnnotation( - &OptionalType{ - Type: PathType, - }, - ), -) - -// AuthAccountKeysType represents the keys associated with an auth account. -var AuthAccountKeysType = func() *CompositeType { - - accountKeys := &CompositeType{ - Identifier: AccountKeysTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeAddFunctionName, - AuthAccountKeysTypeAddFunctionType, - authAccountKeysTypeAddFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeGetFunctionName, - AccountKeysTypeGetFunctionType, - accountKeysTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeRevokeFunctionName, - AuthAccountKeysTypeRevokeFunctionType, - authAccountKeysTypeRevokeFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeForEachFunctionName, - AccountKeysTypeForEachFunctionType, - accountKeysTypeForEachFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - accountKeys, - AccountKeysTypeCountFieldName, - AccountKeysTypeCountFieldType, - accountKeysTypeCountFieldDocString, - ), - } - - accountKeys.Members = GetMembersAsMap(members) - accountKeys.Fields = GetFieldNames(members) - return accountKeys -}() - -var AuthAccountKeysTypeAddFunctionType = NewSimpleFunctionType( - FunctionPurityImpure, - []Parameter{ - { - Identifier: AccountKeyPublicKeyFieldName, - TypeAnnotation: PublicKeyTypeAnnotation, - }, - { - Identifier: AccountKeyHashAlgoFieldName, - TypeAnnotation: HashAlgorithmTypeAnnotation, - }, - { - Identifier: AccountKeyWeightFieldName, - TypeAnnotation: UFix64TypeAnnotation, - }, - }, - AccountKeyTypeAnnotation, -) - -var AccountKeysTypeGetFunctionType = NewSimpleFunctionType( - FunctionPurityView, - []Parameter{ - { - Identifier: AccountKeyKeyIndexFieldName, - TypeAnnotation: IntTypeAnnotation, - }, - }, - NewTypeAnnotation(&OptionalType{Type: AccountKeyType}), -) - -// fun keys.forEach(_ function: fun(AccountKey): Bool): Void -var AccountKeysTypeForEachFunctionType = func() *FunctionType { - const functionPurity = FunctionPurityImpure - - // fun(AccountKey): Bool - iterFunctionType := NewSimpleFunctionType( - functionPurity, - []Parameter{ - { - TypeAnnotation: AccountKeyTypeAnnotation, - }, - }, - BoolTypeAnnotation, - ) - - return NewSimpleFunctionType( - functionPurity, - []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "function", - TypeAnnotation: NewTypeAnnotation(iterFunctionType), - }, - }, - VoidTypeAnnotation, - ) -}() - -var AccountKeysTypeCountFieldType = UInt64Type - -var AuthAccountKeysTypeRevokeFunctionType = NewSimpleFunctionType( - FunctionPurityImpure, - []Parameter{ - { - Identifier: AccountKeyKeyIndexFieldName, - TypeAnnotation: IntTypeAnnotation, - }, - }, - NewTypeAnnotation(&OptionalType{Type: AccountKeyType}), -) - -func init() { - // Set the container type after initializing the AccountKeysTypes, to avoid initializing loop. - AuthAccountKeysType.SetContainerType(AuthAccountType) -} - -const AccountKeysTypeName = "Keys" -const AccountKeysTypeAddFunctionName = "add" -const AccountKeysTypeGetFunctionName = "get" -const AccountKeysTypeForEachFunctionName = "forEach" -const AccountKeysTypeRevokeFunctionName = "revoke" -const AccountKeysTypeCountFieldName = "count" - -const accountTypeGetLinkTargetFunctionDocString = ` -Returns the target path of the capability at the given public or private path, or nil if there exists no capability at the given path. -` - -const accountTypeAddressFieldDocString = ` -The address of the account -` - -const accountTypeContractsFieldDocString = ` -The contracts of the account -` - -const accountTypeAccountBalanceFieldDocString = ` -The FLOW balance of the default vault of this account -` - -const accountTypeAccountAvailableBalanceFieldDocString = ` -The FLOW balance of the default vault of this account that is available to be moved -` - -const accountTypeStorageUsedFieldDocString = ` -The current amount of storage used by the account in bytes -` - -const accountTypeStorageCapacityFieldDocString = ` -The storage capacity of the account in bytes -` - -const accountTypeKeysFieldDocString = ` -The keys associated with the account -` - -const authAccountKeysTypeAddFunctionDocString = ` -Adds the given key to the keys list of the account. -` - -const accountKeysTypeGetFunctionDocString = ` -Retrieves the key at the given index of the account. -` - -const authAccountKeysTypeRevokeFunctionDocString = ` -Revokes the key at the given index of the account. -` -const accountKeysTypeForEachFunctionDocString = ` -Iterates through all the keys of this account, passing each key to the provided function and short-circuiting if the function returns false. - -The order of iteration is undefined. -` - -const accountKeysTypeCountFieldDocString = ` -The number of keys associated with this account. -` - -const authAccountTypeInboxPublishFunctionDocString = ` -Publishes the argument value under the given name, to be later claimed by the specified recipient -` - -var AuthAccountTypeInboxPublishFunctionType = NewSimpleFunctionType( - FunctionPurityImpure, - []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "value", - TypeAnnotation: NewTypeAnnotation(&CapabilityType{}), - }, - { - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - { - Identifier: "recipient", - TypeAnnotation: AddressTypeAnnotation, - }, - }, - VoidTypeAnnotation, -) - -const authAccountTypeInboxUnpublishFunctionDocString = ` -Unpublishes the value specified by the argument string -` - -var AuthAccountTypeInboxUnpublishFunctionType = func() *FunctionType { - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - }, - } - return &FunctionType{ - Purity: FunctionPurityImpure, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - }, - ), - } -}() - -const authAccountTypeInboxClaimFunctionDocString = ` -Claims the value specified by the argument string from the account specified as the provider -` - -var AuthAccountTypeInboxClaimFunctionType = func() *FunctionType { - typeParameter := &TypeParameter{ - Name: "T", - TypeBound: &ReferenceType{ - Type: AnyType, - }, - } - return &FunctionType{ - Purity: FunctionPurityImpure, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "name", - TypeAnnotation: StringTypeAnnotation, - }, - { - Identifier: "provider", - TypeAnnotation: AddressTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &OptionalType{ - Type: &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - }, - ), - } -}() - -var AuthAccountInboxTypeName = "Inbox" - -var accountInboxDocString = "an inbox for sending and receiving capabilities" - -// AuthAccountInboxType represents the account's inbox. -var AuthAccountInboxType = func() *CompositeType { - - accountInbox := &CompositeType{ - Identifier: AuthAccountInboxTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - accountInbox, - AuthAccountTypeInboxClaimFunctionName, - AuthAccountTypeInboxClaimFunctionType, - authAccountTypeInboxClaimFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountInbox, - AuthAccountTypeInboxPublishFunctionName, - AuthAccountTypeInboxPublishFunctionType, - authAccountTypeInboxPublishFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountInbox, - AuthAccountTypeInboxUnpublishFunctionName, - AuthAccountTypeInboxUnpublishFunctionType, - authAccountTypeInboxUnpublishFunctionDocString, - ), - } - - accountInbox.Members = GetMembersAsMap(members) - accountInbox.Fields = GetFieldNames(members) - return accountInbox -}() diff --git a/runtime/sema/block.gen.go b/runtime/sema/block.gen.go index e4eabfc481..ed587a3007 100644 --- a/runtime/sema/block.gen.go +++ b/runtime/sema/block.gen.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const BlockTypeHeightFieldName = "height" var BlockTypeHeightFieldType = UInt64Type @@ -78,72 +73,35 @@ var BlockType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - BlockTypeHeightFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeHeightFieldType, - BlockTypeHeightFieldDocString, - ) - }, - }, - BlockTypeViewFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeViewFieldType, - BlockTypeViewFieldDocString, - ) - }, - }, - BlockTypeTimestampFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeTimestampFieldType, - BlockTypeTimestampFieldDocString, - ) - }, - }, - BlockTypeIdFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - BlockTypeIdFieldType, - BlockTypeIdFieldDocString, - ) - }, - }, - } - }, +} + +func init() { + BlockType.Members = func(t *SimpleType) map[string]MemberResolver { + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeHeightFieldName, + BlockTypeHeightFieldType, + BlockTypeHeightFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeViewFieldName, + BlockTypeViewFieldType, + BlockTypeViewFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeTimestampFieldName, + BlockTypeTimestampFieldType, + BlockTypeTimestampFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + BlockTypeIdFieldName, + BlockTypeIdFieldType, + BlockTypeIdFieldDocString, + ), + }) + } } diff --git a/runtime/sema/block.go b/runtime/sema/block.go index 73959ba5e3..320f2f9853 100644 --- a/runtime/sema/block.go +++ b/runtime/sema/block.go @@ -18,6 +18,6 @@ package sema -//go:generate go run ./gen/main.go block.cdc block.gen.go +//go:generate go run ./gen block.cdc block.gen.go var BlockTypeAnnotation = NewTypeAnnotation(BlockType) diff --git a/runtime/sema/character.cdc b/runtime/sema/character.cdc new file mode 100644 index 0000000000..4339c7bbde --- /dev/null +++ b/runtime/sema/character.cdc @@ -0,0 +1,6 @@ + +pub struct Character: Storable, Equatable, Exportable, Importable { + + /// Returns this character as a String + pub fun toString(): String +} diff --git a/runtime/sema/character_type.go b/runtime/sema/character.gen.go similarity index 53% rename from runtime/sema/character_type.go rename to runtime/sema/character.gen.go index f46bd021c3..b7ea58dce6 100644 --- a/runtime/sema/character_type.go +++ b/runtime/sema/character.gen.go @@ -1,3 +1,4 @@ +// Code generated from character.cdc. DO NOT EDIT. /* * Cadence - The resource-oriented smart contract programming language * @@ -18,18 +19,24 @@ package sema -import ( - "github.com/rivo/uniseg" +const CharacterTypeToStringFunctionName = "toString" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) +var CharacterTypeToStringFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + StringType, + ), +} + +const CharacterTypeToStringFunctionDocString = ` +Returns this character as a String +` + +const CharacterTypeName = "Character" -// CharacterType represents the character type var CharacterType = &SimpleType{ - Name: "Character", - QualifiedName: "Character", - TypeID: "Character", + Name: CharacterTypeName, + QualifiedName: CharacterTypeName, + TypeID: CharacterTypeName, tag: CharacterTypeTag, IsResource: false, Storable: true, @@ -38,27 +45,15 @@ var CharacterType = &SimpleType{ Importable: true, } -func IsValidCharacter(s string) bool { - graphemes := uniseg.NewGraphemes(s) - // a valid character must have exactly one grapheme cluster - return graphemes.Next() && !graphemes.Next() -} - func init() { CharacterType.Members = func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - ToStringFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - ToStringFunctionType, - toStringFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + CharacterTypeToStringFunctionName, + CharacterTypeToStringFunctionType, + CharacterTypeToStringFunctionDocString, + ), + }) } } diff --git a/runtime/sema/character.go b/runtime/sema/character.go new file mode 100644 index 0000000000..7e8475cb25 --- /dev/null +++ b/runtime/sema/character.go @@ -0,0 +1,29 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +//go:generate go run ./gen character.cdc character.gen.go + +import "github.com/rivo/uniseg" + +func IsValidCharacter(s string) bool { + graphemes := uniseg.NewGraphemes(s) + // a valid character must have exactly one grapheme cluster + return graphemes.Next() && !graphemes.Next() +} diff --git a/runtime/sema/check_function.go b/runtime/sema/check_function.go index 14896bbc8a..bfd4a8ef04 100644 --- a/runtime/sema/check_function.go +++ b/runtime/sema/check_function.go @@ -352,8 +352,18 @@ func (checker *Checker) visitWithPostConditions(postConditions *ast.Conditions, if returnType != VoidType { var resultType Type if returnType.IsResourceType() { - resultType = &ReferenceType{ - Type: returnType, + optType, isOptional := returnType.(*OptionalType) + if isOptional { + // If the return type is an optional type T?, then create an optional reference (&T)?. + resultType = &OptionalType{ + Type: &ReferenceType{ + Type: optType.Type, + }, + } + } else { + resultType = &ReferenceType{ + Type: returnType, + } } } else { resultType = returnType diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index fff11b0835..8272174764 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -78,12 +78,14 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere referencedExpression := referenceExpression.Expression - _ = checker.VisitExpression(referencedExpression, targetType) + referencedType := checker.VisitExpression(referencedExpression, targetType) if referenceType == nil { return InvalidType } + checker.checkUnusedExpressionResourceLoss(referencedType, referencedExpression) + checker.Elaboration.SetReferenceExpressionBorrowType(referenceExpression, returnType) return returnType diff --git a/runtime/sema/crypto_algorithm_types.go b/runtime/sema/crypto_algorithm_types.go index 973054745e..a061e71e2c 100644 --- a/runtime/sema/crypto_algorithm_types.go +++ b/runtime/sema/crypto_algorithm_types.go @@ -303,8 +303,8 @@ func newNativeEnumType( ), ) - ty.Members = GetMembersAsMap(members) - ty.Fields = GetFieldNames(members) + ty.Members = MembersAsMap(members) + ty.Fields = MembersFieldNames(members) return ty } diff --git a/runtime/sema/deployed_contract.go b/runtime/sema/deployed_contract.go deleted file mode 100644 index 4a59e0cf10..0000000000 --- a/runtime/sema/deployed_contract.go +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - -// DeployedContractType represents the type `DeployedContract` -var DeployedContractType = &SimpleType{ - Name: "DeployedContract", - QualifiedName: "DeployedContract", - TypeID: "DeployedContract", - tag: DeployedContractTypeTag, - IsResource: false, - Storable: false, - Equatable: false, - Exportable: false, - Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - DeployedContractTypeAddressFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TheAddressType, - deployedContractTypeAddressFieldDocString, - ) - }, - }, - DeployedContractTypeNameFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StringType, - deployedContractTypeNameFieldDocString, - ) - }, - }, - DeployedContractTypeCodeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - ByteArrayType, - deployedContractTypeCodeFieldDocString, - ) - }, - }, - DeployedContractTypePublicTypesFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, report func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DeployedContractTypePublicTypesFunctionType, - DeployedContractTypePublicTypesFunctionDocString, - ) - }, - }, - } - }, -} - -var DeployedContractTypeAnnotation = NewTypeAnnotation(DeployedContractType) - -const DeployedContractTypeAddressFieldName = "address" - -const deployedContractTypeAddressFieldDocString = ` -The address of the account where the contract is deployed at -` - -const DeployedContractTypeNameFieldName = "name" - -const deployedContractTypeNameFieldDocString = ` -The name of the contract -` - -const DeployedContractTypeCodeFieldName = "code" - -const deployedContractTypeCodeFieldDocString = ` -The code of the contract -` - -const DeployedContractTypePublicTypesFunctionName = "publicTypes" -const DeployedContractTypePublicTypesFunctionDocString = ` -Returns an array of Type objects representing all public type declarations in this contract. -` - -var DeployedContractTypePublicTypesFunctionType = &FunctionType{ - ReturnTypeAnnotation: NewTypeAnnotation( - &VariableSizedType{ - Type: MetaType, - }, - ), -} diff --git a/runtime/sema/deployedcontract.cdc b/runtime/sema/deployedcontract.cdc new file mode 100644 index 0000000000..ba9553c197 --- /dev/null +++ b/runtime/sema/deployedcontract.cdc @@ -0,0 +1,24 @@ + +pub struct DeployedContract { + /// The address of the account where the contract is deployed at. + pub let address: Address + + /// The name of the contract. + pub let name: String + + /// The code of the contract. + pub let code: [UInt8] + + /// Returns an array of `Type` objects representing all the public type declarations in this contract + /// (e.g. structs, resources, enums). + /// + /// For example, given a contract + /// ``` + /// contract Foo { + /// pub struct Bar {...} + /// pub resource Qux {...} + /// } + /// ``` + /// then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` + pub fun publicTypes(): [Type] +} diff --git a/runtime/sema/deployedcontract.gen.go b/runtime/sema/deployedcontract.gen.go new file mode 100644 index 0000000000..24ea7d79b9 --- /dev/null +++ b/runtime/sema/deployedcontract.gen.go @@ -0,0 +1,115 @@ +// Code generated from deployedcontract.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +const DeployedContractTypeAddressFieldName = "address" + +var DeployedContractTypeAddressFieldType = TheAddressType + +const DeployedContractTypeAddressFieldDocString = ` +The address of the account where the contract is deployed at. +` + +const DeployedContractTypeNameFieldName = "name" + +var DeployedContractTypeNameFieldType = StringType + +const DeployedContractTypeNameFieldDocString = ` +The name of the contract. +` + +const DeployedContractTypeCodeFieldName = "code" + +var DeployedContractTypeCodeFieldType = &VariableSizedType{ + Type: UInt8Type, +} + +const DeployedContractTypeCodeFieldDocString = ` +The code of the contract. +` + +const DeployedContractTypePublicTypesFunctionName = "publicTypes" + +var DeployedContractTypePublicTypesFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + &VariableSizedType{ + Type: MetaType, + }, + ), +} + +const DeployedContractTypePublicTypesFunctionDocString = ` +Returns an array of ` + "`Type`" + ` objects representing all the public type declarations in this contract +(e.g. structs, resources, enums). + +For example, given a contract +` + ` +contract Foo { +pub struct Bar {...} +pub resource Qux {...} +} +` + ` +then ` + "`.publicTypes()`" + ` will return an array equivalent to the expression ` + "`[Type(), Type()]`" + ` +` + +const DeployedContractTypeName = "DeployedContract" + +var DeployedContractType = &SimpleType{ + Name: DeployedContractTypeName, + QualifiedName: DeployedContractTypeName, + TypeID: DeployedContractTypeName, + tag: DeployedContractTypeTag, + IsResource: false, + Storable: false, + Equatable: false, + Exportable: false, + Importable: false, +} + +func init() { + DeployedContractType.Members = func(t *SimpleType) map[string]MemberResolver { + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + DeployedContractTypeAddressFieldName, + DeployedContractTypeAddressFieldType, + DeployedContractTypeAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DeployedContractTypeNameFieldName, + DeployedContractTypeNameFieldType, + DeployedContractTypeNameFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DeployedContractTypeCodeFieldName, + DeployedContractTypeCodeFieldType, + DeployedContractTypeCodeFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DeployedContractTypePublicTypesFunctionName, + DeployedContractTypePublicTypesFunctionType, + DeployedContractTypePublicTypesFunctionDocString, + ), + }) + } +} diff --git a/runtime/sema/deployedcontract.go b/runtime/sema/deployedcontract.go new file mode 100644 index 0000000000..2581d4298f --- /dev/null +++ b/runtime/sema/deployedcontract.go @@ -0,0 +1,21 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +//go:generate go run ./gen deployedcontract.cdc deployedcontract.gen.go diff --git a/runtime/sema/gen/main.go b/runtime/sema/gen/main.go index fb6a932eca..543613d887 100644 --- a/runtime/sema/gen/main.go +++ b/runtime/sema/gen/main.go @@ -30,13 +30,12 @@ import ( "github.com/dave/dst/decorator" "github.com/dave/dst/decorator/resolver/guess" + "github.com/dave/dst" + "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/parser" "github.com/onflow/cadence/runtime/pretty" - "github.com/onflow/cadence/runtime/sema" - - "github.com/dave/dst" ) const headerTemplate = `// Code generated from {{ . }}. DO NOT EDIT. @@ -143,17 +142,27 @@ func renderDocString(s string) dst.Expr { return result } +type typeDecl struct { + typeName string + fullTypeName string + compositeKind common.CompositeKind + storable bool + equatable bool + exportable bool + importable bool + memberDeclarations []ast.Declaration + nestedTypes []*typeDecl +} + type generator struct { - containerTypeNames []string - decls []dst.Decl + typeStack []*typeDecl + decls []dst.Decl } var _ ast.DeclarationVisitor[struct{}] = &generator{} -func (g *generator) addDecls(decls ...*dst.GenDecl) { - for _, decl := range decls { - g.decls = append(g.decls, decl) - } +func (g *generator) addDecls(decls ...dst.Decl) { + g.decls = append(g.decls, decls...) } func (*generator) VisitVariableDeclaration(_ *ast.VariableDeclaration) struct{} { @@ -161,7 +170,7 @@ func (*generator) VisitVariableDeclaration(_ *ast.VariableDeclaration) struct{} } func (g *generator) VisitFunctionDeclaration(decl *ast.FunctionDeclaration) (_ struct{}) { - if len(g.containerTypeNames) == 0 { + if len(g.typeStack) == 0 { panic("global function declarations are not supported") } @@ -170,7 +179,7 @@ func (g *generator) VisitFunctionDeclaration(decl *ast.FunctionDeclaration) (_ s } functionName := decl.Identifier.Identifier - fullTypeName := g.fullTypeName() + fullTypeName := g.currentFullTypeName() g.addFunctionNameDeclaration(fullTypeName, functionName) @@ -263,6 +272,7 @@ func (g *generator) addFunctionTypeDeclaration( functionTypeVarName(fullTypeName, functionName), functionTypeExpr( &ast.FunctionType{ + PurityAnnotation: decl.Purity, ReturnTypeAnnotation: decl.ReturnTypeAnnotation, ParameterTypeAnnotations: parameterTypeAnnotations, }, @@ -296,7 +306,7 @@ func (g *generator) declarationDocString(decl ast.Declaration) dst.Expr { if len(docString) == 0 { panic(fmt.Errorf( "missing doc string for %s", - g.memberID(identifier), + g.currentMemberID(identifier), )) } @@ -308,75 +318,226 @@ func (*generator) VisitSpecialFunctionDeclaration(_ *ast.SpecialFunctionDeclarat } func (g *generator) VisitCompositeDeclaration(decl *ast.CompositeDeclaration) (_ struct{}) { - var isResource bool compositeKind := decl.CompositeKind switch compositeKind { - case common.CompositeKindStructure: + case common.CompositeKindStructure, + common.CompositeKindResource: break - case common.CompositeKindResource: - isResource = true default: panic(fmt.Sprintf("%s declarations are not supported", compositeKind.Name())) } typeName := decl.Identifier.Identifier - g.containerTypeNames = append(g.containerTypeNames, typeName) + typeDecl := &typeDecl{ + typeName: typeName, + fullTypeName: g.newFullTypeName(typeName), + compositeKind: compositeKind, + } + + if len(g.typeStack) > 0 { + parentType := g.typeStack[len(g.typeStack)-1] + parentType.nestedTypes = append( + parentType.nestedTypes, + typeDecl, + ) + } + + g.typeStack = append( + g.typeStack, + typeDecl, + ) defer func() { - g.containerTypeNames = g.containerTypeNames[:len(g.containerTypeNames)-1] + // Pop + lastIndex := len(g.typeStack) - 1 + g.typeStack[lastIndex] = nil + g.typeStack = g.typeStack[:lastIndex] }() - var memberDeclarations []ast.Declaration + // We can generate a SimpleType declaration, + // if this is a top-level type, + // and this declaration has no nested type declarations. + // Otherwise, we have to generate a CompositeType + + canGenerateSimpleType := len(g.typeStack) == 1 for _, memberDeclaration := range decl.Members.Declarations() { ast.AcceptDeclaration[struct{}](memberDeclaration, g) // Visiting unsupported declarations panics, // so only supported member declarations are added - memberDeclarations = append(memberDeclarations, memberDeclaration) - } + typeDecl.memberDeclarations = append( + typeDecl.memberDeclarations, + memberDeclaration, + ) - var ( - equatable, - storable, - exportable, - importable bool - ) + switch memberDeclaration.(type) { + case *ast.FieldDeclaration, + *ast.FunctionDeclaration: + break + + default: + canGenerateSimpleType = false + } + } for _, conformance := range decl.Conformances { switch conformance.Identifier.Identifier { case "Storable": - storable = true + if !canGenerateSimpleType { + panic(fmt.Errorf( + "composite types cannot be explicitly marked as storable: %s", + g.currentTypeID(), + )) + } + typeDecl.storable = true + case "Equatable": - equatable = true + if !canGenerateSimpleType { + panic(fmt.Errorf( + "composite types cannot be explicitly marked as equatable: %s", + g.currentTypeID(), + )) + } + typeDecl.equatable = true + case "Exportable": - exportable = true + if !canGenerateSimpleType { + panic(fmt.Errorf( + "composite types cannot be explicitly marked as exportable: %s", + g.currentTypeID(), + )) + } + typeDecl.exportable = true + case "Importable": - importable = true + typeDecl.importable = true } } + var typeVarDecl dst.Expr + if canGenerateSimpleType { + typeVarDecl = simpleTypeLiteral(typeDecl) + } else { + typeVarDecl = compositeTypeExpr(typeDecl) + } + + tyVarName := typeVarName(typeDecl.fullTypeName) + g.addDecls( goConstDecl( - typeNameVarName(typeName), + typeNameVarName(typeDecl.fullTypeName), goStringLit(typeName), ), goVarDecl( - fmt.Sprintf("%sType", typeName), - simpleTypeLiteral(simpleType{ - typeName: typeName, - fullTypeName: g.fullTypeName(), - isResource: isResource, - Storable: storable, - Equatable: equatable, - Exportable: exportable, - Importable: importable, - memberDeclarations: memberDeclarations, - }), + tyVarName, + typeVarDecl, ), ) + memberDeclarations := typeDecl.memberDeclarations + + if len(memberDeclarations) > 0 { + + if canGenerateSimpleType { + + // func init() { + // t.Members = func(t *SimpleType) map[string]MemberResolver { + // return MembersAsResolvers(...) + // } + // } + + memberResolversFunc := simpleTypeMemberResolversFunc(typeDecl.fullTypeName, memberDeclarations) + + g.addDecls( + &dst.FuncDecl{ + Name: dst.NewIdent("init"), + Type: &dst.FuncType{}, + Body: &dst.BlockStmt{ + List: []dst.Stmt{ + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Members"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + memberResolversFunc, + }, + }, + }, + }, + }, + ) + + } else { + + // func init() { + // members := []*Member{...} + // t.Members = MembersAsMap(members) + // t.Fields = MembersFieldNames(members) + // } + + members := membersExpr(typeDecl.fullTypeName, tyVarName, memberDeclarations) + + const membersVariableIdentifier = "members" + + g.addDecls( + &dst.FuncDecl{ + Name: dst.NewIdent("init"), + Type: &dst.FuncType{}, + Body: &dst.BlockStmt{ + List: []dst.Stmt{ + &dst.DeclStmt{ + Decl: goVarDecl( + membersVariableIdentifier, + members, + ), + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Members"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsMap"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, + }, + }, + &dst.AssignStmt{ + Lhs: []dst.Expr{ + &dst.SelectorExpr{ + X: dst.NewIdent(tyVarName), + Sel: dst.NewIdent("Fields"), + }, + }, + Tok: token.ASSIGN, + Rhs: []dst.Expr{ + &dst.CallExpr{ + Fun: dst.NewIdent("MembersFieldNames"), + Args: []dst.Expr{ + dst.NewIdent(membersVariableIdentifier), + }, + }, + }, + }, + }, + }, + }, + ) + } + } + return } @@ -394,7 +555,7 @@ func (*generator) VisitTransactionDeclaration(_ *ast.TransactionDeclaration) str func (g *generator) VisitFieldDeclaration(decl *ast.FieldDeclaration) (_ struct{}) { fieldName := decl.Identifier.Identifier - fullTypeName := g.fullTypeName() + fullTypeName := g.currentFullTypeName() docString := g.declarationDocString(decl) g.addDecls( @@ -415,6 +576,10 @@ func (g *generator) VisitFieldDeclaration(decl *ast.FieldDeclaration) (_ struct{ return } +func (g *generator) currentFullTypeName() string { + return g.typeStack[len(g.typeStack)-1].fullTypeName +} + func typeExpr(t ast.Type, typeParams map[string]string) dst.Expr { switch t := t.(type) { case *ast.NominalType: @@ -447,6 +612,10 @@ func typeExpr(t ast.Type, typeParams map[string]string) dst.Expr { Type: dst.NewIdent("CapabilityType"), }, } + default: + for _, nestedIdentifier := range t.NestedIdentifiers { + identifier += nestedIdentifier.Identifier + } } return typeVarIdent(identifier) @@ -540,6 +709,13 @@ func functionTypeExpr( typeParams map[string]string, ) dst.Expr { + // Function purity + + var purityExpr dst.Expr + if t.PurityAnnotation == ast.FunctionPurityView { + purityExpr = dst.NewIdent("FunctionPurityView") + } + // Type parameters var typeParameterTypeAnnotations []*ast.TypeParameter @@ -595,9 +771,11 @@ func functionTypeExpr( if parameter.Label != "" { var lit dst.Expr - if parameter.Label == sema.ArgumentLabelNotRequired { + // NOTE: avoid import of sema (ArgumentLabelNotRequired), + // so sema can be in a non-buildable state + // and code generation will still succeed + if parameter.Label == "_" { lit = &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/sema", Name: "ArgumentLabelNotRequired", } } else { @@ -661,6 +839,16 @@ func functionTypeExpr( var compositeElements []dst.Expr + if purityExpr != nil { + compositeElements = append( + compositeElements, + goKeyValue( + "Purity", + purityExpr, + ), + ) + } + if typeParametersExpr != nil { compositeElements = append( compositeElements, @@ -710,15 +898,36 @@ func (*generator) VisitImportDeclaration(_ *ast.ImportDeclaration) struct{} { panic("import declarations are not supported") } -func (g *generator) fullTypeName() string { - return strings.Join(g.containerTypeNames, "") +func (g *generator) newFullTypeName(typeName string) string { + if len(g.typeStack) == 0 { + return typeName + } + parentFullTypeName := g.typeStack[len(g.typeStack)-1].fullTypeName + return parentFullTypeName + typeName } -func (g *generator) memberID(fieldName string) string { - return fmt.Sprintf("%s.%s", - strings.Join(g.containerTypeNames, "."), - fieldName, - ) +func (g *generator) currentTypeID() string { + var b strings.Builder + for i := range g.typeStack { + if i > 0 { + b.WriteByte('.') + } + b.WriteString(g.typeStack[i].typeName) + } + return b.String() +} + +func (g *generator) currentMemberID(memberName string) string { + var b strings.Builder + for i := range g.typeStack { + if i > 0 { + b.WriteByte('.') + } + b.WriteString(g.typeStack[i].typeName) + } + b.WriteByte('.') + b.WriteString(memberName) + return b.String() } func goField(name string, ty dst.Expr) *dst.Field { @@ -791,10 +1000,10 @@ func goBoolLit(b bool) dst.Expr { return dst.NewIdent(strconv.FormatBool(false)) } -func declarationKindExpr(kind string) *dst.Ident { +func compositeKindExpr(compositeKind common.CompositeKind) *dst.Ident { return &dst.Ident{ Path: "github.com/onflow/cadence/runtime/common", - Name: fmt.Sprintf("DeclarationKind%s", kind), + Name: compositeKind.String(), } } @@ -856,37 +1065,31 @@ func functionDocStringVarName(fullTypeName, functionName string) string { return memberVarName(fullTypeName, functionName, "Function", "DocString") } -type simpleType struct { - typeName string - fullTypeName string - isResource bool - Storable bool - Equatable bool - Exportable bool - Importable bool - memberDeclarations []ast.Declaration -} - -func simpleTypeLiteral(ty simpleType) dst.Expr { +func simpleTypeLiteral(ty *typeDecl) dst.Expr { + + // &SimpleType{ + // Name: TestTypeName, + // QualifiedName: TestTypeName, + // TypeID: TestTypeName, + // tag: TestTypeTag, + // IsResource: true, + // Storable: false, + // Equatable: false, + // Exportable: false, + // Importable: false, + //} + + isResource := ty.compositeKind == common.CompositeKindResource elements := []dst.Expr{ - goKeyValue("Name", typeNameVarIdent(ty.typeName)), - goKeyValue("QualifiedName", typeNameVarIdent(ty.typeName)), - goKeyValue("TypeID", typeNameVarIdent(ty.typeName)), - goKeyValue("tag", typeTagVarIdent(ty.typeName)), - goKeyValue("IsResource", goBoolLit(ty.isResource)), - goKeyValue("Storable", goBoolLit(ty.Storable)), - goKeyValue("Equatable", goBoolLit(ty.Equatable)), - goKeyValue("Exportable", goBoolLit(ty.Exportable)), - goKeyValue("Importable", goBoolLit(ty.Importable)), - } - - if len(ty.memberDeclarations) > 0 { - members := simpleTypeMembers(ty.fullTypeName, ty.memberDeclarations) - - elements = append( - elements, - goKeyValue("Members", members), - ) + goKeyValue("Name", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("QualifiedName", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("TypeID", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("tag", typeTagVarIdent(ty.fullTypeName)), + goKeyValue("IsResource", goBoolLit(isResource)), + goKeyValue("Storable", goBoolLit(ty.storable)), + goKeyValue("Equatable", goBoolLit(ty.equatable)), + goKeyValue("Exportable", goBoolLit(ty.exportable)), + goKeyValue("Importable", goBoolLit(ty.importable)), } return &dst.UnaryExpr{ @@ -898,67 +1101,20 @@ func simpleTypeLiteral(ty simpleType) dst.Expr { } } -func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst.Expr { - - elements := make([]dst.Expr, 0, len(declarations)) - - for _, declaration := range declarations { - resolve := simpleTypeMemberResolver(fullTypeName, declaration) - - var memberName string - var kind dst.Expr - - declarationKind := declaration.DeclarationKind() - - memberName = declaration.DeclarationIdentifier().Identifier - - switch declarationKind { - case common.DeclarationKindField: - memberName = fieldNameVarName( - fullTypeName, - memberName, - ) - kind = declarationKindExpr("Field") - - case common.DeclarationKindFunction: - memberName = functionNameVarName( - fullTypeName, - memberName, - ) - kind = declarationKindExpr("Function") - - default: - panic(fmt.Errorf( - "%s members are not supported", - declarationKind.Name(), - )) - } - - elements = append( - elements, - goKeyValue( - memberName, - &dst.CompositeLit{ - Elts: []dst.Expr{ - goKeyValue("Kind", kind), - goKeyValue("Resolve", resolve), - }, - }, - ), - ) - } - +func simpleTypeMemberResolversFunc(fullTypeName string, declarations []ast.Declaration) dst.Expr { // func(t *SimpleType) map[string]MemberResolver { - // return map[string]MemberResolver{ - // ... - // } + // return MembersAsResolvers(...) // } + const typeVarName = "t" + returnStatement := &dst.ReturnStmt{ Results: []dst.Expr{ - &dst.CompositeLit{ - Type: stringMemberResolverMapType(), - Elts: elements, + &dst.CallExpr{ + Fun: dst.NewIdent("MembersAsResolvers"), + Args: []dst.Expr{ + membersExpr(fullTypeName, typeVarName, declarations), + }, }, }, } @@ -970,7 +1126,7 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. Func: true, Params: &dst.FieldList{ List: []*dst.Field{ - goField("t", &dst.StarExpr{X: dst.NewIdent("SimpleType")}), + goField(typeVarName, simpleType()), }, }, Results: &dst.FieldList{ @@ -989,78 +1145,83 @@ func simpleTypeMembers(fullTypeName string, declarations []ast.Declaration) dst. } } -func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) dst.Expr { +func membersExpr( + fullTypeName string, + typeVarName string, + memberDeclarations []ast.Declaration, +) dst.Expr { - // func( - // memoryGauge common.MemoryGauge, - // identifier string, - // targetRange ast.Range, - // report func(error), - // ) *Member + // []*Member{ + // ... + // } - parameters := []*dst.Field{ - goField( - "memoryGauge", - &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/common", - Name: "MemoryGauge", - }, - ), - goField("identifier", dst.NewIdent("string")), - goField( - "targetRange", - &dst.Ident{ - Path: "github.com/onflow/cadence/runtime/ast", - Name: "Range", - }, - ), - goField( - "report", - &dst.FuncType{ - Params: &dst.FieldList{ - List: []*dst.Field{ - {Type: dst.NewIdent("error")}, - }, - }, - }, - ), - } + elements := make([]dst.Expr, 0, len(memberDeclarations)) - // TODO: bug: does not add newline before first and after last. - // Neither does setting decorations on the parameter field list - // or the function type work. Likely a problem in dst. - for _, parameter := range parameters { - parameter.Decorations().Before = dst.NewLine - parameter.Decorations().After = dst.NewLine + for _, declaration := range memberDeclarations { + var memberVarName string + memberName := declaration.DeclarationIdentifier().Identifier + + declarationKind := declaration.DeclarationKind() + switch declarationKind { + case common.DeclarationKindField: + memberVarName = fieldNameVarName( + fullTypeName, + memberName, + ) + + case common.DeclarationKindFunction: + memberVarName = functionNameVarName( + fullTypeName, + memberName, + ) + + case common.DeclarationKindStructureInterface, + common.DeclarationKindStructure, + common.DeclarationKindResource, + common.DeclarationKindResourceInterface: + + continue + + default: + panic(fmt.Errorf( + "%s members are not supported", + declarationKind.Name(), + )) + } + + element := newDeclarationMember(fullTypeName, typeVarName, memberVarName, declaration) + element.Decorations().Before = dst.NewLine + element.Decorations().After = dst.NewLine + + elements = append(elements, element) } - functionType := &dst.FuncType{ - Func: true, - Params: &dst.FieldList{ - List: parameters, - }, - Results: &dst.FieldList{ - List: []*dst.Field{ - { - Type: &dst.StarExpr{ - X: dst.NewIdent("Member"), - }, - }, - }, + return &dst.CompositeLit{ + Type: &dst.ArrayType{ + Elt: &dst.StarExpr{X: dst.NewIdent("Member")}, }, + Elts: elements, } +} +func simpleType() *dst.StarExpr { + return &dst.StarExpr{X: dst.NewIdent("SimpleType")} +} + +func newDeclarationMember( + fullTypeName string, + containerTypeVariableIdentifier string, + memberNameVariableIdentifier string, + declaration ast.Declaration, +) dst.Expr { declarationKind := declaration.DeclarationKind() declarationName := declaration.DeclarationIdentifier().Identifier - var result dst.Expr - switch declarationKind { case common.DeclarationKindField: args := []dst.Expr{ - dst.NewIdent("memoryGauge"), - dst.NewIdent("t"), - dst.NewIdent("identifier"), + dst.NewIdent(containerTypeVariableIdentifier), + dst.NewIdent(memberNameVariableIdentifier), dst.NewIdent(fieldTypeVarName(fullTypeName, declarationName)), dst.NewIdent(fieldDocStringVarName(fullTypeName, declarationName)), } @@ -1071,16 +1232,15 @@ func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) } // TODO: add support for var - result = &dst.CallExpr{ - Fun: dst.NewIdent("NewPublicConstantFieldMember"), + return &dst.CallExpr{ + Fun: dst.NewIdent("NewUnmeteredPublicConstantFieldMember"), Args: args, } case common.DeclarationKindFunction: args := []dst.Expr{ - dst.NewIdent("memoryGauge"), - dst.NewIdent("t"), - dst.NewIdent("identifier"), + dst.NewIdent(containerTypeVariableIdentifier), + dst.NewIdent(memberNameVariableIdentifier), dst.NewIdent(functionTypeVarName(fullTypeName, declarationName)), dst.NewIdent(functionDocStringVarName(fullTypeName, declarationName)), } @@ -1090,8 +1250,8 @@ func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) arg.Decorations().After = dst.NewLine } - result = &dst.CallExpr{ - Fun: dst.NewIdent("NewPublicFunctionMember"), + return &dst.CallExpr{ + Fun: dst.NewIdent("NewUnmeteredPublicFunctionMember"), Args: args, } @@ -1102,27 +1262,104 @@ func simpleTypeMemberResolver(fullTypeName string, declaration ast.Declaration) )) } - returnStatement := &dst.ReturnStmt{ - Results: []dst.Expr{ - result, +} + +func stringMemberResolverMapType() *dst.MapType { + return &dst.MapType{ + Key: dst.NewIdent("string"), + Value: dst.NewIdent("MemberResolver"), + } +} + +func compositeTypeExpr(ty *typeDecl) dst.Expr { + + // func() *CompositeType { + // var t = &CompositeType{ + // Identifier: FooTypeName, + // Kind: common.CompositeKindStructure, + // importable: false, + // hasComputedMembers: true, + // } + // + // t.SetNestedType(FooBarTypeName, FooBarType) + // return t + // }() + + const typeVarName = "t" + + statements := []dst.Stmt{ + &dst.DeclStmt{ + Decl: goVarDecl( + typeVarName, + compositeTypeLiteral(ty), + ), }, } - returnStatement.Decorations().Before = dst.EmptyLine - return &dst.FuncLit{ - Type: functionType, - Body: &dst.BlockStmt{ - List: []dst.Stmt{ - returnStatement, + for _, nestedType := range ty.nestedTypes { + statements = append( + statements, + &dst.ExprStmt{ + X: &dst.CallExpr{ + Fun: &dst.SelectorExpr{ + X: dst.NewIdent(typeVarName), + Sel: dst.NewIdent("SetNestedType"), + }, + Args: []dst.Expr{ + typeNameVarIdent(nestedType.fullTypeName), + typeVarIdent(nestedType.fullTypeName), + }, + }, + }, + ) + } + + statements = append( + statements, + &dst.ReturnStmt{ + Results: []dst.Expr{ + dst.NewIdent(typeVarName), + }, + }, + ) + + return &dst.CallExpr{ + Fun: &dst.FuncLit{ + Type: &dst.FuncType{ + Func: true, + Results: &dst.FieldList{ + List: []*dst.Field{ + { + Type: &dst.StarExpr{ + X: dst.NewIdent("CompositeType"), + }, + }, + }, + }, + }, + Body: &dst.BlockStmt{ + List: statements, }, }, } } -func stringMemberResolverMapType() *dst.MapType { - return &dst.MapType{ - Key: dst.NewIdent("string"), - Value: dst.NewIdent("MemberResolver"), +func compositeTypeLiteral(ty *typeDecl) dst.Expr { + kind := compositeKindExpr(ty.compositeKind) + + elements := []dst.Expr{ + goKeyValue("Identifier", typeNameVarIdent(ty.fullTypeName)), + goKeyValue("Kind", kind), + goKeyValue("importable", goBoolLit(ty.importable)), + goKeyValue("hasComputedMembers", goBoolLit(true)), + } + + return &dst.UnaryExpr{ + Op: token.AND, + X: &dst.CompositeLit{ + Type: dst.NewIdent("CompositeType"), + Elts: elements, + }, } } diff --git a/runtime/sema/gen/testdata/docstrings.golden.go b/runtime/sema/gen/testdata/docstrings.golden.go index 2459e56e5c..254880bd11 100644 --- a/runtime/sema/gen/testdata/docstrings.golden.go +++ b/runtime/sema/gen/testdata/docstrings.golden.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const DocstringsTypeOwoFieldName = "owo" var DocstringsTypeOwoFieldType = IntType @@ -116,104 +111,47 @@ var DocstringsType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - DocstringsTypeOwoFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - DocstringsTypeOwoFieldType, - DocstringsTypeOwoFieldDocString, - ) - }, - }, - DocstringsTypeUwuFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - DocstringsTypeUwuFieldType, - DocstringsTypeUwuFieldDocString, - ) - }, - }, - DocstringsTypeNwnFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DocstringsTypeNwnFunctionType, - DocstringsTypeNwnFunctionDocString, - ) - }, - }, - DocstringsTypeWithBlanksFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - DocstringsTypeWithBlanksFieldType, - DocstringsTypeWithBlanksFieldDocString, - ) - }, - }, - DocstringsTypeIsSmolBeanFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DocstringsTypeIsSmolBeanFunctionType, - DocstringsTypeIsSmolBeanFunctionDocString, - ) - }, - }, - DocstringsTypeRunningOutOfIdeasFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - DocstringsTypeRunningOutOfIdeasFunctionType, - DocstringsTypeRunningOutOfIdeasFunctionDocString, - ) - }, - }, - } - }, +} + +func init() { + DocstringsType.Members = func(t *SimpleType) map[string]MemberResolver { + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + DocstringsTypeOwoFieldName, + DocstringsTypeOwoFieldType, + DocstringsTypeOwoFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DocstringsTypeUwuFieldName, + DocstringsTypeUwuFieldType, + DocstringsTypeUwuFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DocstringsTypeNwnFunctionName, + DocstringsTypeNwnFunctionType, + DocstringsTypeNwnFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + DocstringsTypeWithBlanksFieldName, + DocstringsTypeWithBlanksFieldType, + DocstringsTypeWithBlanksFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DocstringsTypeIsSmolBeanFunctionName, + DocstringsTypeIsSmolBeanFunctionType, + DocstringsTypeIsSmolBeanFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + DocstringsTypeRunningOutOfIdeasFunctionName, + DocstringsTypeRunningOutOfIdeasFunctionType, + DocstringsTypeRunningOutOfIdeasFunctionDocString, + ), + }) + } } diff --git a/runtime/sema/gen/testdata/fields.golden.go b/runtime/sema/gen/testdata/fields.golden.go index 3e4fa3a02a..a747746953 100644 --- a/runtime/sema/gen/testdata/fields.golden.go +++ b/runtime/sema/gen/testdata/fields.golden.go @@ -19,11 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) - const TestTypeTestIntFieldName = "testInt" var TestTypeTestIntFieldType = UInt64Type @@ -131,168 +126,71 @@ var TestType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - TestTypeTestIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestIntFieldType, - TestTypeTestIntFieldDocString, - ) - }, - }, - TestTypeTestOptIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestOptIntFieldType, - TestTypeTestOptIntFieldDocString, - ) - }, - }, - TestTypeTestRefIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestRefIntFieldType, - TestTypeTestRefIntFieldDocString, - ) - }, - }, - TestTypeTestVarIntsFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestVarIntsFieldType, - TestTypeTestVarIntsFieldDocString, - ) - }, - }, - TestTypeTestConstIntsFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestConstIntsFieldType, - TestTypeTestConstIntsFieldDocString, - ) - }, - }, - TestTypeTestParamFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestParamFieldType, - TestTypeTestParamFieldDocString, - ) - }, - }, - TestTypeTestAddressFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestAddressFieldType, - TestTypeTestAddressFieldDocString, - ) - }, - }, - TestTypeTestTypeFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestTypeFieldType, - TestTypeTestTypeFieldDocString, - ) - }, - }, - TestTypeTestCapFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestCapFieldType, - TestTypeTestCapFieldDocString, - ) - }, - }, - TestTypeTestCapIntFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TestTypeTestCapIntFieldType, - TestTypeTestCapIntFieldDocString, - ) - }, - }, - } - }, +} + +func init() { + TestType.Members = func(t *SimpleType) map[string]MemberResolver { + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestIntFieldName, + TestTypeTestIntFieldType, + TestTypeTestIntFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestOptIntFieldName, + TestTypeTestOptIntFieldType, + TestTypeTestOptIntFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestRefIntFieldName, + TestTypeTestRefIntFieldType, + TestTypeTestRefIntFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestVarIntsFieldName, + TestTypeTestVarIntsFieldType, + TestTypeTestVarIntsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestConstIntsFieldName, + TestTypeTestConstIntsFieldType, + TestTypeTestConstIntsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestParamFieldName, + TestTypeTestParamFieldType, + TestTypeTestParamFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestAddressFieldName, + TestTypeTestAddressFieldType, + TestTypeTestAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestTypeFieldName, + TestTypeTestTypeFieldType, + TestTypeTestTypeFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestCapFieldName, + TestTypeTestCapFieldType, + TestTypeTestCapFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + TestTypeTestCapIntFieldName, + TestTypeTestCapIntFieldType, + TestTypeTestCapIntFieldDocString, + ), + }) + } } diff --git a/runtime/sema/gen/testdata/functions.cdc b/runtime/sema/gen/testdata/functions.cdc index 6ef3af288f..376ad84682 100644 --- a/runtime/sema/gen/testdata/functions.cdc +++ b/runtime/sema/gen/testdata/functions.cdc @@ -19,4 +19,7 @@ pub struct Test { /// This is a test function with a type parameter and a parameter using it. pub fun typeParamWithBoundAndParam(t: T) {} + + /// This is a function with 'view' modifier + pub view fun viewFunction() {} } diff --git a/runtime/sema/gen/testdata/functions.golden.go b/runtime/sema/gen/testdata/functions.golden.go index ebe3835954..b0dfc3b327 100644 --- a/runtime/sema/gen/testdata/functions.golden.go +++ b/runtime/sema/gen/testdata/functions.golden.go @@ -19,12 +19,6 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" -) - const TestTypeNothingFunctionName = "nothing" var TestTypeNothingFunctionType = &FunctionType{ @@ -46,7 +40,7 @@ var TestTypeParamsFunctionType = &FunctionType{ TypeAnnotation: NewTypeAnnotation(IntType), }, { - Label: sema.ArgumentLabelNotRequired, + Label: ArgumentLabelNotRequired, Identifier: "b", TypeAnnotation: NewTypeAnnotation(StringType), }, @@ -81,7 +75,7 @@ var TestTypeParamsAndReturnFunctionType = &FunctionType{ TypeAnnotation: NewTypeAnnotation(IntType), }, { - Label: sema.ArgumentLabelNotRequired, + Label: ArgumentLabelNotRequired, Identifier: "b", TypeAnnotation: NewTypeAnnotation(StringType), }, @@ -163,6 +157,19 @@ const TestTypeTypeParamWithBoundAndParamFunctionDocString = ` This is a test function with a type parameter and a parameter using it. ` +const TestTypeViewFunctionFunctionName = "viewFunction" + +var TestTypeViewFunctionFunctionType = &FunctionType{ + Purity: FunctionPurityView, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const TestTypeViewFunctionFunctionDocString = ` +This is a function with 'view' modifier +` + const TestTypeName = "Test" var TestType = &SimpleType{ @@ -175,120 +182,59 @@ var TestType = &SimpleType{ Equatable: false, Exportable: false, Importable: false, - Members: func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - TestTypeNothingFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeNothingFunctionType, - TestTypeNothingFunctionDocString, - ) - }, - }, - TestTypeParamsFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeParamsFunctionType, - TestTypeParamsFunctionDocString, - ) - }, - }, - TestTypeReturnBoolFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeReturnBoolFunctionType, - TestTypeReturnBoolFunctionDocString, - ) - }, - }, - TestTypeParamsAndReturnFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeParamsAndReturnFunctionType, - TestTypeParamsAndReturnFunctionDocString, - ) - }, - }, - TestTypeTypeParamFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeTypeParamFunctionType, - TestTypeTypeParamFunctionDocString, - ) - }, - }, - TestTypeTypeParamWithBoundFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { - - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeTypeParamWithBoundFunctionType, - TestTypeTypeParamWithBoundFunctionDocString, - ) - }, - }, - TestTypeTypeParamWithBoundAndParamFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, - identifier string, - targetRange ast.Range, - report func(error)) *Member { +} - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - TestTypeTypeParamWithBoundAndParamFunctionType, - TestTypeTypeParamWithBoundAndParamFunctionDocString, - ) - }, - }, - } - }, +func init() { + TestType.Members = func(t *SimpleType) map[string]MemberResolver { + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + TestTypeNothingFunctionName, + TestTypeNothingFunctionType, + TestTypeNothingFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeParamsFunctionName, + TestTypeParamsFunctionType, + TestTypeParamsFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeReturnBoolFunctionName, + TestTypeReturnBoolFunctionType, + TestTypeReturnBoolFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeParamsAndReturnFunctionName, + TestTypeParamsAndReturnFunctionType, + TestTypeParamsAndReturnFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeTypeParamFunctionName, + TestTypeTypeParamFunctionType, + TestTypeTypeParamFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeTypeParamWithBoundFunctionName, + TestTypeTypeParamWithBoundFunctionType, + TestTypeTypeParamWithBoundFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeTypeParamWithBoundAndParamFunctionName, + TestTypeTypeParamWithBoundAndParamFunctionType, + TestTypeTypeParamWithBoundAndParamFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + TestTypeViewFunctionFunctionName, + TestTypeViewFunctionFunctionType, + TestTypeViewFunctionFunctionDocString, + ), + }) + } } diff --git a/runtime/sema/gen/testdata/nested.cdc b/runtime/sema/gen/testdata/nested.cdc new file mode 100644 index 0000000000..5b6175f88a --- /dev/null +++ b/runtime/sema/gen/testdata/nested.cdc @@ -0,0 +1,12 @@ +struct Foo { + /// foo + fun foo() + + /// Bar + let bar: Foo.Bar + + struct Bar { + /// bar + fun bar() + } +} diff --git a/runtime/sema/gen/testdata/nested.golden.go b/runtime/sema/gen/testdata/nested.golden.go new file mode 100644 index 0000000000..dda9006a39 --- /dev/null +++ b/runtime/sema/gen/testdata/nested.golden.go @@ -0,0 +1,115 @@ +// Code generated from testdata/nested.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import "github.com/onflow/cadence/runtime/common" + +const FooTypeFooFunctionName = "foo" + +var FooTypeFooFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const FooTypeFooFunctionDocString = ` +foo +` + +const FooTypeBarFieldName = "bar" + +var FooTypeBarFieldType = FooBarType + +const FooTypeBarFieldDocString = ` +Bar +` + +const FooBarTypeBarFunctionName = "bar" + +var FooBarTypeBarFunctionType = &FunctionType{ + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const FooBarTypeBarFunctionDocString = ` +bar +` + +const FooBarTypeName = "Bar" + +var FooBarType = func() *CompositeType { + var t = &CompositeType{ + Identifier: FooBarTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + FooBarType, + FooBarTypeBarFunctionName, + FooBarTypeBarFunctionType, + FooBarTypeBarFunctionDocString, + ), + } + + FooBarType.Members = MembersAsMap(members) + FooBarType.Fields = MembersFieldNames(members) +} + +const FooTypeName = "Foo" + +var FooType = func() *CompositeType { + var t = &CompositeType{ + Identifier: FooTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + t.SetNestedType(FooBarTypeName, FooBarType) + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + FooType, + FooTypeFooFunctionName, + FooTypeFooFunctionType, + FooTypeFooFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + FooType, + FooTypeBarFieldName, + FooTypeBarFieldType, + FooTypeBarFieldDocString, + ), + } + + FooType.Members = MembersAsMap(members) + FooType.Fields = MembersFieldNames(members) +} diff --git a/runtime/sema/meta_type.go b/runtime/sema/meta_type.go index 9817dd8bd4..d278bf4bd3 100644 --- a/runtime/sema/meta_type.go +++ b/runtime/sema/meta_type.go @@ -18,16 +18,15 @@ package sema -import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" -) +const MetaTypeIdentifierFieldName = "identifier" -const metaTypeIdentifierDocString = ` +const metaTypeIdentifierFieldDocString = ` The fully-qualified identifier of the type ` -const metaTypeSubtypeDocString = ` +const MetaTypeIsSubtypeFunctionName = "isSubtype" + +const metaTypeIsSubtypeFunctionDocString = ` Returns true if this type is a subtype of the given type at run-time ` @@ -62,31 +61,19 @@ var MetaTypeIsSubtypeFunctionType = NewSimpleFunctionType( func init() { MetaType.Members = func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - "identifier": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StringType, - metaTypeIdentifierDocString, - ) - }, - }, - "isSubtype": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - MetaTypeIsSubtypeFunctionType, - metaTypeSubtypeDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicConstantFieldMember( + t, + MetaTypeIdentifierFieldName, + StringType, + metaTypeIdentifierFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + MetaTypeIsSubtypeFunctionName, + MetaTypeIsSubtypeFunctionType, + metaTypeIsSubtypeFunctionDocString, + ), + }) } } diff --git a/runtime/sema/public_account_contracts.go b/runtime/sema/public_account_contracts.go deleted file mode 100644 index 0c963bbc9a..0000000000 --- a/runtime/sema/public_account_contracts.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const PublicAccountContractsTypeName = "Contracts" - -// PublicAccountContractsType represents the type `PublicAccount.Contracts` -var PublicAccountContractsType = func() *CompositeType { - - publicAccountContractsType := &CompositeType{ - Identifier: PublicAccountContractsTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - publicAccountContractsType, - AccountContractsTypeGetFunctionName, - AccountContractsTypeGetFunctionType, - accountContractsTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountContractsType, - AccountContractsTypeBorrowFunctionName, - AccountContractsTypeBorrowFunctionType, - accountContractsTypeBorrowFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountContractsType, - AccountContractsTypeNamesFieldName, - accountContractsTypeNamesFieldType, - accountContractsTypeNamesFieldDocString, - ), - } - - publicAccountContractsType.Members = GetMembersAsMap(members) - publicAccountContractsType.Fields = GetFieldNames(members) - return publicAccountContractsType -}() - -func init() { - // Set the container type after initializing the `PublicAccountContractsType`, to avoid initializing loop. - PublicAccountContractsType.SetContainerType(PublicAccountType) -} diff --git a/runtime/sema/publicaccount.cdc b/runtime/sema/publicaccount.cdc new file mode 100644 index 0000000000..e69f186589 --- /dev/null +++ b/runtime/sema/publicaccount.cdc @@ -0,0 +1,82 @@ + +pub struct PublicAccount { + + /// The address of the account. + pub let address: Address + + /// The FLOW balance of the default vault of this account. + pub let balance: UFix64 + + /// The FLOW balance of the default vault of this account that is available to be moved. + pub let availableBalance: UFix64 + + /// The current amount of storage used by the account in bytes. + pub let storageUsed: UInt64 + + /// The storage capacity of the account in bytes. + pub let storageCapacity: UInt64 + + /// The contracts deployed to the account. + pub let contracts: PublicAccount.Contracts + + /// The keys assigned to the account. + pub let keys: PublicAccount.Keys + + /// All public paths of this account. + pub let publicPaths: [PublicPath] + + /// Returns the capability at the given public path. + pub fun getCapability(_ path: PublicPath): Capability + + /// Returns the target path of the capability at the given public or private path, + /// or nil if there exists no capability at the given path. + pub fun getLinkTarget(_ path: CapabilityPath): Path? + + /// Iterate over all the public paths of an account. + /// passing each path and type in turn to the provided callback function. + /// + /// The callback function takes two arguments: + /// 1. The path of the stored object + /// 2. The runtime type of that object + /// + /// Iteration is stopped early if the callback function returns `false`. + /// + /// The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, + /// is undefined. + pub fun forEachPublic(_ function: fun(PublicPath, Type): Bool) + + pub struct Contracts { + + /// The names of all contracts deployed in the account. + pub let names: [String] + + /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + /// + /// Returns nil if no contract/contract interface with the given name exists in the account. + pub fun get(name: String): DeployedContract? + + /// Returns a reference of the given type to the contract with the given name in the account, if any. + /// + /// Returns nil if no contract with the given name exists in the account, + /// or if the contract does not conform to the given type. + pub fun borrow(name: String): T? + } + + pub struct Keys { + + /// Returns the key at the given index, if it exists, or nil otherwise. + /// + /// Revoked keys are always returned, but they have `isRevoked` field set to true. + pub fun get(keyIndex: Int): AccountKey? + + /// Iterate over all unrevoked keys in this account, + /// passing each key in turn to the provided function. + /// + /// Iteration is stopped early if the function returns `false`. + /// The order of iteration is undefined. + pub fun forEach(_ function: fun(AccountKey): Bool) + + /// The total number of unrevoked keys in this account. + pub let count: UInt64 + } +} diff --git a/runtime/sema/publicaccount.cdc.go b/runtime/sema/publicaccount.cdc.go new file mode 100644 index 0000000000..9e8772a112 --- /dev/null +++ b/runtime/sema/publicaccount.cdc.go @@ -0,0 +1,23 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +//go:generate go run ./gen publicaccount.cdc publicaccount.gen.go + +var PublicAccountTypeAnnotation = NewTypeAnnotation(PublicAccountType) diff --git a/runtime/sema/publicaccount.gen.go b/runtime/sema/publicaccount.gen.go new file mode 100644 index 0000000000..a06fc53e78 --- /dev/null +++ b/runtime/sema/publicaccount.gen.go @@ -0,0 +1,481 @@ +// Code generated from publicaccount.cdc. DO NOT EDIT. +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sema + +import "github.com/onflow/cadence/runtime/common" + +const PublicAccountTypeAddressFieldName = "address" + +var PublicAccountTypeAddressFieldType = TheAddressType + +const PublicAccountTypeAddressFieldDocString = ` +The address of the account. +` + +const PublicAccountTypeBalanceFieldName = "balance" + +var PublicAccountTypeBalanceFieldType = UFix64Type + +const PublicAccountTypeBalanceFieldDocString = ` +The FLOW balance of the default vault of this account. +` + +const PublicAccountTypeAvailableBalanceFieldName = "availableBalance" + +var PublicAccountTypeAvailableBalanceFieldType = UFix64Type + +const PublicAccountTypeAvailableBalanceFieldDocString = ` +The FLOW balance of the default vault of this account that is available to be moved. +` + +const PublicAccountTypeStorageUsedFieldName = "storageUsed" + +var PublicAccountTypeStorageUsedFieldType = UInt64Type + +const PublicAccountTypeStorageUsedFieldDocString = ` +The current amount of storage used by the account in bytes. +` + +const PublicAccountTypeStorageCapacityFieldName = "storageCapacity" + +var PublicAccountTypeStorageCapacityFieldType = UInt64Type + +const PublicAccountTypeStorageCapacityFieldDocString = ` +The storage capacity of the account in bytes. +` + +const PublicAccountTypeContractsFieldName = "contracts" + +var PublicAccountTypeContractsFieldType = PublicAccountContractsType + +const PublicAccountTypeContractsFieldDocString = ` +The contracts deployed to the account. +` + +const PublicAccountTypeKeysFieldName = "keys" + +var PublicAccountTypeKeysFieldType = PublicAccountKeysType + +const PublicAccountTypeKeysFieldDocString = ` +The keys assigned to the account. +` + +const PublicAccountTypePublicPathsFieldName = "publicPaths" + +var PublicAccountTypePublicPathsFieldType = &VariableSizedType{ + Type: PublicPathType, +} + +const PublicAccountTypePublicPathsFieldDocString = ` +All public paths of this account. +` + +const PublicAccountTypeGetCapabilityFunctionName = "getCapability" + +var PublicAccountTypeGetCapabilityFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var PublicAccountTypeGetCapabilityFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + PublicAccountTypeGetCapabilityFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + MustInstantiate( + &CapabilityType{}, + &GenericType{ + TypeParameter: PublicAccountTypeGetCapabilityFunctionTypeParameterT, + }, + ), + ), +} + +const PublicAccountTypeGetCapabilityFunctionDocString = ` +Returns the capability at the given public path. +` + +const PublicAccountTypeGetLinkTargetFunctionName = "getLinkTarget" + +var PublicAccountTypeGetLinkTargetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "path", + TypeAnnotation: NewTypeAnnotation(CapabilityPathType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: PathType, + }, + ), +} + +const PublicAccountTypeGetLinkTargetFunctionDocString = ` +Returns the target path of the capability at the given public or private path, +or nil if there exists no capability at the given path. +` + +const PublicAccountTypeForEachPublicFunctionName = "forEachPublic" + +var PublicAccountTypeForEachPublicFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(PublicPathType), + }, + { + TypeAnnotation: NewTypeAnnotation(MetaType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const PublicAccountTypeForEachPublicFunctionDocString = ` +Iterate over all the public paths of an account. +passing each path and type in turn to the provided callback function. + +The callback function takes two arguments: +1. The path of the stored object +2. The runtime type of that object + +Iteration is stopped early if the callback function returns ` + "`false`" + `. + +The order of iteration, as well as the behavior of adding or removing objects from storage during iteration, +is undefined. +` + +const PublicAccountContractsTypeNamesFieldName = "names" + +var PublicAccountContractsTypeNamesFieldType = &VariableSizedType{ + Type: StringType, +} + +const PublicAccountContractsTypeNamesFieldDocString = ` +The names of all contracts deployed in the account. +` + +const PublicAccountContractsTypeGetFunctionName = "get" + +var PublicAccountContractsTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: DeployedContractType, + }, + ), +} + +const PublicAccountContractsTypeGetFunctionDocString = ` +Returns the deployed contract for the contract/contract interface with the given name in the account, if any. + +Returns nil if no contract/contract interface with the given name exists in the account. +` + +const PublicAccountContractsTypeBorrowFunctionName = "borrow" + +var PublicAccountContractsTypeBorrowFunctionTypeParameterT = &TypeParameter{ + Name: "T", + TypeBound: &ReferenceType{ + Type: AnyType, + }, +} + +var PublicAccountContractsTypeBorrowFunctionType = &FunctionType{ + TypeParameters: []*TypeParameter{ + PublicAccountContractsTypeBorrowFunctionTypeParameterT, + }, + Parameters: []Parameter{ + { + Identifier: "name", + TypeAnnotation: NewTypeAnnotation(StringType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: &GenericType{ + TypeParameter: PublicAccountContractsTypeBorrowFunctionTypeParameterT, + }, + }, + ), +} + +const PublicAccountContractsTypeBorrowFunctionDocString = ` +Returns a reference of the given type to the contract with the given name in the account, if any. + +Returns nil if no contract with the given name exists in the account, +or if the contract does not conform to the given type. +` + +const PublicAccountContractsTypeName = "Contracts" + +var PublicAccountContractsType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountContractsTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + PublicAccountContractsType, + PublicAccountContractsTypeNamesFieldName, + PublicAccountContractsTypeNamesFieldType, + PublicAccountContractsTypeNamesFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountContractsType, + PublicAccountContractsTypeGetFunctionName, + PublicAccountContractsTypeGetFunctionType, + PublicAccountContractsTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountContractsType, + PublicAccountContractsTypeBorrowFunctionName, + PublicAccountContractsTypeBorrowFunctionType, + PublicAccountContractsTypeBorrowFunctionDocString, + ), + } + + PublicAccountContractsType.Members = MembersAsMap(members) + PublicAccountContractsType.Fields = MembersFieldNames(members) +} + +const PublicAccountKeysTypeGetFunctionName = "get" + +var PublicAccountKeysTypeGetFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Identifier: "keyIndex", + TypeAnnotation: NewTypeAnnotation(IntType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + &OptionalType{ + Type: AccountKeyType, + }, + ), +} + +const PublicAccountKeysTypeGetFunctionDocString = ` +Returns the key at the given index, if it exists, or nil otherwise. + +Revoked keys are always returned, but they have ` + "`isRevoked`" + ` field set to true. +` + +const PublicAccountKeysTypeForEachFunctionName = "forEach" + +var PublicAccountKeysTypeForEachFunctionType = &FunctionType{ + Parameters: []Parameter{ + { + Label: ArgumentLabelNotRequired, + Identifier: "function", + TypeAnnotation: NewTypeAnnotation(&FunctionType{ + Parameters: []Parameter{ + { + TypeAnnotation: NewTypeAnnotation(AccountKeyType), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + BoolType, + ), + }), + }, + }, + ReturnTypeAnnotation: NewTypeAnnotation( + VoidType, + ), +} + +const PublicAccountKeysTypeForEachFunctionDocString = ` +Iterate over all unrevoked keys in this account, +passing each key in turn to the provided function. + +Iteration is stopped early if the function returns ` + "`false`" + `. +The order of iteration is undefined. +` + +const PublicAccountKeysTypeCountFieldName = "count" + +var PublicAccountKeysTypeCountFieldType = UInt64Type + +const PublicAccountKeysTypeCountFieldDocString = ` +The total number of unrevoked keys in this account. +` + +const PublicAccountKeysTypeName = "Keys" + +var PublicAccountKeysType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountKeysTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicFunctionMember( + PublicAccountKeysType, + PublicAccountKeysTypeGetFunctionName, + PublicAccountKeysTypeGetFunctionType, + PublicAccountKeysTypeGetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountKeysType, + PublicAccountKeysTypeForEachFunctionName, + PublicAccountKeysTypeForEachFunctionType, + PublicAccountKeysTypeForEachFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountKeysType, + PublicAccountKeysTypeCountFieldName, + PublicAccountKeysTypeCountFieldType, + PublicAccountKeysTypeCountFieldDocString, + ), + } + + PublicAccountKeysType.Members = MembersAsMap(members) + PublicAccountKeysType.Fields = MembersFieldNames(members) +} + +const PublicAccountTypeName = "PublicAccount" + +var PublicAccountType = func() *CompositeType { + var t = &CompositeType{ + Identifier: PublicAccountTypeName, + Kind: common.CompositeKindStructure, + importable: false, + hasComputedMembers: true, + } + + t.SetNestedType(PublicAccountContractsTypeName, PublicAccountContractsType) + t.SetNestedType(PublicAccountKeysTypeName, PublicAccountKeysType) + return t +}() + +func init() { + var members = []*Member{ + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeAddressFieldName, + PublicAccountTypeAddressFieldType, + PublicAccountTypeAddressFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeBalanceFieldName, + PublicAccountTypeBalanceFieldType, + PublicAccountTypeBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeAvailableBalanceFieldName, + PublicAccountTypeAvailableBalanceFieldType, + PublicAccountTypeAvailableBalanceFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeStorageUsedFieldName, + PublicAccountTypeStorageUsedFieldType, + PublicAccountTypeStorageUsedFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeStorageCapacityFieldName, + PublicAccountTypeStorageCapacityFieldType, + PublicAccountTypeStorageCapacityFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeContractsFieldName, + PublicAccountTypeContractsFieldType, + PublicAccountTypeContractsFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypeKeysFieldName, + PublicAccountTypeKeysFieldType, + PublicAccountTypeKeysFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + PublicAccountType, + PublicAccountTypePublicPathsFieldName, + PublicAccountTypePublicPathsFieldType, + PublicAccountTypePublicPathsFieldDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountType, + PublicAccountTypeGetCapabilityFunctionName, + PublicAccountTypeGetCapabilityFunctionType, + PublicAccountTypeGetCapabilityFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountType, + PublicAccountTypeGetLinkTargetFunctionName, + PublicAccountTypeGetLinkTargetFunctionType, + PublicAccountTypeGetLinkTargetFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + PublicAccountType, + PublicAccountTypeForEachPublicFunctionName, + PublicAccountTypeForEachPublicFunctionType, + PublicAccountTypeForEachPublicFunctionDocString, + ), + } + + PublicAccountType.Members = MembersAsMap(members) + PublicAccountType.Fields = MembersFieldNames(members) +} diff --git a/runtime/sema/publicaccount_type.go b/runtime/sema/publicaccount_type.go deleted file mode 100644 index 12dd41eb68..0000000000 --- a/runtime/sema/publicaccount_type.go +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Cadence - The resource-oriented smart contract programming language - * - * Copyright Dapper Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package sema - -import ( - "github.com/onflow/cadence/runtime/common" -) - -const PublicAccountTypeName = "PublicAccount" -const PublicAccountTypeAddressFieldName = "address" -const PublicAccountTypeBalanceFieldName = "balance" -const PublicAccountTypeAvailableBalanceFieldName = "availableBalance" -const PublicAccountTypeStorageUsedFieldName = "storageUsed" -const PublicAccountTypeStorageCapacityFieldName = "storageCapacity" -const PublicAccountTypeGetCapabilityFieldName = "getCapability" -const PublicAccountTypeGetTargetLinkFieldName = "getLinkTarget" -const PublicAccountTypeForEachPublicFieldName = "forEachPublic" -const PublicAccountTypeKeysFieldName = "keys" -const PublicAccountTypeContractsFieldName = "contracts" -const PublicAccountTypePathsFieldName = "publicPaths" - -// PublicAccountType represents the publicly accessible portion of an account. -var PublicAccountType = func() *CompositeType { - - publicAccountType := &CompositeType{ - Identifier: PublicAccountTypeName, - Kind: common.CompositeKindStructure, - hasComputedMembers: true, - importable: false, - NestedTypes: func() *StringTypeOrderedMap { - nestedTypes := &StringTypeOrderedMap{} - nestedTypes.Set(AccountKeysTypeName, PublicAccountKeysType) - nestedTypes.Set(PublicAccountContractsTypeName, PublicAccountContractsType) - return nestedTypes - }(), - } - - var members = []*Member{ - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeAddressFieldName, - TheAddressType, - accountTypeAddressFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeBalanceFieldName, - UFix64Type, - accountTypeAccountBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeAvailableBalanceFieldName, - UFix64Type, - accountTypeAccountAvailableBalanceFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeStorageUsedFieldName, - UInt64Type, - accountTypeStorageUsedFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeStorageCapacityFieldName, - UInt64Type, - accountTypeStorageCapacityFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountType, - PublicAccountTypeGetCapabilityFieldName, - PublicAccountTypeGetCapabilityFunctionType, - publicAccountTypeGetLinkTargetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountType, - PublicAccountTypeGetTargetLinkFieldName, - AccountTypeGetLinkTargetFunctionType, - accountTypeGetLinkTargetFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeKeysFieldName, - PublicAccountKeysType, - accountTypeKeysFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypeContractsFieldName, - PublicAccountContractsType, - accountTypeContractsFieldDocString, - ), - NewUnmeteredPublicConstantFieldMember( - publicAccountType, - PublicAccountTypePathsFieldName, - PublicAccountPathsType, - publicAccountTypePathsFieldDocString, - ), - NewUnmeteredPublicFunctionMember( - publicAccountType, - PublicAccountTypeForEachPublicFieldName, - PublicAccountForEachPublicFunctionType, - publicAccountForEachPublicDocString, - ), - } - - publicAccountType.Members = GetMembersAsMap(members) - publicAccountType.Fields = GetFieldNames(members) - return publicAccountType -}() - -var PublicAccountTypeAnnotation = NewTypeAnnotation(PublicAccountType) - -var PublicAccountPathsType = &VariableSizedType{ - Type: PublicPathType, -} - -const publicAccountTypePathsFieldDocString = ` -All the public paths of an account -` - -func AccountForEachFunctionType(pathType Type) *FunctionType { - const functionPurity = FunctionPurityImpure - - iterFunctionType := &FunctionType{ - Purity: functionPurity, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "path", - TypeAnnotation: NewTypeAnnotation(pathType), - }, - { - Label: ArgumentLabelNotRequired, - Identifier: "type", - TypeAnnotation: MetaTypeAnnotation, - }, - }, - ReturnTypeAnnotation: BoolTypeAnnotation, - } - return &FunctionType{ - Purity: functionPurity, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "function", - TypeAnnotation: NewTypeAnnotation(iterFunctionType), - }, - }, - ReturnTypeAnnotation: VoidTypeAnnotation, - } -} - -const publicAccountForEachPublicDocString = ` -Iterate over all the public paths in an account. - -Takes two arguments: the first is the path (/domain/key) of the stored object, and the second is the runtime type of that object - -The returned boolean of the supplied function indicates whether the iteration should continue; true will continue iterating onto the next element in storage, -false will abort iteration. -` - -var PublicAccountForEachPublicFunctionType = AccountForEachFunctionType(PublicPathType) - -// PublicAccountKeysType represents the keys associated with a public account. -var PublicAccountKeysType = func() *CompositeType { - - accountKeys := &CompositeType{ - Identifier: AccountKeysTypeName, - Kind: common.CompositeKindStructure, - importable: false, - } - - var members = []*Member{ - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeGetFunctionName, - AccountKeysTypeGetFunctionType, - accountKeysTypeGetFunctionDocString, - ), - NewUnmeteredPublicFunctionMember( - accountKeys, - AccountKeysTypeForEachFunctionName, - AccountKeysTypeForEachFunctionType, - accountKeysTypeForEachFunctionDocString, - ), - NewUnmeteredPublicConstantFieldMember( - accountKeys, - AccountKeysTypeCountFieldName, - AccountKeysTypeCountFieldType, - accountKeysTypeCountFieldDocString, - ), - } - - accountKeys.Members = GetMembersAsMap(members) - accountKeys.Fields = GetFieldNames(members) - return accountKeys -}() - -func init() { - // Set the container type after initializing the AccountKeysTypes, to avoid initializing loop. - PublicAccountKeysType.SetContainerType(PublicAccountType) -} - -var PublicAccountTypeGetCapabilityFunctionType = func() *FunctionType { - - typeParameter := &TypeParameter{ - TypeBound: &ReferenceType{ - Type: AnyType, - }, - Name: "T", - Optional: true, - } - - return &FunctionType{ - Purity: FunctionPurityView, - TypeParameters: []*TypeParameter{ - typeParameter, - }, - Parameters: []Parameter{ - { - Label: ArgumentLabelNotRequired, - Identifier: "capabilityPath", - TypeAnnotation: PublicPathTypeAnnotation, - }, - }, - ReturnTypeAnnotation: NewTypeAnnotation( - &CapabilityType{ - BorrowType: &GenericType{ - TypeParameter: typeParameter, - }, - }, - ), - } -}() - -const publicAccountTypeGetLinkTargetFunctionDocString = ` -Returns the capability at the given public path, or nil if it does not exist -` diff --git a/runtime/sema/resources.go b/runtime/sema/resources.go index 3f6e8cd43f..5c28541dc3 100644 --- a/runtime/sema/resources.go +++ b/runtime/sema/resources.go @@ -98,7 +98,7 @@ func (ris *Resources) String() string { builder.WriteString(fmt.Sprint(resource)) builder.WriteString(": ") builder.WriteString(fmt.Sprint(info)) - builder.WriteRune('\n') + builder.WriteByte('\n') }) return builder.String() } diff --git a/runtime/sema/string_type.go b/runtime/sema/string_type.go index f5a9a7d111..87a8a85796 100644 --- a/runtime/sema/string_type.go +++ b/runtime/sema/string_type.go @@ -19,8 +19,6 @@ package sema import ( - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" ) @@ -64,80 +62,44 @@ var StringTypeAnnotation = NewTypeAnnotation(StringType) func init() { StringType.Members = func(t *SimpleType) map[string]MemberResolver { - return map[string]MemberResolver{ - "concat": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StringTypeConcatFunctionType, - stringTypeConcatFunctionDocString, - ) - }, - }, - "slice": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StringTypeSliceFunctionType, - stringTypeSliceFunctionDocString, - ) - }, - }, - "decodeHex": { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - StringTypeDecodeHexFunctionType, - stringTypeDecodeHexFunctionDocString, - ) - }, - }, - "utf8": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - ByteArrayType, - stringTypeUtf8FieldDocString, - ) - }, - }, - "length": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - IntType, - stringTypeLengthFieldDocString, - ) - }, - }, - "toLower": { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - StringTypeToLowerFunctionType, - stringTypeToLowerFunctionDocString, - ) - }, - }, - } + return MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + StringTypeConcatFunctionName, + StringTypeConcatFunctionType, + stringTypeConcatFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StringTypeSliceFunctionName, + StringTypeSliceFunctionType, + stringTypeSliceFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + StringTypeDecodeHexFunctionName, + StringTypeDecodeHexFunctionType, + stringTypeDecodeHexFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StringTypeUtf8FieldName, + ByteArrayType, + stringTypeUtf8FieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StringTypeLengthFieldName, + IntType, + stringTypeLengthFieldDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + StringTypeToLowerFunctionName, + StringTypeToLowerFunctionType, + stringTypeToLowerFunctionDocString, + ), + }) } } @@ -153,6 +115,8 @@ var StringTypeConcatFunctionType = NewSimpleFunctionType( StringTypeAnnotation, ) +const StringTypeConcatFunctionName = "concat" + const stringTypeConcatFunctionDocString = ` Returns a new string which contains the given string concatenated to the end of the original string, but does not modify the original string ` @@ -172,6 +136,8 @@ var StringTypeSliceFunctionType = NewSimpleFunctionType( StringTypeAnnotation, ) +const StringTypeSliceFunctionName = "slice" + const stringTypeSliceFunctionDocString = ` Returns a new string containing the slice of the characters in the given string from start index ` + "`from`" + ` up to, but not including, the end index ` + "`upTo`" + `. @@ -200,6 +166,8 @@ var StringTypeDecodeHexFunctionType = NewSimpleFunctionType( ByteArrayTypeAnnotation, ) +const StringTypeDecodeHexFunctionName = "decodeHex" + const stringTypeDecodeHexFunctionDocString = ` Returns an array containing the bytes represented by the given hexadecimal string. @@ -207,10 +175,14 @@ The given string must only contain hexadecimal characters and must have an even If the string is malformed, the program aborts ` +const StringTypeLengthFieldName = "length" + const stringTypeLengthFieldDocString = ` The number of characters in the string ` +const StringTypeUtf8FieldName = "utf8" + const stringTypeUtf8FieldDocString = ` The byte array of the UTF-8 encoding ` @@ -221,6 +193,8 @@ var StringTypeToLowerFunctionType = NewSimpleFunctionType( StringTypeAnnotation, ) +const StringTypeToLowerFunctionName = "toLower" + const stringTypeToLowerFunctionDocString = ` Returns the string with upper case letters replaced with lowercase ` diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 8d16911a6f..b0f084cacd 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2474,15 +2474,15 @@ func formatParameter(spaces bool, label, identifier, typeAnnotation string) stri if label != "" { builder.WriteString(label) if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } } if identifier != "" { builder.WriteString(identifier) - builder.WriteRune(':') + builder.WriteByte(':') if spaces { - builder.WriteRune(' ') + builder.WriteByte(' ') } } @@ -2598,7 +2598,7 @@ func (p TypeParameter) checkTypeBound(ty Type, typeRange ast.Range) error { // Function types func formatFunctionType( - spaces bool, + separator string, purity string, typeParameters []string, parameters []string, @@ -2615,32 +2615,26 @@ func formatFunctionType( builder.WriteString("fun") if len(typeParameters) > 0 { - builder.WriteRune('<') + builder.WriteByte('<') for i, typeParameter := range typeParameters { if i > 0 { - builder.WriteRune(',') - if spaces { - builder.WriteRune(' ') - } + builder.WriteByte(',') + builder.WriteString(separator) } builder.WriteString(typeParameter) } - builder.WriteRune('>') + builder.WriteByte('>') } - builder.WriteRune('(') + builder.WriteByte('(') for i, parameter := range parameters { if i > 0 { - builder.WriteRune(',') - if spaces { - builder.WriteRune(' ') - } + builder.WriteByte(',') + builder.WriteString(separator) } builder.WriteString(parameter) } builder.WriteString("):") - if spaces { - builder.WriteRune(' ') - } + builder.WriteString(separator) builder.WriteString(returnTypeAnnotation) return builder.String() } @@ -2669,6 +2663,8 @@ type FunctionType struct { Members *StringMemberOrderedMap TypeParameters []*TypeParameter Parameters []Parameter + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once IsConstructor bool } @@ -2696,7 +2692,11 @@ func (t *FunctionType) Tag() TypeTag { return FunctionTypeTag } -func (t *FunctionType) String() string { +func (t *FunctionType) string( + typeParameterFormatter func(*TypeParameter) string, + parameterFormatter func(Parameter) string, + returnTypeAnnotationFormatter func(TypeAnnotation) string, +) string { purity := t.Purity.String() @@ -2705,7 +2705,7 @@ func (t *FunctionType) String() string { if typeParameterCount > 0 { typeParameters = make([]string, typeParameterCount) for i, typeParameter := range t.TypeParameters { - typeParameters[i] = typeParameter.String() + typeParameters[i] = typeParameterFormatter(typeParameter) } } @@ -2714,14 +2714,14 @@ func (t *FunctionType) String() string { if parameterCount > 0 { parameters = make([]string, parameterCount) for i, parameter := range t.Parameters { - parameters[i] = parameter.String() + parameters[i] = parameterFormatter(parameter) } } - returnTypeAnnotation := t.ReturnTypeAnnotation.String() + returnTypeAnnotation := returnTypeAnnotationFormatter(t.ReturnTypeAnnotation) return formatFunctionType( - true, + " ", purity, typeParameters, parameters, @@ -2729,26 +2729,14 @@ func (t *FunctionType) String() string { ) } -func (t *FunctionType) QualifiedString() string { - - purity := t.Purity.String() - - typeParameters := make([]string, len(t.TypeParameters)) - - for i, typeParameter := range t.TypeParameters { - typeParameters[i] = typeParameter.QualifiedString() - } - - parameters := make([]string, len(t.Parameters)) - - for i, parameter := range t.Parameters { - parameters[i] = parameter.QualifiedString() - } - - returnTypeAnnotation := t.ReturnTypeAnnotation.QualifiedString() - +func FormatFunctionTypeID( + purity string, + typeParameters []string, + parameters []string, + returnTypeAnnotation string, +) string { return formatFunctionType( - true, + "", purity, typeParameters, parameters, @@ -2756,28 +2744,61 @@ func (t *FunctionType) QualifiedString() string { ) } +func (t *FunctionType) String() string { + return t.string( + func(parameter *TypeParameter) string { + return parameter.String() + }, + func(parameter Parameter) string { + return parameter.String() + }, + func(typeAnnotation TypeAnnotation) string { + return typeAnnotation.String() + }, + ) +} + +func (t *FunctionType) QualifiedString() string { + return t.string( + func(parameter *TypeParameter) string { + return parameter.QualifiedString() + }, + func(parameter Parameter) string { + return parameter.QualifiedString() + }, + func(typeAnnotation TypeAnnotation) string { + return typeAnnotation.QualifiedString() + }, + ) +} + // NOTE: parameter names and argument labels are *not* part of the ID! func (t *FunctionType) ID() TypeID { purity := t.Purity.String() - typeParameters := make([]string, len(t.TypeParameters)) - - for i, typeParameter := range t.TypeParameters { - typeParameters[i] = string(typeParameter.TypeBound.ID()) + typeParameterCount := len(t.TypeParameters) + var typeParameters []string + if typeParameterCount > 0 { + typeParameters = make([]string, typeParameterCount) + for i, typeParameter := range t.TypeParameters { + typeParameters[i] = typeParameter.Name + } } - parameters := make([]string, len(t.Parameters)) - - for i, parameter := range t.Parameters { - parameters[i] = string(parameter.TypeAnnotation.Type.ID()) + parameterCount := len(t.Parameters) + var parameters []string + if parameterCount > 0 { + parameters = make([]string, parameterCount) + for i, parameter := range t.Parameters { + parameters[i] = string(parameter.TypeAnnotation.Type.ID()) + } } returnTypeAnnotation := string(t.ReturnTypeAnnotation.Type.ID()) return TypeID( - formatFunctionType( - false, + FormatFunctionTypeID( purity, typeParameters, parameters, @@ -3115,22 +3136,18 @@ func (t *FunctionType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type } func (t *FunctionType) GetMembers() map[string]MemberResolver { - // TODO: optimize - var members map[string]MemberResolver - if t.Members != nil { - members = make(map[string]MemberResolver, t.Members.Len()) - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) - } - return withBuiltinMembers(t, members) + t.initializeMemberResolvers() + return t.memberResolvers +} + +func (t *FunctionType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + var memberResolvers map[string]MemberResolver + if t.Members != nil { + memberResolvers = MembersMapAsResolvers(t.Members) + } + t.memberResolvers = withBuiltinMembers(t, memberResolvers) + }) } type ArgumentExpressionsCheck func( @@ -3998,18 +4015,7 @@ func (t *CompositeType) GetMembers() map[string]MemberResolver { func (t *CompositeType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - members := make(map[string]MemberResolver, t.Members.Len()) - - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) + memberResolvers := MembersMapAsResolvers(t.Members) // Check conformances. // If this composite type results from a normal composite declaration, @@ -4020,13 +4026,13 @@ func (t *CompositeType) initializeMemberResolvers() { t.ExplicitInterfaceConformanceSet(). ForEach(func(conformance *InterfaceType) { for name, resolver := range conformance.GetMembers() { //nolint:maprange - if _, ok := members[name]; !ok { - members[name] = resolver + if _, ok := memberResolvers[name]; !ok { + memberResolvers[name] = resolver } } }) - t.memberResolvers = withBuiltinMembers(t, members) + t.memberResolvers = withBuiltinMembers(t, memberResolvers) }) } @@ -4044,6 +4050,14 @@ func (t *CompositeType) FieldPosition(name string, declaration ast.CompositeLike return pos } +func (t *CompositeType) SetNestedType(name string, nestedType ContainedType) { + if t.NestedTypes == nil { + t.NestedTypes = &StringTypeOrderedMap{} + } + t.NestedTypes.Set(name, nestedType) + nestedType.SetContainerType(t) +} + // Member type Member struct { @@ -4341,17 +4355,7 @@ func (t *InterfaceType) GetMembers() map[string]MemberResolver { func (t *InterfaceType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - members := make(map[string]MemberResolver, t.Members.Len()) - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) + members := MembersMapAsResolvers(t.Members) t.memberResolvers = withBuiltinMembers(t, members) }) @@ -4551,9 +4555,9 @@ func (t *DictionaryType) IsImportable(results map[*Member]bool) bool { t.ValueType.IsImportable(results) } -func (*DictionaryType) IsEquatable() bool { - // TODO: - return false +func (t *DictionaryType) IsEquatable() bool { + return t.KeyType.IsEquatable() && + t.ValueType.IsEquatable() } func (t *DictionaryType) TypeAnnotationState() TypeAnnotationState { @@ -4917,37 +4921,49 @@ func (t *ReferenceType) Tag() TypeTag { return ReferenceTypeTag } +func formatReferenceType( + separator string, + authorized bool, + typeString string, +) string { + var builder strings.Builder + if authorized { + builder.WriteString("auth") + builder.WriteString(separator) + } + builder.WriteByte('&') + builder.WriteString(typeString) + return builder.String() +} + +func FormatReferenceTypeID(authorized bool, typeString string) string { + return formatReferenceType("", authorized, typeString) +} + func (t *ReferenceType) string(typeFormatter func(Type) string) string { if t.Type == nil { return "reference" } - var builder strings.Builder - if t.Authorized { - builder.WriteString("auth ") - } - builder.WriteRune('&') - builder.WriteString(typeFormatter(t.Type)) - return builder.String() + return formatReferenceType(" ", t.Authorized, typeFormatter(t.Type)) } func (t *ReferenceType) String() string { - return t.string(func(ty Type) string { - return ty.String() + return t.string(func(t Type) string { + return t.String() }) } func (t *ReferenceType) QualifiedString() string { - return t.string(func(ty Type) string { - return ty.QualifiedString() + return t.string(func(t Type) string { + return t.QualifiedString() }) } func (t *ReferenceType) ID() TypeID { - return TypeID( - t.string(func(ty Type) string { - return string(ty.ID()) - }), - ) + if t.Type == nil { + return "reference" + } + return TypeID(FormatReferenceTypeID(t.Authorized, string(t.Type.ID()))) } func (t *ReferenceType) Equal(other Type) bool { @@ -5201,20 +5217,15 @@ func (t *AddressType) GetMembers() map[string]MemberResolver { func (t *AddressType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - t.memberResolvers = withBuiltinMembers(t, map[string]MemberResolver{ - AddressTypeToBytesFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - AddressTypeToBytesFunctionType, - addressTypeToBytesFunctionDocString, - ) - }, - }, + memberResolvers := MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + AddressTypeToBytesFunctionName, + AddressTypeToBytesFunctionType, + addressTypeToBytesFunctionDocString, + ), }) + t.memberResolvers = withBuiltinMembers(t, memberResolvers) }) } @@ -5992,10 +6003,12 @@ func IsNilType(ty Type) bool { } type TransactionType struct { - Members *StringMemberOrderedMap - Fields []string - PrepareParameters []Parameter - Parameters []Parameter + Fields []string + PrepareParameters []Parameter + Parameters []Parameter + Members *StringMemberOrderedMap + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ Type = &TransactionType{} @@ -6083,22 +6096,18 @@ func (t *TransactionType) RewriteWithRestrictedTypes() (Type, bool) { } func (t *TransactionType) GetMembers() map[string]MemberResolver { - // TODO: optimize - var members map[string]MemberResolver - if t.Members != nil { - members = make(map[string]MemberResolver, t.Members.Len()) - t.Members.Foreach(func(name string, loopMember *Member) { - // NOTE: don't capture loop variable - member := loopMember - members[name] = MemberResolver{ - Kind: member.DeclarationKind, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return member - }, - } - }) - } - return withBuiltinMembers(t, members) + t.initializeMemberResolvers() + return t.memberResolvers +} + +func (t *TransactionType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { + var memberResolvers map[string]MemberResolver + if t.Members != nil { + memberResolvers = MembersMapAsResolvers(t.Members) + } + t.memberResolvers = withBuiltinMembers(t, memberResolvers) + }) } func (*TransactionType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { @@ -6116,9 +6125,11 @@ func (t *TransactionType) Resolve(_ *TypeParameterTypeOrderedMap) Type { type RestrictedType struct { Type Type // an internal set of field `Restrictions` - restrictionSet *InterfaceSet - Restrictions []*InterfaceType - restrictionSetOnce sync.Once + restrictionSet *InterfaceSet + Restrictions []*InterfaceType + restrictionSetOnce sync.Once + memberResolvers map[string]MemberResolver + memberResolversOnce sync.Once } var _ Type = &RestrictedType{} @@ -6158,21 +6169,37 @@ func (t *RestrictedType) Tag() TypeTag { return RestrictedTypeTag } -func (t *RestrictedType) string(separator string, typeFormatter func(Type) string) string { +func formatRestrictedType(separator string, typeString string, restrictionStrings []string) string { var result strings.Builder - result.WriteString(typeFormatter(t.Type)) - result.WriteRune('{') - for i, restriction := range t.Restrictions { + result.WriteString(typeString) + result.WriteByte('{') + for i, restrictionString := range restrictionStrings { if i > 0 { - result.WriteRune(',') + result.WriteByte(',') result.WriteString(separator) } - result.WriteString(typeFormatter(restriction)) + result.WriteString(restrictionString) } - result.WriteRune('}') + result.WriteByte('}') return result.String() } +func FormatRestrictedTypeID(typeString string, restrictionStrings []string) string { + return formatRestrictedType("", typeString, restrictionStrings) +} + +func (t *RestrictedType) string(separator string, typeFormatter func(Type) string) string { + var restrictionStrings []string + restrictionCount := len(t.Restrictions) + if restrictionCount > 0 { + restrictionStrings = make([]string, 0, restrictionCount) + for _, restriction := range t.Restrictions { + restrictionStrings = append(restrictionStrings, typeFormatter(restriction)) + } + } + return formatRestrictedType(separator, typeFormatter(t.Type), restrictionStrings) +} + func (t *RestrictedType) String() string { return t.string(" ", func(ty Type) string { return ty.String() @@ -6294,54 +6321,66 @@ func (t *RestrictedType) RewriteWithRestrictedTypes() (Type, bool) { } func (t *RestrictedType) GetMembers() map[string]MemberResolver { + t.initializeMemberResolvers() + return t.memberResolvers +} - members := map[string]MemberResolver{} +func (t *RestrictedType) initializeMemberResolvers() { + t.memberResolversOnce.Do(func() { - // Return the members of all restrictions. - // The invariant that restrictions may not have overlapping members is not checked here, - // but implicitly when the resource declaration's conformances are checked. + memberResolvers := map[string]MemberResolver{} - for _, restriction := range t.Restrictions { - for name, resolver := range restriction.GetMembers() { //nolint:maprange - if _, ok := members[name]; !ok { - members[name] = resolver + // Return the members of all restrictions. + // The invariant that restrictions may not have overlapping members is not checked here, + // but implicitly when the resource declaration's conformances are checked. + + for _, restriction := range t.Restrictions { + for name, resolver := range restriction.GetMembers() { //nolint:maprange + if _, ok := memberResolvers[name]; !ok { + memberResolvers[name] = resolver + } } } - } - - // Also include members of the restricted type for convenience, - // to help check the rest of the program and improve the developer experience, - // *but* also report an error that this access is invalid when the entry is resolved. - // - // The restricted type may be `AnyResource`, in which case there are no members. - for name, loopResolver := range t.Type.GetMembers() { //nolint:maprange + // Also include members of the restricted type for convenience, + // to help check the rest of the program and improve the developer experience, + // *but* also report an error that this access is invalid when the entry is resolved. + // + // The restricted type may be `AnyResource`, in which case there are no members. - if _, ok := members[name]; ok { - continue - } + for name, loopResolver := range t.Type.GetMembers() { //nolint:maprange - // NOTE: don't capture loop variable - resolver := loopResolver + if _, ok := memberResolvers[name]; ok { + continue + } - members[name] = MemberResolver{ - Kind: resolver.Kind, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, targetRange ast.Range, report func(error)) *Member { - member := resolver.Resolve(memoryGauge, identifier, targetRange, report) + // NOTE: don't capture loop variable + resolver := loopResolver + + memberResolvers[name] = MemberResolver{ + Kind: resolver.Kind, + Resolve: func( + memoryGauge common.MemoryGauge, + identifier string, + targetRange ast.Range, + report func(error), + ) *Member { + member := resolver.Resolve(memoryGauge, identifier, targetRange, report) - report( - &InvalidRestrictedTypeMemberAccessError{ - Name: identifier, - Range: targetRange, - }, - ) + report( + &InvalidRestrictedTypeMemberAccessError{ + Name: identifier, + Range: targetRange, + }, + ) - return member - }, + return member + }, + } } - } - return members + t.memberResolvers = memberResolvers + }) } func (*RestrictedType) Unify(_ Type, _ *TypeParameterTypeOrderedMap, _ func(err error), _ ast.Range) bool { @@ -6404,33 +6443,46 @@ func (t *CapabilityType) Tag() TypeTag { return CapabilityTypeTag } -func (t *CapabilityType) string(typeFormatter func(Type) string) string { +func formatCapabilityType(borrowTypeString string) string { var builder strings.Builder builder.WriteString("Capability") - if t.BorrowType != nil { - builder.WriteRune('<') - builder.WriteString(typeFormatter(t.BorrowType)) - builder.WriteRune('>') + if borrowTypeString != "" { + builder.WriteByte('<') + builder.WriteString(borrowTypeString) + builder.WriteByte('>') } return builder.String() } +func FormatCapabilityTypeID(borrowTypeString string) string { + return formatCapabilityType(borrowTypeString) +} + func (t *CapabilityType) String() string { - return t.string(func(t Type) string { - return t.String() - }) + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = borrowType.String() + } + return formatCapabilityType(borrowTypeString) } func (t *CapabilityType) QualifiedString() string { - return t.string(func(t Type) string { - return t.QualifiedString() - }) + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = borrowType.QualifiedString() + } + return formatCapabilityType(borrowTypeString) } func (t *CapabilityType) ID() TypeID { - return TypeID(t.string(func(t Type) string { - return string(t.ID()) - })) + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = string(borrowType.ID()) + } + return TypeID(FormatCapabilityTypeID(borrowTypeString)) } func (t *CapabilityType) Equal(other Type) bool { @@ -6633,44 +6685,27 @@ const CapabilityTypeAddressFieldName = "address" func (t *CapabilityType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { - t.memberResolvers = withBuiltinMembers(t, map[string]MemberResolver{ - CapabilityTypeBorrowFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityTypeBorrowFunctionType(t.BorrowType), - capabilityTypeBorrowFunctionDocString, - ) - }, - }, - CapabilityTypeCheckFunctionName: { - Kind: common.DeclarationKindFunction, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicFunctionMember( - memoryGauge, - t, - identifier, - CapabilityTypeCheckFunctionType(t.BorrowType), - capabilityTypeCheckFunctionDocString, - ) - }, - }, - CapabilityTypeAddressFieldName: { - Kind: common.DeclarationKindField, - Resolve: func(memoryGauge common.MemoryGauge, identifier string, _ ast.Range, _ func(error)) *Member { - return NewPublicConstantFieldMember( - memoryGauge, - t, - identifier, - TheAddressType, - capabilityTypeAddressFieldDocString, - ) - }, - }, + members := MembersAsResolvers([]*Member{ + NewUnmeteredPublicFunctionMember( + t, + CapabilityTypeBorrowFunctionName, + CapabilityTypeBorrowFunctionType(t.BorrowType), + capabilityTypeBorrowFunctionDocString, + ), + NewUnmeteredPublicFunctionMember( + t, + CapabilityTypeCheckFunctionName, + CapabilityTypeCheckFunctionType(t.BorrowType), + capabilityTypeCheckFunctionDocString, + ), + NewUnmeteredPublicConstantFieldMember( + t, + CapabilityTypeAddressFieldName, + TheAddressType, + capabilityTypeAddressFieldDocString, + ), }) + t.memberResolvers = withBuiltinMembers(t, members) }) } @@ -6750,8 +6785,8 @@ var AccountKeyType = func() *CompositeType { ), } - accountKeyType.Members = GetMembersAsMap(members) - accountKeyType.Fields = GetFieldNames(members) + accountKeyType.Members = MembersAsMap(members) + accountKeyType.Fields = MembersFieldNames(members) return accountKeyType }() @@ -6820,8 +6855,8 @@ var PublicKeyType = func() *CompositeType { ), } - publicKeyType.Members = GetMembersAsMap(members) - publicKeyType.Fields = GetFieldNames(members) + publicKeyType.Members = MembersAsMap(members) + publicKeyType.Fields = MembersFieldNames(members) return publicKeyType }() @@ -6875,7 +6910,7 @@ type CryptoAlgorithm interface { DocString() string } -func GetMembersAsMap(members []*Member) *StringMemberOrderedMap { +func MembersAsMap(members []*Member) *StringMemberOrderedMap { membersMap := &StringMemberOrderedMap{} for _, member := range members { name := member.Identifier.Identifier @@ -6888,7 +6923,7 @@ func GetMembersAsMap(members []*Member) *StringMemberOrderedMap { return membersMap } -func GetFieldNames(members []*Member) []string { +func MembersFieldNames(members []*Member) []string { fields := make([]string, 0) for _, member := range members { if member.DeclarationKind == common.DeclarationKindField { @@ -6899,6 +6934,36 @@ func GetFieldNames(members []*Member) []string { return fields } +func MembersMapAsResolvers(members *StringMemberOrderedMap) map[string]MemberResolver { + resolvers := make(map[string]MemberResolver, members.Len()) + + members.Foreach(func(name string, member *Member) { + resolvers[name] = MemberResolver{ + Kind: member.DeclarationKind, + Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { + return member + }, + } + }) + return resolvers +} + +func MembersAsResolvers(members []*Member) map[string]MemberResolver { + resolvers := make(map[string]MemberResolver, len(members)) + + for _, loopMember := range members { + // NOTE: don't capture loop variable + member := loopMember + resolvers[member.Identifier.Identifier] = MemberResolver{ + Kind: member.DeclarationKind, + Resolve: func(_ common.MemoryGauge, _ string, _ ast.Range, _ func(error)) *Member { + return member + }, + } + } + return resolvers +} + func isNumericSuperType(typ Type) bool { if numberType, ok := typ.(IntegerRangedType); ok { return numberType.IsSuperType() diff --git a/runtime/stdlib/account.go b/runtime/stdlib/account.go index ae671ee774..dec71fec84 100644 --- a/runtime/stdlib/account.go +++ b/runtime/stdlib/account.go @@ -230,23 +230,27 @@ func newAuthAccountContractsValue( gauge, addressValue, newAuthAccountContractsChangeFunction( + sema.AuthAccountContractsTypeAddFunctionType, gauge, handler, addressValue, false, ), newAuthAccountContractsChangeFunction( + sema.AuthAccountContractsTypeUpdate__experimentalFunctionType, gauge, handler, addressValue, true, ), newAccountContractsGetFunction( + sema.AuthAccountContractsTypeGetFunctionType, gauge, handler, addressValue, ), newAccountContractsBorrowFunction( + sema.AuthAccountContractsTypeBorrowFunctionType, gauge, handler, addressValue, @@ -283,6 +287,7 @@ func newAuthAccountKeysValue( addressValue, ), newAccountKeysGetFunction( + sema.AuthAccountKeysTypeGetFunctionType, gauge, handler, addressValue, @@ -292,7 +297,12 @@ func newAuthAccountKeysValue( handler, addressValue, ), - newAccountKeysForEachFunction(gauge, handler, addressValue), + newAccountKeysForEachFunction( + sema.AuthAccountKeysTypeForEachFunctionType, + gauge, + handler, + addressValue, + ), newAccountKeysCountGetter(gauge, handler, addressValue), ) } @@ -526,6 +536,7 @@ type AccountKeyProvider interface { } func newAccountKeysGetFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, provider AccountKeyProvider, addressValue interpreter.AddressValue, @@ -536,7 +547,7 @@ func newAccountKeysGetFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountKeysTypeGetFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { indexValue, ok := invocation.Arguments[0].(interpreter.IntValue) if !ok { @@ -583,6 +594,7 @@ func newAccountKeysGetFunction( var accountKeysForEachCallbackTypeParams = []sema.Type{sema.AccountKeyType} func newAccountKeysForEachFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, provider AccountKeyProvider, addressValue interpreter.AddressValue, @@ -591,7 +603,7 @@ func newAccountKeysForEachFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountKeysTypeForEachFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { fnValue, ok := invocation.Arguments[0].(interpreter.FunctionValue) @@ -791,11 +803,13 @@ func newPublicAccountKeysValue( gauge, addressValue, newAccountKeysGetFunction( + sema.PublicAccountKeysTypeGetFunctionType, gauge, handler, addressValue, ), newAccountKeysForEachFunction( + sema.PublicAccountKeysTypeForEachFunctionType, gauge, handler, addressValue, @@ -822,11 +836,13 @@ func newPublicAccountContractsValue( gauge, addressValue, newAccountContractsGetFunction( + sema.PublicAccountContractsTypeGetFunctionType, gauge, handler, addressValue, ), newAccountContractsBorrowFunction( + sema.PublicAccountContractsTypeBorrowFunctionType, gauge, handler, addressValue, @@ -848,7 +864,7 @@ func accountInboxPublishFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountTypeInboxPublishFunctionType, + sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { value, ok := invocation.Arguments[0].(*interpreter.StorageCapabilityValue) if !ok { @@ -903,7 +919,7 @@ func accountInboxUnpublishFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountTypeInboxPublishFunctionType, + sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { nameValue, ok := invocation.Arguments[0].(*interpreter.StringValue) if !ok { @@ -969,7 +985,7 @@ func accountInboxClaimFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountTypeInboxPublishFunctionType, + sema.AuthAccountInboxTypePublishFunctionType, func(invocation interpreter.Invocation) interpreter.Value { nameValue, ok := invocation.Arguments[0].(*interpreter.StringValue) if !ok { @@ -1121,6 +1137,7 @@ type AccountContractProvider interface { } func newAccountContractsGetFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, provider AccountContractProvider, addressValue interpreter.AddressValue, @@ -1131,7 +1148,7 @@ func newAccountContractsGetFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountContractsTypeGetFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { nameValue, ok := invocation.Arguments[0].(*interpreter.StringValue) if !ok { @@ -1170,6 +1187,7 @@ func newAccountContractsGetFunction( } func newAccountContractsBorrowFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, handler PublicAccountContractsHandler, addressValue interpreter.AddressValue, @@ -1180,7 +1198,7 @@ func newAccountContractsBorrowFunction( return interpreter.NewHostFunctionValue( gauge, - sema.AccountContractsTypeBorrowFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { inter := invocation.Interpreter @@ -1233,6 +1251,8 @@ func newAccountContractsBorrowFunction( return interpreter.Nil } + // No need to track the referenced value, since the reference is taken to a contract value. + // A contract value would never be moved or destroyed, within the execution of a program. reference := interpreter.NewEphemeralReferenceValue( inter, false, @@ -1276,6 +1296,7 @@ type AccountContractAdditionHandler interface { // - adding: `AuthAccount.contracts.add(name: "Foo", code: [...])` (isUpdate = false) // - updating: `AuthAccount.contracts.update__experimental(name: "Foo", code: [...])` (isUpdate = true) func newAuthAccountContractsChangeFunction( + functionType *sema.FunctionType, gauge common.MemoryGauge, handler AccountContractAdditionHandler, addressValue interpreter.AddressValue, @@ -1283,7 +1304,7 @@ func newAuthAccountContractsChangeFunction( ) *interpreter.HostFunctionValue { return interpreter.NewHostFunctionValue( gauge, - sema.AuthAccountContractsTypeAddFunctionType, + functionType, func(invocation interpreter.Invocation) interpreter.Value { locationRange := invocation.LocationRange diff --git a/runtime/stdlib/bls.go b/runtime/stdlib/bls.go index a99b7cafec..a879ee6f47 100644 --- a/runtime/stdlib/bls.go +++ b/runtime/stdlib/bls.go @@ -31,7 +31,7 @@ var blsContractType = func() *sema.CompositeType { Kind: common.CompositeKindContract, } - ty.Members = sema.GetMembersAsMap([]*sema.Member{ + ty.Members = sema.MembersAsMap([]*sema.Member{ sema.NewUnmeteredPublicFunctionMember( ty, blsAggregatePublicKeysFunctionName, diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 07eae85af7..cfe6b15ca6 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -132,13 +132,22 @@ pub contract Test { pub case failed } + /// Result is the interface to be implemented by the various execution + /// operations, such as transactions and scripts. + /// + pub struct interface Result { + /// The resulted status of an executed operation. + /// + pub let status: ResultStatus + } + /// The result of a transaction execution. /// - pub struct TransactionResult { + pub struct TransactionResult: Result { pub let status: ResultStatus pub let error: Error? - init(status: ResultStatus, error: Error) { + init(status: ResultStatus, error: Error?) { self.status = status self.error = error } @@ -146,7 +155,7 @@ pub contract Test { /// The result of a script execution. /// - pub struct ScriptResult { + pub struct ScriptResult: Result { pub let status: ResultStatus pub let returnValue: AnyStruct? pub let error: Error? @@ -250,4 +259,41 @@ pub contract Test { /// pub fun useConfiguration(_ configuration: Configuration) } + + /// Returns a new matcher that negates the test of the given matcher. + /// + pub fun not(_ matcher: Matcher): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + return !matcher.test(value) + }) + } + + /// Returns a new matcher that checks if the given test value is either + /// a ScriptResult or TransactionResult and the ResultStatus is succeeded. + /// Returns false in any other case. + /// + pub fun beSucceeded(): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + return (value as! {Result}).status == ResultStatus.succeeded + }) + } + + /// Returns a new matcher that checks if the given test value is either + /// a ScriptResult or TransactionResult and the ResultStatus is failed. + /// Returns false in any other case. + /// + pub fun beFailed(): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + return (value as! {Result}).status == ResultStatus.failed + }) + } + + /// Returns a new matcher that checks if the given test value is nil. + /// + pub fun beNil(): Matcher { + return Matcher(test: fun (value: AnyStruct): Bool { + return value == nil + }) + } + } diff --git a/runtime/stdlib/crypto.go b/runtime/stdlib/crypto.go index 7c9645bfa8..580b09fc7c 100644 --- a/runtime/stdlib/crypto.go +++ b/runtime/stdlib/crypto.go @@ -152,7 +152,7 @@ func cryptoAlgorithmEnumConstructorType[T sema.CryptoAlgorithm]( Type: enumType, }, ), - Members: sema.GetMembersAsMap(members), + Members: sema.MembersAsMap(members), } } diff --git a/runtime/stdlib/flow.go b/runtime/stdlib/flow.go index ff8ea53df3..e299268ab7 100644 --- a/runtime/stdlib/flow.go +++ b/runtime/stdlib/flow.go @@ -279,9 +279,7 @@ var AccountLinkedEventType = newFlowEventType( "AccountLinked", AccountEventAddressParameter, sema.Parameter{ - Identifier: "path", - TypeAnnotation: sema.NewTypeAnnotation( - sema.AuthAccountTypeLinkAccountFunctionTypePathParameterType, - ), + Identifier: "path", + TypeAnnotation: sema.AuthAccountTypeLinkAccountFunctionTypePathParameterTypeAnnotation, }, ) diff --git a/runtime/stdlib/rlp.go b/runtime/stdlib/rlp.go index 69b21d3b55..1f2085ae35 100644 --- a/runtime/stdlib/rlp.go +++ b/runtime/stdlib/rlp.go @@ -34,7 +34,7 @@ var rlpContractType = func() *sema.CompositeType { Kind: common.CompositeKindContract, } - ty.Members = sema.GetMembersAsMap([]*sema.Member{ + ty.Members = sema.MembersAsMap([]*sema.Member{ sema.NewUnmeteredPublicFunctionMember( ty, rlpDecodeListFunctionName, diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index 3864868ad4..50b31e1fe1 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -69,6 +69,7 @@ var TestContractChecker = func() *sema.Checker { activation := sema.NewVariableActivation(sema.BaseValueActivation) activation.DeclareValue(AssertFunction) + activation.DeclareValue(PanicFunction) var checker *sema.Checker checker, err = sema.NewChecker( @@ -124,6 +125,11 @@ func NewTestContract( // Inject natively implemented matchers compositeValue.Functions[newMatcherFunctionName] = newMatcherFunction compositeValue.Functions[equalMatcherFunctionName] = equalMatcherFunction + compositeValue.Functions[beEmptyMatcherFunctionName] = beEmptyMatcherFunction + compositeValue.Functions[haveElementCountMatcherFunctionName] = haveElementCountMatcherFunction + compositeValue.Functions[containMatcherFunctionName] = containMatcherFunction + compositeValue.Functions[beGreaterThanMatcherFunctionName] = beGreaterThanMatcherFunction + compositeValue.Functions[beLessThanMatcherFunctionName] = beLessThanMatcherFunction return compositeValue, nil } @@ -277,7 +283,7 @@ func init() { ), ) - // Matcher functions + // Test.equal() testContractType.Members.Set( equalMatcherFunctionName, sema.NewUnmeteredPublicFunctionMember( @@ -288,6 +294,61 @@ func init() { ), ) + // Test.beEmpty() + testContractType.Members.Set( + beEmptyMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + beEmptyMatcherFunctionName, + beEmptyMatcherFunctionType, + beEmptyMatcherFunctionDocString, + ), + ) + + // Test.haveElementCount() + testContractType.Members.Set( + haveElementCountMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + haveElementCountMatcherFunctionName, + haveElementCountMatcherFunctionType, + haveElementCountMatcherFunctionDocString, + ), + ) + + // Test.contain() + testContractType.Members.Set( + containMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + containMatcherFunctionName, + containMatcherFunctionType, + containMatcherFunctionDocString, + ), + ) + + // Test.beGreaterThan() + testContractType.Members.Set( + beGreaterThanMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + beGreaterThanMatcherFunctionName, + beGreaterThanMatcherFunctionType, + beGreaterThanMatcherFunctionDocString, + ), + ) + + // Test.beLessThan() + testContractType.Members.Set( + beLessThanMatcherFunctionName, + sema.NewUnmeteredPublicFunctionMember( + testContractType, + beLessThanMatcherFunctionName, + beLessThanMatcherFunctionType, + beLessThanMatcherFunctionDocString, + ), + ) + // Test.readFile() testContractType.Members.Set( testReadFileFunctionName, @@ -316,7 +377,7 @@ var blockchainType = func() sema.Type { return typ }() -// Functions belong to the 'Test' contract +// Functions belonging to the 'Test' contract // 'Test.assert' function @@ -472,7 +533,9 @@ var testExpectFunction = interpreter.NewUnmeteredHostFunctionValue( ) if !result { - panic(AssertionError{}) + panic(AssertionError{ + LocationRange: locationRange, + }) } return interpreter.Void @@ -765,8 +828,8 @@ var EmulatorBackendType = func() *sema.CompositeType { ), } - ty.Members = sema.GetMembersAsMap(members) - ty.Fields = sema.GetFieldNames(members) + ty.Members = sema.MembersAsMap(members) + ty.Fields = sema.MembersFieldNames(members) return ty }() @@ -1358,6 +1421,280 @@ var equalMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( }, ) +const beEmptyMatcherFunctionName = "beEmpty" + +const beEmptyMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is an array or dictionary, +and the tested value contains no elements. +` + +var beEmptyMatcherFunctionType = func() *sema.FunctionType { + return &sema.FunctionType{ + IsConstructor: false, + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{}, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +}() + +var beEmptyMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + beEmptyMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + beEmptyTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var isEmpty bool + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + isEmpty = value.Count() == 0 + case *interpreter.DictionaryValue: + isEmpty = value.Count() == 0 + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return interpreter.AsBoolValue(isEmpty) + }, + ) + + return newMatcherWithAnyStructTestFunction(invocation, beEmptyTestFunc) + }, +) + +const haveElementCountMatcherFunctionName = "haveElementCount" + +const haveElementCountMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is an array or dictionary, +and has the given number of elements. +` + +var haveElementCountMatcherFunctionType = func() *sema.FunctionType { + return &sema.FunctionType{ + IsConstructor: false, + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "count", + TypeAnnotation: sema.NewTypeAnnotation( + sema.IntType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +}() + +var haveElementCountMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + haveElementCountMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + count, ok := invocation.Arguments[0].(interpreter.IntValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + haveElementCountTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var matchingCount bool + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + matchingCount = value.Count() == count.ToInt(invocation.LocationRange) + case *interpreter.DictionaryValue: + matchingCount = value.Count() == count.ToInt(invocation.LocationRange) + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return interpreter.AsBoolValue(matchingCount) + }, + ) + + return newMatcherWithAnyStructTestFunction(invocation, haveElementCountTestFunc) + }, +) + +const containMatcherFunctionName = "contain" + +const containMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is an array that contains +a value that is equal to the given value, or the tested value is a dictionary +that contains an entry where the key is equal to the given value. +` + +var containMatcherFunctionType = func() *sema.FunctionType { + return &sema.FunctionType{ + IsConstructor: false, + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "element", + TypeAnnotation: sema.NewTypeAnnotation( + sema.AnyStructType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +}() + +var containMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + containMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + element, ok := invocation.Arguments[0].(interpreter.EquatableValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + containTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + var elementFound interpreter.BoolValue + switch value := invocation.Arguments[0].(type) { + case *interpreter.ArrayValue: + elementFound = value.Contains( + inter, + invocation.LocationRange, + element, + ) + case *interpreter.DictionaryValue: + elementFound = value.ContainsKey( + inter, + invocation.LocationRange, + element, + ) + default: + panic(errors.NewDefaultUserError("expected Array or Dictionary argument")) + } + + return elementFound + }, + ) + + return newMatcherWithAnyStructTestFunction(invocation, containTestFunc) + }, +) + +const beGreaterThanMatcherFunctionName = "beGreaterThan" + +const beGreaterThanMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is a number and +greater than the given number. +` + +var beGreaterThanMatcherFunctionType = func() *sema.FunctionType { + return &sema.FunctionType{ + IsConstructor: false, + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + sema.NumberType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +}() + +var beGreaterThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + beGreaterThanMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + beGreaterThanTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + isGreaterThan := thisValue.Greater( + inter, + otherValue, + invocation.LocationRange, + ) + + return isGreaterThan + }, + ) + + return newMatcherWithAnyStructTestFunction(invocation, beGreaterThanTestFunc) + }, +) + +const beLessThanMatcherFunctionName = "beLessThan" + +const beLessThanMatcherFunctionDocString = ` +Returns a matcher that succeeds if the tested value is a number and +less than the given number. +` + +var beLessThanMatcherFunctionType = func() *sema.FunctionType { + return &sema.FunctionType{ + IsConstructor: false, + TypeParameters: []*sema.TypeParameter{}, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: sema.NewTypeAnnotation( + sema.NumberType, + ), + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(matcherType), + } +}() + +var beLessThanMatcherFunction = interpreter.NewUnmeteredHostFunctionValue( + beLessThanMatcherFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + otherValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + inter := invocation.Interpreter + + beLessThanTestFunc := interpreter.NewHostFunctionValue( + nil, + matcherTestFunctionType, + func(invocation interpreter.Invocation) interpreter.Value { + thisValue, ok := invocation.Arguments[0].(interpreter.NumberValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + isLessThan := thisValue.Less( + inter, + otherValue, + invocation.LocationRange, + ) + + return isLessThan + }, + ) + + return newMatcherWithAnyStructTestFunction(invocation, beLessThanTestFunc) + }, +) + // 'EmulatorBackend.deployContract' function const emulatorBackendDeployContractFunctionName = "deployContract" @@ -1493,12 +1830,38 @@ func (e TestFailedError) Error() string { return fmt.Sprintf("test failed: %s", e.Err.Error()) } -func newMatcherWithGenericTestFunction( +// Creates a matcher using a function that accepts an `AnyStruct` typed parameter. +// i.e: invokes `newMatcher(fun (value: AnyStruct): Bool)`. +func newMatcherWithAnyStructTestFunction( invocation interpreter.Invocation, testFunc interpreter.FunctionValue, ) interpreter.Value { - inter := invocation.Interpreter + matcherConstructor := getNestedTypeConstructorValue( + *invocation.Self, + matcherTypeName, + ) + matcher, err := invocation.Interpreter.InvokeExternally( + matcherConstructor, + matcherConstructor.Type, + []interpreter.Value{ + testFunc, + }, + ) + + if err != nil { + panic(err) + } + + return matcher +} + +// Creates a matcher using a function that accepts a generic `T` typed parameter. +// NOTE: Use this function only if the matcher function has a generic type. +func newMatcherWithGenericTestFunction( + invocation interpreter.Invocation, + testFunc interpreter.FunctionValue, +) interpreter.Value { typeParameterPair := invocation.TypeParameterTypes.Oldest() if typeParameterPair == nil { @@ -1547,23 +1910,7 @@ func newMatcherWithGenericTestFunction( }, ) - matcherConstructor := getNestedTypeConstructorValue( - *invocation.Self, - matcherTypeName, - ) - matcher, err := inter.InvokeExternally( - matcherConstructor, - matcherConstructor.Type, - []interpreter.Value{ - matcherTestFunction, - }, - ) - - if err != nil { - panic(err) - } - - return matcher + return newMatcherWithAnyStructTestFunction(invocation, matcherTestFunction) } func TestCheckerContractValueHandler( diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index b5cae7454c..b00ecec19b 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -27,6 +27,7 @@ import ( "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" + cdcErrors "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/parser" "github.com/onflow/cadence/runtime/sema" @@ -42,12 +43,17 @@ func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpr ) require.NoError(t, err) + activation := sema.NewVariableActivation(sema.BaseValueActivation) + activation.DeclareValue(AssertFunction) + activation.DeclareValue(PanicFunction) + checker, err := sema.NewChecker( program, utils.TestLocation, nil, &sema.Config{ - AccessCheckMode: sema.AccessCheckModeStrict, + BaseValueActivation: activation, + AccessCheckMode: sema.AccessCheckModeStrict, ImportHandler: func( checker *sema.Checker, importedLocation common.Location, @@ -526,6 +532,41 @@ func TestTestEqualMatcher(t *testing.T) { assert.Equal(t, interpreter.FalseValue, result) }) + t.Run("matcher not", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let one = Test.equal(1) + + let notOne = Test.not(one) + + return notOne.test(2) + } + + pub fun testNoMatch(): Bool { + let one = Test.equal(1) + + let notOne = Test.not(one) + + return notOne.test(1) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + t.Run("chained matchers", func(t *testing.T) { t.Parallel() @@ -614,6 +655,645 @@ func TestTestEqualMatcher(t *testing.T) { }) } +func TestTestBeSucceededMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beSucceeded with ScriptResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let successful = Test.beSucceeded() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.succeeded, + returnValue: 42, + error: nil + ) + + return successful.test(scriptResult) + } + + pub fun testNoMatch(): Bool { + let successful = Test.beSucceeded() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.failed, + returnValue: nil, + error: Test.Error("Exceeding limit") + ) + + return successful.test(scriptResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beSucceeded with TransactionResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let successful = Test.beSucceeded() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.succeeded, + error: nil + ) + + return successful.test(transactionResult) + } + + pub fun testNoMatch(): Bool { + let successful = Test.beSucceeded() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.failed, + error: Test.Error("Exceeded Limit") + ) + + return successful.test(transactionResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beSucceeded with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let successful = Test.beSucceeded() + + return successful.test("hello") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + +func TestTestBeFailedMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beFailed with ScriptResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let failed = Test.beFailed() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.failed, + returnValue: nil, + error: Test.Error("Exceeding limit") + ) + + return failed.test(scriptResult) + } + + pub fun testNoMatch(): Bool { + let failed = Test.beFailed() + + let scriptResult = Test.ScriptResult( + status: Test.ResultStatus.succeeded, + returnValue: 42, + error: nil + ) + + return failed.test(scriptResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beFailed with TransactionResult", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let failed = Test.beFailed() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.failed, + error: Test.Error("Exceeding limit") + ) + + return failed.test(transactionResult) + } + + pub fun testNoMatch(): Bool { + let failed = Test.beFailed() + + let transactionResult = Test.TransactionResult( + status: Test.ResultStatus.succeeded, + error: nil + ) + + return failed.test(transactionResult) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beFailed with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let failed = Test.beFailed() + + return failed.test([]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + +func TestTestBeNilMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beNil", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let isNil = Test.beNil() + + return isNil.test(nil) + } + + pub fun testNoMatch(): Bool { + let isNil = Test.beNil() + + return isNil.test([1, 2]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) +} + +func TestTestBeEmptyMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beEmpty with Array", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let emptyArray = Test.beEmpty() + + return emptyArray.test([]) + } + + pub fun testNoMatch(): Bool { + let emptyArray = Test.beEmpty() + + return emptyArray.test([42, 23, 31]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beEmpty with Dictionary", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let emptyDict = Test.beEmpty() + let dict: {Bool: Int} = {} + + return emptyDict.test(dict) + } + + pub fun testNoMatch(): Bool { + let emptyDict = Test.beEmpty() + let dict: {Bool: Int} = {true: 1, false: 0} + + return emptyDict.test(dict) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beEmpty with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let emptyDict = Test.beEmpty() + + return emptyDict.test("empty") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + assert.ErrorAs(t, err, &cdcErrors.DefaultUserError{}) + assert.ErrorContains(t, err, "expected Array or Dictionary argument") + }) +} + +func TestTestHaveElementCountMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher haveElementCount with Array", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let hasThreeElements = Test.haveElementCount(3) + + return hasThreeElements.test([42, 23, 31]) + } + + pub fun testNoMatch(): Bool { + let hasThreeElements = Test.haveElementCount(3) + + return hasThreeElements.test([42]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher haveElementCount with Dictionary", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let hasTwoElements = Test.haveElementCount(2) + let dict: {Bool: Int} = {true: 1, false: 0} + + return hasTwoElements.test(dict) + } + + pub fun testNoMatch(): Bool { + let hasTwoElements = Test.haveElementCount(2) + let dict: {Bool: Int} = {} + + return hasTwoElements.test(dict) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher haveElementCount with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let hasTwoElements = Test.haveElementCount(2) + + return hasTwoElements.test("two") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + assert.ErrorAs(t, err, &cdcErrors.DefaultUserError{}) + assert.ErrorContains(t, err, "expected Array or Dictionary argument") + }) +} + +func TestTestContainMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher contain with Array", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let containsTwenty = Test.contain(20) + + return containsTwenty.test([42, 20, 31]) + } + + pub fun testNoMatch(): Bool { + let containsTwenty = Test.contain(20) + + return containsTwenty.test([42]) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher contain with Dictionary", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let containsFalse = Test.contain(false) + let dict: {Bool: Int} = {true: 1, false: 0} + + return containsFalse.test(dict) + } + + pub fun testNoMatch(): Bool { + let containsFive = Test.contain(5) + let dict: {Int: Bool} = {1: true, 0: false} + + return containsFive.test(dict) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher contain with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let containsFalse = Test.contain(false) + + return containsFalse.test("false") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + assert.ErrorAs(t, err, &cdcErrors.DefaultUserError{}) + assert.ErrorContains(t, err, "expected Array or Dictionary argument") + }) +} + +func TestTestBeGreaterThanMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beGreaterThan", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let greaterThanFive = Test.beGreaterThan(5) + + return greaterThanFive.test(7) + } + + pub fun testNoMatch(): Bool { + let greaterThanFive = Test.beGreaterThan(5) + + return greaterThanFive.test(2) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beGreaterThan with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let greaterThanFive = Test.beGreaterThan(5) + + return greaterThanFive.test("7") + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + +func TestTestBeLessThanMatcher(t *testing.T) { + + t.Parallel() + + t.Run("matcher beLessThan", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun testMatch(): Bool { + let lessThanSeven = Test.beLessThan(7) + + return lessThanSeven.test(5) + } + + pub fun testNoMatch(): Bool { + let lessThanSeven = Test.beLessThan(7) + + return lessThanSeven.test(9) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + result, err := inter.Invoke("testMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.TrueValue, result) + + result, err = inter.Invoke("testNoMatch") + require.NoError(t, err) + assert.Equal(t, interpreter.FalseValue, result) + }) + + t.Run("matcher beLessThan with type mismatch", func(t *testing.T) { + t.Parallel() + + script := ` + import Test + + pub fun test(): Bool { + let lessThanSeven = Test.beLessThan(7) + + return lessThanSeven.test(true) + } + ` + + inter, err := newTestContractInterpreter(t, script) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + }) +} + func TestTestExpect(t *testing.T) { t.Parallel() diff --git a/runtime/storage_test.go b/runtime/storage_test.go index 0128426f2b..0374a0c103 100644 --- a/runtime/storage_test.go +++ b/runtime/storage_test.go @@ -664,7 +664,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadStored( signer, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "test", }, Context{ @@ -681,7 +681,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadStored( signer, cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "other", }, Context{ @@ -698,7 +698,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadLinked( signer, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "test", }, Context{ @@ -715,7 +715,7 @@ func TestRuntimeStorageReadAndBorrow(t *testing.T) { value, err := runtime.ReadLinked( signer, cadence.Path{ - Domain: "private", + Domain: common.PathDomainPrivate, Identifier: "other", }, Context{ @@ -1402,7 +1402,7 @@ func TestRuntimeStorageSaveStorageCapability(t *testing.T) { t.Run(fmt.Sprintf("%s %s", domain.Identifier(), typeDescription), func(t *testing.T) { storagePath := cadence.Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: fmt.Sprintf( "test%s%s", typeDescription, @@ -1445,7 +1445,7 @@ func TestRuntimeStorageSaveStorageCapability(t *testing.T) { expected := cadence.StorageCapability{ Path: cadence.Path{ - Domain: domain.Identifier(), + Domain: domain, Identifier: "test", }, Address: cadence.Address(signer), diff --git a/runtime/tests/checker/arrays_dictionaries_test.go b/runtime/tests/checker/arrays_dictionaries_test.go index c2cd48cfe7..7ee643a772 100644 --- a/runtime/tests/checker/arrays_dictionaries_test.go +++ b/runtime/tests/checker/arrays_dictionaries_test.go @@ -317,6 +317,167 @@ func TestCheckDictionaryValues(t *testing.T) { ) } +func TestCheckDictionaryEqual(t *testing.T) { + t.Parallel() + + testValid := func(name, code string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, code) + require.NoError(t, err) + }) + } + + assertInvalid := func(name, code string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, code) + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.InvalidBinaryOperandsError{}, errs[0]) + }) + } + + for _, opStr := range []string{"==", "!="} { + testValid( + "self_dict_equality", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + return d %s d + } + `, + opStr, + ), + ) + + testValid( + "self_dict_equality_nested_1", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: 100, 2: 200}, "def": {4: 400, 5: 500}} + return d %s d + } + `, + opStr, + ), + ) + + testValid( + "self_dict_equality_nested_2", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_true", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + return d %s d2 + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_true_nested", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_false", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2, "xyz": 4} + return d %s d2 + } + `, + opStr, + ), + ) + + testValid( + "dict_equality_false_nested", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + } + `, + opStr, + ), + ) + + assertInvalid("dict_equality_invalid", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": 1, "def": 2} + let d2 = {1: "abc", 2: "def"} + return d %s d2 + } + `, + opStr, + ), + ) + + assertInvalid( + "dict_equality_invalid_nested", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {1000: "a"}, 2: {2000: "b"}}, "def": {4: {1000: "c"}, 5: {2000: "d"}}} + return d %s d2 + } + `, + opStr, + ), + ) + + assertInvalid( + "dict_equality_invalid_inner_type_unequatable", + fmt.Sprintf( + ` + fun test(): Bool { + let d = {"abc": fun (): Void {}} + let d2 = {"abc": fun (): Void {}} + return d %s d2 + } + `, + opStr, + ), + ) + } +} + func TestCheckLength(t *testing.T) { t.Parallel() diff --git a/runtime/tests/checker/fixedpoint_test.go b/runtime/tests/checker/fixedpoint_test.go index ba65b9c1d5..18d1be7708 100644 --- a/runtime/tests/checker/fixedpoint_test.go +++ b/runtime/tests/checker/fixedpoint_test.go @@ -144,7 +144,7 @@ func TestCheckFixedPointLiteralRanges(t *testing.T) { formatLiteral := func(integer, fractional *big.Int) string { var builder strings.Builder builder.WriteString(integer.String()) - builder.WriteRune('.') + builder.WriteByte('.') builder.WriteString(format.PadLeft(fractional.String(), '0', ranged.Scale())) return builder.String() } diff --git a/runtime/tests/checker/function_test.go b/runtime/tests/checker/function_test.go index dcd0dde756..5a53d957c5 100644 --- a/runtime/tests/checker/function_test.go +++ b/runtime/tests/checker/function_test.go @@ -456,3 +456,52 @@ func TestCheckNativeFunctionDeclaration(t *testing.T) { assert.IsType(t, &sema.InvalidNativeModifierError{}, errs[0]) } + +func TestCheckResultVariable(t *testing.T) { + + t.Parallel() + + t.Run("resource", func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R { + post { + result.id == 1234: "Invalid id" + } + return <- create R() + }`, + ) + + require.NoError(t, err) + }) + + t.Run("optional resource", func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheck(t, ` + pub resource R { + pub let id: UInt64 + init() { + self.id = 1 + } + } + + pub fun main(): @R? { + post { + result!.id == 1234: "invalid id" + } + return nil + }`, + ) + + require.NoError(t, err) + }) +} diff --git a/runtime/tests/checker/reference_test.go b/runtime/tests/checker/reference_test.go index 52f5d923f7..030597c602 100644 --- a/runtime/tests/checker/reference_test.go +++ b/runtime/tests/checker/reference_test.go @@ -834,6 +834,27 @@ func TestCheckInvalidReferenceResourceLoss(t *testing.T) { assert.IsType(t, &sema.ResourceLossError{}, errs[0]) } +func TestCheckInvalidReferenceResourceLoss2(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource R {} + + fun f(): @R { + return <- create R() + } + + fun test() { + let ref = &f() as &R + } + `) + + errs := RequireCheckerErrors(t, err, 1) + + assert.IsType(t, &sema.ResourceLossError{}, errs[0]) +} + func TestCheckInvalidReferenceIndexingIfReferencedNotIndexable(t *testing.T) { t.Parallel() diff --git a/runtime/tests/checker/resources_test.go b/runtime/tests/checker/resources_test.go index 6871b3cd21..ec4e04b1d5 100644 --- a/runtime/tests/checker/resources_test.go +++ b/runtime/tests/checker/resources_test.go @@ -9135,8 +9135,9 @@ func TestCheckResourceInvalidationWithMove(t *testing.T) { } `) - errs := RequireCheckerErrors(t, err, 1) - assert.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[0]) + errs := RequireCheckerErrors(t, err, 2) + assert.IsType(t, &sema.ResourceLossError{}, errs[0]) + assert.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[1]) }) t.Run("in casting expression", func(t *testing.T) { diff --git a/runtime/tests/interpreter/attachments_test.go b/runtime/tests/interpreter/attachments_test.go index edca638b37..216afb1ddb 100644 --- a/runtime/tests/interpreter/attachments_test.go +++ b/runtime/tests/interpreter/attachments_test.go @@ -1570,18 +1570,30 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { inter, _ := testAccount(t, address, true, ` resource R {} attachment A for R { - fun foo(): Int { return 3 } + pub(set) var id: UInt8 + init() { + self.id = 1 + } } - fun test() { + fun test(): UInt8 { let r <- create R() let r2 <- attach A() to <-r let a = r2[A]! - authAccount.save(<-r2, to: /storage/foo) - let i = a.foo() - } - `, sema.Config{ - AttachmentsEnabled: true, - }, + + + // Move the resource after taking a reference to the attachment. + // Then update the field of the attachment. + var r3 <- r2 + let a2 = r3[A]! + a2.id = 5 + authAccount.save(<-r3, to: /storage/foo) + + // Access the attachment filed from the previous reference. + return a.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, ) _, err := inter.Invoke("test") @@ -1633,18 +1645,70 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { } } attachment A for R { - fun foo(): Int { return 3 } + pub(set) var id: UInt8 + init() { + self.id = 1 + } } - fun test() { + fun test(): UInt8 { let r2 <- create R2(r: <-attach A() to <-create R()) let a = r2.r[A]! - authAccount.save(<-r2, to: /storage/foo) - let i = a.foo() + + // Move the resource after taking a reference to the attachment. + // Then update the field of the attachment. + var r3 <- r2 + let a2 = r3.r[A]! + a2.id = 5 + authAccount.save(<-r3, to: /storage/foo) + + // Access the attachment filed from the previous reference. + return a.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, + ) + + _, err := inter.Invoke("test") + require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{}) + }) + + t.Run("base reference", func(t *testing.T) { + + t.Parallel() + + address := interpreter.NewUnmeteredAddressValueFromBytes([]byte{42}) + + inter, _ := testAccount(t, address, true, ` + pub resource R { + pub(set) var id: UInt8 + init() { + self.id = 1 + } } - - `, sema.Config{ - AttachmentsEnabled: true, - }, + + var ref: &R? = nil + + attachment A for R { + fun saveBaseRef() { + ref = base + } + } + + fun test(): UInt8 { + let r <- attach A() to <-create R() + let a = r[A]! + + a.saveBaseRef() + + var r2 <- r + r2.id = 5 + authAccount.save(<-r2, to: /storage/foo) + return ref!.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, ) _, err := inter.Invoke("test") @@ -1686,6 +1750,47 @@ func TestInterpretAttachmentResourceReferenceInvalidation(t *testing.T) { _, err := inter.Invoke("test") require.ErrorAs(t, err, &interpreter.DestroyedResourceError{}) }) + + t.Run("self reference", func(t *testing.T) { + + t.Parallel() + + address := interpreter.NewUnmeteredAddressValueFromBytes([]byte{42}) + + inter, _ := testAccount(t, address, true, ` + pub resource R {} + + var ref: &A? = nil + + attachment A for R { + pub(set) var id: UInt8 + init() { + self.id = 1 + } + + fun saveSelfRef() { + ref = self as &A + } + } + + fun test(): UInt8 { + let r <- attach A() to <-create R() + r[A]!.saveSelfRef() + + var r2 <- r + let a = r2[A]! + a.id = 5 + authAccount.save(<-r2, to: /storage/foo) + return ref!.id + }`, + sema.Config{ + AttachmentsEnabled: true, + }, + ) + + _, err := inter.Invoke("test") + require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{}) + }) } func TestInterpretAttachmentsRuntimeType(t *testing.T) { diff --git a/runtime/tests/interpreter/function_test.go b/runtime/tests/interpreter/function_test.go new file mode 100644 index 0000000000..29d10857fa --- /dev/null +++ b/runtime/tests/interpreter/function_test.go @@ -0,0 +1,284 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package interpreter_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/tests/checker" + "github.com/onflow/cadence/runtime/tests/utils" +) + +func TestInterpretResultVariable(t *testing.T) { + + t.Parallel() + + t.Run("resource type, resource value", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt8 + init() { + self.id = 1 + } + } + + pub fun main(): @R { + post { + result.id == 1: "invalid id" + } + return <- create R() + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + require.IsType(t, &interpreter.CompositeValue{}, result) + resource := result.(*interpreter.CompositeValue) + assert.Equal(t, common.CompositeKindResource, resource.Kind) + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt8Value(1), + resource.GetField(inter, interpreter.EmptyLocationRange, "id"), + ) + }) + + t.Run("optional resource type, resource value", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt8 + init() { + self.id = 1 + } + } + + pub fun main(): @R? { + post { + result!.id == 1: "invalid id" + } + return <- create R() + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + require.IsType(t, &interpreter.SomeValue{}, result) + someValue := result.(*interpreter.SomeValue) + + innerValue := someValue.InnerValue(inter, interpreter.EmptyLocationRange) + require.IsType(t, &interpreter.CompositeValue{}, innerValue) + + resource := innerValue.(*interpreter.CompositeValue) + assert.Equal(t, common.CompositeKindResource, resource.Kind) + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt8Value(1), + resource.GetField(inter, interpreter.EmptyLocationRange, "id"), + ) + }) + + t.Run("optional resource type, nil value", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt8 + init() { + self.id = 1 + } + } + + pub fun main(): @R? { + post { + result == nil: "invalid result" + } + return nil + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + require.Equal(t, interpreter.NilValue{}, result) + }) + + t.Run("any resource type, optional value", func(t *testing.T) { + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + pub resource R { + pub let id: UInt8 + init() { + self.id = 1 + } + } + + pub fun main(): @AnyResource { + post { + result != nil: "invalid value" + } + + var value: @R? <- create R() + return <- value + }`, + ) + + result, err := inter.Invoke("main") + require.NoError(t, err) + + require.IsType(t, &interpreter.SomeValue{}, result) + someValue := result.(*interpreter.SomeValue) + + innerValue := someValue.InnerValue(inter, interpreter.EmptyLocationRange) + require.IsType(t, &interpreter.CompositeValue{}, innerValue) + + resource := innerValue.(*interpreter.CompositeValue) + assert.Equal(t, common.CompositeKindResource, resource.Kind) + utils.AssertValuesEqual( + t, + inter, + interpreter.UInt8Value(1), + resource.GetField(inter, interpreter.EmptyLocationRange, "id"), + ) + }) + + t.Run("reference invalidation, optional type", func(t *testing.T) { + t.Parallel() + + var checkerErrors []error + + inter, err := parseCheckAndInterpretWithOptions(t, ` + pub resource R { + pub(set) var id: UInt8 + init() { + self.id = 1 + } + } + + var ref: &R? = nil + + pub fun main(): @R? { + var r <- createAndStoreRef() + if var r2 <- r { + r2.id = 2 + return <- r2 + } + + return nil + } + + pub fun createAndStoreRef(): @R? { + post { + storeRef(result) + } + return <- create R() + } + + pub fun storeRef(_ r: &R?): Bool { + ref = r + return r != nil + } + + pub fun getID(): UInt8 { + return ref!.id + }`, + ParseCheckAndInterpretOptions{ + HandleCheckerError: func(err error) { + checkerErrors = append(checkerErrors, err) + }, + }, + ) + require.NoError(t, err) + require.Len(t, checkerErrors, 1) + checkerError := checker.RequireCheckerErrors(t, checkerErrors[0], 1) + require.IsType(t, &sema.PurityError{}, checkerError[0]) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + _, err = inter.Invoke("getID") + require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{}) + }) + + t.Run("reference invalidation, non optional", func(t *testing.T) { + t.Parallel() + + var checkerErrors []error + + inter, err := parseCheckAndInterpretWithOptions(t, ` + pub resource R { + pub(set) var id: UInt8 + init() { + self.id = 1 + } + } + + var ref: &R? = nil + + pub fun main(): @R { + var r <- createAndStoreRef() + r.id = 2 + return <- r + } + + pub fun createAndStoreRef(): @R { + post { + storeRef(result) + } + return <- create R() + } + + pub fun storeRef(_ r: &R): Bool { + ref = r + return r != nil + } + + pub fun getID(): UInt8 { + return ref!.id + }`, + ParseCheckAndInterpretOptions{ + HandleCheckerError: func(err error) { + checkerErrors = append(checkerErrors, err) + }, + }, + ) + require.NoError(t, err) + require.Len(t, checkerErrors, 1) + checkerError := checker.RequireCheckerErrors(t, checkerErrors[0], 1) + require.IsType(t, &sema.PurityError{}, checkerError[0]) + + _, err = inter.Invoke("main") + require.NoError(t, err) + + _, err = inter.Invoke("getID") + require.ErrorAs(t, err, &interpreter.InvalidatedResourceReferenceError{}) + }) +} diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 7a3ab23df4..456cd03ea4 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -4569,6 +4569,113 @@ func TestInterpretDictionaryIndexingAssignmentNil(t *testing.T) { ) } +func TestInterpretDictionaryEquality(t *testing.T) { + t.Parallel() + + testBooleanFunction := func(t *testing.T, name string, expected bool, innerCode string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + + code := fmt.Sprintf("fun test(): Bool { \n %s \n }", innerCode) + + inter := parseCheckAndInterpret(t, code) + res, err := inter.Invoke("test") + + require.NoError(t, err) + + boolVal, ok := res.(interpreter.BoolValue) + require.True(t, ok) + + require.Equal(t, bool(boolVal), expected) + }) + + } + + for _, opStr := range []string{"==", "!="} { + testBooleanFunction( + t, + "dictionary should be equal to itself", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": 1, "def": 2} + return d %s d + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "nested dictionary should be equal to itself", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "simple dictionary equality", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2} + return d %s d2 + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "nested dictionary equality check", + opStr == "==", + fmt.Sprintf( + ` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "simple dictionary unequal", + opStr == "!=", + fmt.Sprintf( + ` + let d = {"abc": 1, "def": 2} + let d2 = {"abc": 1, "def": 2, "xyz": 4} + return d %s d2 + `, + opStr, + ), + ) + + testBooleanFunction( + t, + "nested dictionary unequal", + opStr == "!=", + fmt.Sprintf( + ` + let d = {"abc": {1: {"a": 1000}, 2: {"b": 2000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + let d2 = {"abc": {1: {"a": 1000}, 2: {"c": 1000}}, "def": {4: {"c": 1000}, 5: {"d": 2000}}} + return d %s d2 + `, + opStr, + ), + ) + } +} + func TestInterpretOptionalAnyStruct(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/resources_test.go b/runtime/tests/interpreter/resources_test.go index 85607534d7..544a24ea51 100644 --- a/runtime/tests/interpreter/resources_test.go +++ b/runtime/tests/interpreter/resources_test.go @@ -1751,8 +1751,9 @@ func TestInterpretInvalidatedResourceValidation(t *testing.T) { }`, ParseCheckAndInterpretOptions{ HandleCheckerError: func(err error) { - errs := checker.RequireCheckerErrors(t, err, 1) - require.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[0]) + errs := checker.RequireCheckerErrors(t, err, 2) + require.IsType(t, &sema.ResourceLossError{}, errs[0]) + require.IsType(t, &sema.ResourceUseAfterInvalidationError{}, errs[1]) }, }, ) diff --git a/runtime/tests/interpreter/uuid_test.go b/runtime/tests/interpreter/uuid_test.go index 8ef6adca9c..d1aaf27a11 100644 --- a/runtime/tests/interpreter/uuid_test.go +++ b/runtime/tests/interpreter/uuid_test.go @@ -88,7 +88,8 @@ func TestInterpretResourceUUID(t *testing.T) { interpreter.ProgramFromChecker(importingChecker), importingChecker.Location, &interpreter.Config{ - Storage: storage, + InvalidatedResourceValidationEnabled: true, + Storage: storage, UUIDHandler: func() (uint64, error) { defer func() { uuid++ }() return uuid, nil diff --git a/types.go b/types.go index 70b08781f3..78f23f6998 100644 --- a/types.go +++ b/types.go @@ -23,6 +23,7 @@ import ( "sync" "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" ) type Type interface { @@ -1053,6 +1054,23 @@ func NewParameter( } } +// TypeParameter + +type TypeParameter struct { + Name string + TypeBound Type +} + +func NewTypeParameter( + name string, + typeBound Type, +) TypeParameter { + return TypeParameter{ + Name: name, + TypeBound: typeBound, + } +} + // CompositeType type CompositeType interface { @@ -1701,48 +1719,77 @@ const ( FunctionPurityView ) -// TODO: type parameters type FunctionType struct { - ReturnType Type - typeID string - Purity FunctionPurity - Parameters []Parameter + TypeParameters []TypeParameter + Parameters []Parameter + ReturnType Type + Purity FunctionPurity + typeID string } func NewFunctionType( - typeID string, purity FunctionPurity, + typeParameters []TypeParameter, parameters []Parameter, returnType Type, ) *FunctionType { return &FunctionType{ - typeID: typeID, - Purity: purity, - Parameters: parameters, - ReturnType: returnType, + Purity: purity, + TypeParameters: typeParameters, + Parameters: parameters, + ReturnType: returnType, } } func NewMeteredFunctionType( gauge common.MemoryGauge, - typeID string, purity FunctionPurity, + typeParameters []TypeParameter, parameters []Parameter, returnType Type, ) *FunctionType { common.UseMemory(gauge, common.CadenceFunctionTypeMemoryUsage) - return NewFunctionType(typeID, purity, parameters, returnType) + return NewFunctionType(purity, typeParameters, parameters, returnType) } func (*FunctionType) isType() {} func (t *FunctionType) ID() string { - return t.typeID -} + if t.typeID == "" { -func (t *FunctionType) WithID(id string) *FunctionType { - t.typeID = id - return t + var purity string + if t.Purity == FunctionPurityView { + purity = "view" + } + + typeParameterCount := len(t.TypeParameters) + var typeParameters []string + if typeParameterCount > 0 { + typeParameters = make([]string, typeParameterCount) + for i, typeParameter := range t.TypeParameters { + typeParameters[i] = typeParameter.Name + } + } + + parameterCount := len(t.Parameters) + var parameters []string + if parameterCount > 0 { + parameters = make([]string, parameterCount) + for i, parameter := range t.Parameters { + parameters[i] = parameter.Type.ID() + } + } + + returnType := t.ReturnType.ID() + + t.typeID = sema.FormatFunctionTypeID( + purity, + typeParameters, + parameters, + returnType, + ) + } + return t.typeID } func (t *FunctionType) Equal(other Type) bool { @@ -1751,6 +1798,28 @@ func (t *FunctionType) Equal(other Type) bool { return false } + // Type parameters + + if len(t.TypeParameters) != len(otherType.TypeParameters) { + return false + } + + for i, typeParameter := range t.TypeParameters { + otherTypeParameter := otherType.TypeParameters[i] + + if typeParameter.TypeBound == nil { + if otherTypeParameter.TypeBound != nil { + return false + } + } else if otherTypeParameter.TypeBound == nil || + !typeParameter.TypeBound.Equal(otherTypeParameter.TypeBound) { + + return false + } + } + + // Parameters + if len(t.Parameters) != len(otherType.Parameters) { return false } @@ -1797,12 +1866,8 @@ func NewMeteredReferenceType( func (*ReferenceType) isType() {} func (t *ReferenceType) ID() string { - if len(t.typeID) == 0 { - var prefix string - if t.Authorized { - prefix = "auth" - } - t.typeID = fmt.Sprintf("%s&%s", prefix, t.Type.ID()) + if t.typeID == "" { + t.typeID = sema.FormatReferenceTypeID(t.Authorized, t.Type.ID()) } return t.typeID } @@ -1819,23 +1884,21 @@ func (t *ReferenceType) Equal(other Type) bool { // RestrictedType -type restrictionSet = map[Type]struct{} +type RestrictionSet = map[Type]struct{} type RestrictedType struct { typeID string Type Type Restrictions []Type - restrictionSet restrictionSet + restrictionSet RestrictionSet restrictionSetOnce sync.Once } func NewRestrictedType( - typeID string, typ Type, restrictions []Type, ) *RestrictedType { return &RestrictedType{ - typeID: typeID, Type: typ, Restrictions: restrictions, } @@ -1843,44 +1906,59 @@ func NewRestrictedType( func NewMeteredRestrictedType( gauge common.MemoryGauge, - typeID string, typ Type, restrictions []Type, ) *RestrictedType { common.UseMemory(gauge, common.CadenceRestrictedTypeMemoryUsage) - return NewRestrictedType(typeID, typ, restrictions) + return NewRestrictedType(typ, restrictions) } func (*RestrictedType) isType() {} func (t *RestrictedType) ID() string { + if t.typeID == "" { + var restrictionStrings []string + restrictionCount := len(t.Restrictions) + if restrictionCount > 0 { + restrictionStrings = make([]string, 0, restrictionCount) + for _, restriction := range t.Restrictions { + restrictionStrings = append(restrictionStrings, restriction.ID()) + } + } + var typeString string + if t.Type != nil { + typeString = t.Type.ID() + } + t.typeID = sema.FormatRestrictedTypeID(typeString, restrictionStrings) + } return t.typeID } -func (t *RestrictedType) WithID(id string) *RestrictedType { - t.typeID = id - return t -} - func (t *RestrictedType) Equal(other Type) bool { otherType, ok := other.(*RestrictedType) if !ok { return false } - if !t.Type.Equal(otherType.Type) { + if t.Type == nil && otherType.Type != nil { + return false + } + if t.Type != nil && otherType.Type == nil { + return false + } + if t.Type != nil && !t.Type.Equal(otherType.Type) { return false } - t.initializeRestrictionSet() - otherType.initializeRestrictionSet() + restrictionSet := t.RestrictionSet() + otherRestrictionSet := otherType.RestrictionSet() - if len(t.restrictionSet) != len(otherType.restrictionSet) { + if len(restrictionSet) != len(otherRestrictionSet) { return false } - for restriction := range t.restrictionSet { //nolint:maprange - _, ok := otherType.restrictionSet[restriction] + for restriction := range restrictionSet { //nolint:maprange + _, ok := otherRestrictionSet[restriction] if !ok { return false } @@ -1891,13 +1969,18 @@ func (t *RestrictedType) Equal(other Type) bool { func (t *RestrictedType) initializeRestrictionSet() { t.restrictionSetOnce.Do(func() { - t.restrictionSet = restrictionSet{} + t.restrictionSet = make(RestrictionSet, len(t.Restrictions)) for _, restriction := range t.Restrictions { t.restrictionSet[restriction] = struct{}{} } }) } +func (t *RestrictedType) RestrictionSet() RestrictionSet { + t.initializeRestrictionSet() + return t.restrictionSet +} + // BlockType type BlockType struct{} @@ -2042,12 +2125,13 @@ func NewMeteredCapabilityType( func (*CapabilityType) isType() {} func (t *CapabilityType) ID() string { - if len(t.typeID) == 0 { - if t.BorrowType != nil { - t.typeID = fmt.Sprintf("Capability<%s>", t.BorrowType.ID()) - } else { - t.typeID = "Capability" + if t.typeID == "" { + var borrowTypeString string + borrowType := t.BorrowType + if borrowType != nil { + borrowTypeString = borrowType.ID() } + t.typeID = sema.FormatCapabilityTypeID(borrowTypeString) } return t.typeID } @@ -2141,8 +2225,7 @@ func (t *EnumType) Equal(other Type) bool { } return t.Location == otherType.Location && - t.QualifiedIdentifier == otherType.QualifiedIdentifier && - t.RawType.Equal(otherType.RawType) + t.QualifiedIdentifier == otherType.QualifiedIdentifier } // AuthAccountType @@ -2330,6 +2413,11 @@ func TypeWithCachedTypeID(t Type) Type { TypeWithCachedTypeID(p.Type) } } + + case *RestrictedType: + for _, restriction := range t.Restrictions { + TypeWithCachedTypeID(restriction) + } } return t diff --git a/types_test.go b/types_test.go index eaafba7a00..16414a2faf 100644 --- a/types_test.go +++ b/types_test.go @@ -169,9 +169,29 @@ func TestType_ID(t *testing.T) { "S.test.BarI", }, { - (&RestrictedType{}).WithID("S.test.Foo{S.test.FooI}"), + &RestrictedType{ + Type: &ResourceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "Foo", + }, + Restrictions: []Type{ + &ResourceInterfaceType{ + Location: utils.TestLocation, + QualifiedIdentifier: "FooI", + }, + }, + }, "S.test.Foo{S.test.FooI}", }, + { + &FunctionType{ + Parameters: []Parameter{ + {Type: IntType{}}, + }, + ReturnType: StringType{}, + }, + "fun(Int):String", + }, { &EventType{ QualifiedIdentifier: "Event", @@ -1390,6 +1410,200 @@ func TestTypeEquality(t *testing.T) { assert.False(t, source.Equal(target)) }) + t.Run("different type param count", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{}, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("different type param name", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "U", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.True(t, source.Equal(target)) + }) + + t.Run("different type param bound: nil, some", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyStructType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("different type param bound: some, nil", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyStructType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("different type param bounds", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyResourceType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyStructType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.False(t, source.Equal(target)) + }) + + t.Run("same type param bounds", func(t *testing.T) { + t.Parallel() + + source := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyResourceType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + target := &FunctionType{ + TypeParameters: []TypeParameter{ + { + Name: "T", + TypeBound: AnyResourceType{}, + }, + }, + Parameters: []Parameter{ + { + Type: IntType{}, + }, + }, + ReturnType: StringType{}, + } + assert.True(t, source.Equal(target)) + }) + t.Run("different type", func(t *testing.T) { t.Parallel() @@ -1639,28 +1853,6 @@ func TestTypeEquality(t *testing.T) { assert.True(t, source.Equal(target)) }) - t.Run("different raw type", func(t *testing.T) { - t.Parallel() - - source := &EnumType{ - Location: common.AddressLocation{ - Name: "Foo", - Address: common.Address{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}, - }, - QualifiedIdentifier: "Bar", - RawType: IntType{}, - } - target := &EnumType{ - Location: common.AddressLocation{ - Name: "Foo", - Address: common.Address{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}, - }, - QualifiedIdentifier: "Bar", - RawType: StringType{}, - } - assert.False(t, source.Equal(target)) - }) - t.Run("different location name", func(t *testing.T) { t.Parallel() diff --git a/values.go b/values.go index df7f603e35..50bd503399 100644 --- a/values.go +++ b/values.go @@ -1267,6 +1267,11 @@ func NewMeteredFix64(gauge common.MemoryGauge, constructor func() (string, error return NewFix64(value) } +func NewMeteredFix64FromRawFixedPointNumber(gauge common.MemoryGauge, n int64) (Fix64, error) { + common.UseMemory(gauge, fix64MemoryUsage) + return Fix64(n), nil +} + func (Fix64) isValue() {} func (Fix64) Type() Type { @@ -1336,6 +1341,11 @@ func ParseUFix64(s string) (uint64, error) { return v.Uint64(), nil } +func NewMeteredUFix64FromRawFixedPointNumber(gauge common.MemoryGauge, n uint64) (UFix64, error) { + common.UseMemory(gauge, ufix64MemoryUsage) + return UFix64(n), nil +} + func (UFix64) isValue() {} func (UFix64) Type() Type { @@ -1862,7 +1872,7 @@ func NewPathLink(targetPath Path, borrowType string) PathLink { } } -func NewMeteredLink(gauge common.MemoryGauge, targetPath Path, borrowType string) PathLink { +func NewMeteredPathLink(gauge common.MemoryGauge, targetPath Path, borrowType string) PathLink { common.UseMemory(gauge, common.CadencePathLinkValueMemoryUsage) return NewPathLink(targetPath, borrowType) } @@ -1888,31 +1898,77 @@ func (v PathLink) String() string { ) } +// AccountLink + +type AccountLink struct{} + +var _ Value = AccountLink{} + +func NewAccountLink() AccountLink { + return AccountLink{} +} + +func NewMeteredAccountLink(gauge common.MemoryGauge) AccountLink { + common.UseMemory(gauge, common.CadenceAccountLinkValueMemoryUsage) + return NewAccountLink() +} + +func (AccountLink) isValue() {} + +func (v AccountLink) Type() Type { + return nil +} + +func (v AccountLink) MeteredType(_ common.MemoryGauge) Type { + return v.Type() +} + +func (v AccountLink) ToGoValue() any { + return nil +} + +func (v AccountLink) String() string { + return format.AccountLink +} + // Path type Path struct { - Domain string + Domain common.PathDomain Identifier string } var _ Value = Path{} -func NewPath(domain, identifier string) Path { +func NewPath(domain common.PathDomain, identifier string) (Path, error) { + if domain == common.PathDomainUnknown { + return Path{}, errors.NewDefaultUserError("unknown domain in path") + } + return Path{ Domain: domain, Identifier: identifier, - } + }, nil } -func NewMeteredPath(gauge common.MemoryGauge, domain, identifier string) Path { +func NewMeteredPath(gauge common.MemoryGauge, domain common.PathDomain, identifier string) (Path, error) { common.UseMemory(gauge, common.CadencePathValueMemoryUsage) return NewPath(domain, identifier) } func (Path) isValue() {} -func (Path) Type() Type { - return ThePathType +func (v Path) Type() Type { + switch v.Domain { + case common.PathDomainStorage: + return TheStoragePathType + case common.PathDomainPrivate: + return ThePrivatePathType + case common.PathDomainPublic: + return ThePublicPathType + } + + panic(errors.NewUnreachableError()) } func (v Path) MeteredType(common.MemoryGauge) Type { @@ -1925,7 +1981,7 @@ func (Path) ToGoValue() any { func (v Path) String() string { return format.Path( - v.Domain, + v.Domain.Identifier(), v.Identifier, ) } diff --git a/values_test.go b/values_test.go index 85080e7eb7..02faa7c3c9 100644 --- a/values_test.go +++ b/values_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/sema" "github.com/onflow/cadence/runtime/tests/utils" ) @@ -45,8 +46,8 @@ func newValueTestCases() map[string]valueTestCase { fix64, _ := NewFix64("-32.11") testFunctionType := NewFunctionType( - "((String):UInt8)", FunctionPurityUnspecified, + nil, []Parameter{ { Type: StringType{}, @@ -331,10 +332,10 @@ func newValueTestCases() map[string]valueTestCase { }, string: "S.test.FooAttachment(bar: 1)", }, - "Link": { + "PathLink": { value: NewPathLink( Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, "Int", @@ -342,14 +343,35 @@ func newValueTestCases() map[string]valueTestCase { string: "PathLink(/storage/foo)", noType: true, }, - "Path": { + "AccountLink": { + value: NewAccountLink(), + string: "AccountLink()", + noType: true, + }, + "StoragePath": { value: Path{ - Domain: "storage", + Domain: common.PathDomainStorage, Identifier: "foo", }, - expectedType: PathType{}, + expectedType: TheStoragePathType, string: "/storage/foo", }, + "PrivatePath": { + value: Path{ + Domain: common.PathDomainPrivate, + Identifier: "foo", + }, + expectedType: ThePrivatePathType, + string: "/private/foo", + }, + "PublicPath": { + value: Path{ + Domain: common.PathDomainPublic, + Identifier: "foo", + }, + expectedType: ThePublicPathType, + string: "/public/foo", + }, "Type": { value: TypeValue{StaticType: IntType{}}, expectedType: NewMetaType(), @@ -357,7 +379,10 @@ func newValueTestCases() map[string]valueTestCase { }, "Capability": { value: StorageCapability{ - Path: Path{Domain: "storage", Identifier: "foo"}, + Path: Path{ + Domain: common.PathDomainStorage, + Identifier: "foo", + }, Address: BytesToAddress([]byte{1, 2, 3, 4, 5}), BorrowType: IntType{}, }, @@ -785,10 +810,12 @@ func TestValue_Type(t *testing.T) { require.Equal(t, exampleType, returnedType) } - // Check if the type is not a duplicate of some other type - // i.e: two values can't return the same type. - require.NotContains(t, checkedTypes, returnedType) - checkedTypes[returnedType] = struct{}{} + if !testCase.noType { + // Check if the type is not a duplicate of some other type + // i.e: two values can't return the same type. + require.NotContains(t, checkedTypes, returnedType) + checkedTypes[returnedType] = struct{}{} + } }) } diff --git a/version.go b/version.go index dfa632ff30..d1f72bb1fa 100644 --- a/version.go +++ b/version.go @@ -21,4 +21,4 @@ package cadence -const Version = "v0.37.0" +const Version = "v0.38.0" diff --git a/vm/vm.go b/vm/vm.go index 1144056b59..f96cadc8d0 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -27,7 +27,7 @@ import ( "C" - "github.com/bytecodealliance/wasmtime-go" + "github.com/bytecodealliance/wasmtime-go/v7" "github.com/onflow/cadence/runtime/interpreter" )