# Confetti - Cosmos NFT Engine This document describes as extensively as possible the design of an NFT-centric blockchain built on the Cosmos network. We code-name this blockchain **confetti** (**co**smos **nft** **e**ng**i**ne), and we will use this name to refer to the blockchain throughout the document. We organize this document in the following sections: * **Tokenomics**: describes tokens involved, purpose and mechanics, and potential initial distributions * **Data structures**: defines data structures in detail, providing also rules for validation and relations among different data structures * **Transactions**: provides a detailed account of state-machine transitions, i.e. actions that can be performed on-chain, including pseudo code * **Queries**: discusses which queries should be added, with the intent of providing a complete set of queries that can comprehensively reconstruct the entire blockchain state * **CEL**: a formal definition of the [CEL](https://github.com/google/cel-go/) environment employed in recipe executions * **Randomness**: we formally address the problem of generating deterministic randomness on-chain for computing recipe outcomes * **Actors and incentives**: lists potential actors and driving motivations and their impact on the blockchain with the current design ## Tokenomics When adding tokens we should also make sure proper [`DenomMetadata`](https://github.com/cosmos/cosmos-sdk/blob/master/x/bank/keeper/keeper.go#L266) is set using `x/bank`. ### FooToken The utility token, used to pay for gas fees. Partially pre-minted, no cap Inflation Mechanic: rng-game reward mechanics ### BarToken The staking and governance token. Partially pre-minted, no cap Inflation Mechanic: staking reward mechanics ## Data Structures At the center of the chain we find *Non-Fungible Tokens* (NFT). On Ethereum [ERC-721](https://ethereum.org/en/developers/docs/standards/tokens/erc-721/) tokens are identified by a `uint256` value called `tokenId`. The responsibility of giving meaning to a `tokenId` resides entirely on the dApp's shoulders, as they need to know in advance which `tokenId` represents *what*. We instead propose an approach where meaningful data of an NFT is encoded within the NFT itself, but how this data is encoded is up to the NFT creator, who chooses the format that best suits her needs. This means that NFTs that follow the same encoding rules for their data can be seen as belonging to the same *kind*, and the same static code can be used on these NFTs to decode their content for use or visualization, so that the `tokenId` does not need to be known in advance. Moreover, we allow NFTs not only to be created, but to also be *modified*, both in terms of simple personalizations for the NFT owner, as well as the ability to programmatically alter the NFT content for the NFT creator. ### NFT It should be owned by an account, same as coins. This means we need to do the same as `x/bank`, and mirror the [setBalance](https://github.com/cosmos/cosmos-sdk/blob/master/x/bank/keeper/send.go#L271) function in particular (and everything else around `SendCoins`). * `tokenId sdk.Int`: uniquely identifies NFTs. The maximum bitsize of the `sdk.Int` type is 256, providing an equavalent "address space size" as given by ERC-721. NFT structs are stored as a *starport list*, and when they are minted a new id is simply generated by the current counter. The `id` value should be represented externally in hex notation so it can be shortened. When created, it is also added to the `Account` and `CookbookNFTs` KVStores. * `cookbookId sdk.Int`: identifies the scope of this NFT, i.e. the cookbook within which it was generated and/or modified. An NFT can only belong to a single cookbook. * `owner sdk.AccAddr`: identifies the owner. Should not be stored as a string, if Bech32 prefix is changed in the app (a chain upgrade perhaps) the string would not be anymore valid. We should always use and store accounts as `sdk.AccAddr`. When responding to a query the stored `sdk.AccAddr` value would be converted to a string `owner.String()`. This approach mirrors best practices seen in Cosmos SDK modules such as [x/bank](https://github.com/cosmos/cosmos-sdk/blob/master/x/bank/keeper/view.go#L236) and [x/gov](https://github.com/cosmos/cosmos-sdk/blob/master/x/gov/keeper/deposit.go#L33). * `doubles, longs, strings []{key: string, value: sdk.Dec, sdk.Int, string}`: these fields define the NFT encoded data that characterizes it. They can only be generated/edited by recipes executions. * `ownerStrings []{key, value: string}`: an owner should be allowed to personalize their NFT if they wanted, or rather the NFT creator should be able to offer personalization opportunities on NFTs, but this personalization should be sandboxed and separated from other NFT data. Any (and only) `key` in this list can be assigned an arbitrary `value` from the NFT owner. They are first generated at minting by a recipe execution, but cannot be further modified by recipes. Successive recipe executions can only add new `key`s and set their default `value` there, but cannot reset existing `key`s. NFT owner can only edit values for existing `key`s though, and not add new key-value pairs. * `locked bool`: indicates whether this item can be traded or transferred. Can only be set by a recipe execution. Experience designers (Recipe designers) are given the freedom to dictate functionality ("tradeability" just being one example of which) of the NFTs that are generated. * `baseTransferPrice sdk.Int`: since we need to allow for trade-less transfers (no counterpart), we also need to account for the possibility that NFT creators don’t want to allow free transfers on-chain of their items (could entail external payments i.e. bypassing of confetti for transfer of value, which would hurt the NFT creator that does not get her share). This value defines the minimum price that the NFT creator is willing to accept to allow a transfer of this NFT. When an NFT is transferred outside of a trade, this amount is paid by the sender to make the transfer. When an NFT is transferred as part of a trade the amount to be paid to the NFT creator is calculated as the `max` between `baseTransferPrice` and the amount calculated from `tradePercentage`. * `tradePercentage sdk.Dec`: a number in the range \[0, `Params.MaxTradePercentage`\] used to compute the percentage held by the NFT creator(s) as fees when a trade happens. The computed amount cannot be lower than `baseTransferPrice` though, which is a strict minimum and is used instead of the computed amount whenever this happens. * ~~`cookbookOwnersCoinsPercentages []sdk.Dec`: same as `Recipe.cookbookOwnersCoinsPercentages []sdk.Dec` - see there for more info~~ ### Cookbook * `id sdk.Int`: uniquely identifies cookbook. It is assigned by the keeper, as cookbooks are stored as a starport list. When created, it is also added to the `Account` KVStore. * `owners []sdk.AccAddr`: defines the cookbook owners. Can be more than one account, and in such a case any cookbook or recipe CRUD transaction should be signed by every owner, so that the cookbook in its entirety cannot be updated (enlarged, modified) unless every owner agrees. The order in which addresses are added to this slice is important, as it relates to ~~`Recipe.cookbookOwnersCoinsPercentages` and `Nft.cookbookOwnersCoinsPercentages`~~ `Cookbook.percentages`. * `name, description, URI(optional) string`: these are self-explanatory. * `enabled bool`: if set to false, any ~~trade involving NFTs in this cookbook or~~ recipe creation or execution in this cookbook is disabled. * `percentages []sdk.Dec`: indicates the percentages used to split coins amounts among cookbook owners. Percentages are required to add up to a value less than or equal to 1, and the length of this slice cannot be greater than the length of `Cookbook.owners`. Any entry `i` in `Cookbook.owners[i]` that does not have a corresponding percentage at the same index (meaning that `len(Cookbook.percentages) <= i`) or is assigned a negative value, takes an equal part of the remainder of coin amounts once explicitly defined percentages are subtracted. Conventionally, the negative value that should be used to identify owners added to the equal-repartition pool is `-1`, and any negative value passed as input is sanitized to `-1` when this recipe is stored. This field should be carefully defined by cookbook owners, and kept in sync with `Cookbook.owners` because it could lead to undesired repartition logic. (When updating `Cookbook.owners` we might want to require a new `Cookbook.percentages` to be provided alongside it). This setup will enable the creation of multi-owned cookbooks where revenue is split among owners according to an agreed policy. A concrete example might be NFT art where a publisher and artist would be the two owners of the cookbook (and recipe(s) that generate the NFTs) and would split earnings based on their agreed-upon percentages. ### Recipe * `id sdk.Int`: uniquely identifies recipe. It is assigned by the keeper, as recipes are stored as a starport list. When created, it is also added to the `CookbookRecipes` KVStore. * `cookbookId sdk.Int`: identifies the cookbook that includes this recipe * `name, description, extra string`: these are self-explanatory. * `enabled bool`: if set to false, attempts to execute this recipe would fail. Can only be set by multi-sig transactions of `Cookbook.owners`. * `nodeVersion string`: indicates the node binary semver when this recipe has been added to the store. Could be used in the future to disable recipes that include deprecated *Cel* code, and initially it won't be used. It is set by the node upon creation of a recipe, and cannot be set by the recipe creator(s) nor be updated later. * `signer sdk.AccAddr`: indicates which signer need to sign for recipe executions. It is not required to be a member of `Cookbook.owners`, this duty can be delegated (and re-delegated). If empty, indicates that this recipe can be run without requiring any signature except the execution creator’s signature, hence can be run asynchronously (i.e. no `endRecipe` is required). Cannot be empty if there are more than 1 outcomes or other RNG is required, and should return an error if set to be true in such a case. * ~~`signerCoinsPercentages sdk.Dec`: indicates the percentage of non-cookbook `coins` that are sent to the signer. This value represents an absolute percentage and is deducted first from `coins`. Cannot be less than `Param.minExecutionSignerPercentage` and this value takes precedence if greater than `signerCoinsPercentage`. Must be a value in \[0, 1).~~ * ~~`cookbookOwnersCoinsPercentages []sdk.Dec`: indicates the relative percentages used to split coins amounts among cookbook owners. It indicates how the remainder of `coins`, after `signerCoinsPercentages` is taken, is distributed. Percentages are required to add up to a value less or equal to 1, and the length of this slice cannot be greater than the length of `Cookbook.owners`. If this happens because `Cookbook.owners` is shrunk as result of an update, excess percentages are ignored, and the value that they represent is then added to the remainder of `coins` that will be equally split among actual `Cookbook.owners`(everyone except whoever has an assigned percentage in this list). Any entry in `Cookbook.owners` that does not have a corresponding percentage at the same index, takes an equal part of the remainder of `coins` once defined percentages are taken. A negative value at index `i` means that `Cookbook.owners[i]` will be added to the list of owners that will receive a part of the final equal split, instead of a specific percentage. Conventionally, the negative value that should be used is `-1`, and any negative value passed as input is transformed to `-1` when this recipe is stored. This field should be carefully defined by cookbook owners, and kept in sync with `Cookbook.owners` because it could lead to undesired repartition logic especially if `Cookbook.owners` is reduced in size after an update.~~ * `coins sdk.Coins`: defines a combination of cookbook and non-cookbook coins that are required as input. Cookbook-owned coins need to be indicated as such in their `coins[i].Denom` either by using the full denom including the `cookbookId` or written as `cookbook/<denom>`. If found, the prefix `cookbook/` in denoms will be resolved automatically to the proper `cookbookId` (the actual prefix in the stored is `cb-<cookbookId>`). The coins belonging to the same `cookbookId` as this recipe need to be recognized and separated from other coins, as they will be burned (to close the mint/burn cycle) and not transferred. The burning is strictly enforced and cannot be circumvented, to counterbalance the minting allowed for `outputs.coins`. Any coin can be set as input here, not just `fooToken` or cookbook coins, but also `barToken` as well as any other coin, even the ones minted by different cookbooks. * `nfts []MatchNft`: a list of NFT-matching conditions to lock NFTs required as input for a recipe execution. * `MatchNft`: … [TODO] * `outputs Outputs`: a struct that defines all building blocks to construct possible outcomes. When storing a recipe, any id in `outputs` not referenced in `outcomes` is discarded, to save space. * `Outputs {coins []MintCoin, modify []ModifyNft, mint []MintNft}` * `MintCoin {id string, coin sdk.Coin}`: defines a coin minting outcome. When minted, the actual denom will be constructed as `cb-<cookbookId>/<MintCoin.coin.Denom>` and the new denom, if it does not exist yet, will be added to the `CookbookCoins` KVStore. * `ModifyNft`: … [TODO] * `MintNft`: … [TODO] * `outcomes []{outputIds []string, weight uint64}`: defines a collection of valid ids included in `outputs`, and a weight for the outcome. Weight values are equally as important as the order in which these outcomes are defined. The actual outcome is computed by summing all weights, and generating a random number between 0 and the total sum. The weights define a set of intervals \[0, w1) for outcome 1, \[w1, w2) for outcome 2, … that are then used to assess which outcome to select based on the generated value. E.g.: there are two outcomes with w1 as 2 and w2 as 10. The total sum of weights is 12, meaning that a random number in the range \[0, 12) is generated. This random number is then used to select the right outcome by checking if it belongs to \[0,2) or \[2,12). Say the random number is 4, then in this case it belongs to \[2,12) so outcome 2 is selected. ### ~~Execution~~ As long as we emit events properly (en example [here](https://github.com/cosmos/cosmos-sdk/blob/master/x/authz/keeper/keeper.go#L144)) for executions and their outcomes, we don’t need to store this data. We leave the job of indexing data for complex queries to a block explorer. ### Epoch It is a list of simple structs that keep record of how many rng-games happened and `fooToken`s minted as a result. It is stored to keep a metric of inflation and allow the automatic adjustment. * `id sdk.Int`: uniquely identifies Epochs. Epoch structs are stored as a *starport list*, and when a new one is added its id is simply generated by the current counter. * `blockHeight uint64`: indicates the block height at which this epoch is finalized. * `totalMinted uint64`: indicates the total amount of `fooToken` minted during this epoch. If the referenced epoch is in the past, this value cannot be updated anymore * `totalChallenges uint64`: indicates the total amount of RNG-games happened during this epoch. If the referenced epoch is in the past, this value cannot be updated anymore ### PendingExecution when an execution request is submitted, we need to store a record of this happening so that the RNG-game dynamics can be implemented. We need to keep track of what NFTs are involved (and have been locked), information on coins involved (the amounts have also been locked), the `blockHeight` at which this pendingExecution gets dropped (computed as `headBlockHeight` + `Params.executionBlockInterval` when the pending execution is added to the store), the two `secret`s used for computing the seed, and which recipe is getting executed. This data structure is the only one fow which it makes sense to delete entries from, both when an execution is dropped or when it gets finalized. Since this data structs are not meant to be permanently stored, it may make sense to use in-memory only storage of them (if possible). * `id sdk.Int`: uniquely identifies `PendingExecution`. Epoch structs are stored as a *starport list*, and when a new one is added its id is simply generated by an ever incrementing counter. Pending executions are considered volatile and are removed once the execution is either dropped or finalized. The internal counter is always increasing even if pending executions are removed from the store, so that the logic for handling id generation is kept simple and no id collision is created even on transient data such as pending executions. * `dropAtBlockHeight uint64`: indicates the block height at which this execution is dropped, unless finalized earlier. * `coins sdk.Coins`: collection of coins that have been locked and will be used to pay when the execution is finalized. * `nfts []string`: collection of `tokenId`s that have been locked for this execution * `txHash []byte`: the hash of the transaction submitted to request the recipe execution * `creator sdk.AccAddr`: the address that requested the execution * `creatorSecret string`: encrypted secret number provided by `creator` * `signer sdk.AccAddr`: the address of the `Recipe.signer` * `signerSecret string`: encrypted secret number provided by `signer` ### Other KVStores In order to simplify querying and handling of large interconnected data structures, we intend to partially replicate data internally to retain O(1) search complexity. To this end, we propose the following additional structures * `Account`: same entity used by `x/bank` and handled by the `accountKeeper`. Holds information on which NFTs and Cookbooks are held by the account, i.e. ~~has two sub-prefixes *NFT* and *Cookbook* and owned elements are stored id-only (`tokenId` and `cookbookId`) under these sub-prefixes.~~ ~~Also holds a history of executions launched, with a sub-prefix *Executions* that stores a list of `executionId`~~ two physical KVStores are created `AddressNfts` and `AddressCookbooks` that maps an address to the set of owned `tokenId`s and `cookbookId`s. * `CookbookRecipes`: maps all `recipeId` belonging to a `cookbookId` * `CookbookCoins`: maps `Coin.Denoms` belonging to a `cookbookId` * `CookbookNfts`: maps all `tokenId` belonging to a `cookbookId` * ~~`RecipeNfts`: maps all `tokenId` touched by a `recipeId`, indicating which NFTs this recipe created or modified~~ * ~~`NftExecutions`: maps all `executionId` involving `tokenId`~~ * ~~`RecipeExecutions`: maps all `executionId` generated from `recipeId`~~ * ~~`NftTrades`: ...~~ ### Parameters * `maxTradePercentage sdk.Dec`: maximum allowed trade percentage, a number in the range \[0, 1\] * ~~`minExecutionSignerPercentage sdk.Dec`: minimum allowed percentage to be retained to pay an execution signer. - 10%, a number in the range \[0, 1)~~ * `fooTokenRewardEpoch uint64`: defines the amount of blocks to include in each epoch * `fooTokenRewardAmount sdk.Int`: defines the maximum `fooToken`s that can be rewarded for winning a random-seed generation game in the current epoch. This value is adjusted algorithmically at each epoch, defined by `Params.fooTokenRewardEpoch`, and essentially bounds the reward to a maximum value that cannot be exceeded even if gas fees paid are really high. * `fooTokenRewardAsGasFeePercentage sdk.Dec`: defines the percentage of `fooToken`s total gas fees payed (for both transactions needed) that is rewarded for winning a random-seed generation game in the current epoch. This value is set by governance and can only be in the range \[0, 1) * ~~`fooTokenRewardLoserPercentage sdk.Int`: defines what percentage (i.e. a number in \[0,1)) is assigned to the loser of the game from `Params.fooTokenRewardAmount`. It should be a small value~~ * `executionBlockInterval uint64` defines the timeout to wait before discarding a pending execution, unless a `endRecipe` transaction is sent where secret `key`s are revealed so the random seed can be generated. ~~* `targetFooTokenInflationTolerance sdk.Dec`: see below~~ * `targetFooTokenInflationAmount uint64`: defines a target amount of `fooToken` to be rewarded in total at each epoch (interval of blocks). At the end of each epoch ~~if total `fooToken`s minted misses this target (with a certain tolerance provided by `Params.targetFooTokenInflationTolerance`) then~~ the `Params.fooTokenRewardAmount` is adjusted so that the projected minted amount is as close as possible to `Params.targetFooTokenRewardsEpoch`. To perform the projection and adjust `Params.fooTokenRewardAmount`, we keep track of total rng-games played and total amount of `fooToken` minted via `Epoch`s. ## Transactions ## Queries Queries should be simple and light, and have very little complexity in their logic. For this reason, we only list them and briefly comment why they would be useful. Queries that would require iterating over the store can become quite heavy over time,\ hence any query that cannot be solved in O(1) directly by key are discarded (but still potentially mentioned in **discarded queries**). Moreover, we intentionally discard queries that are better suited for a block explorer (the one intentionally designed to reconstruct the blockchain history, since we don’t store history data and rather we emit events when executing transactions). ### Valid Queries * **List NFTs by address**: to reconstruct wallet holdings * **List NFTs by cookbook**: this could be useful to retrieve any nft that was generated by a cookbook * **List cookbooks by address**: gets all cookbook belonging to the given address * **List coins by cookbook**: retrieves a list of coins that are minted and burned in this cookbook. * **List recipes by cookbook**: gets all recipes belonging to a cookbook * **GetById** for basic data structures (*starport lists* as defined in the data structures section) ### Discarded queries * **List executions by address**: to reconstruct account history. Executions are transactions, so filtering over transactions will achieve the same result, this is better suited * **List NFT string edit**: to reconstruct item history. This would require adding a data structure to hold nft previous states, which is undesirable. Better suited on a block explorer (filter tx by item and you get its history). As long as we emit events properly, we can safely disregard keeping track of history on-chain * **List NFTs by recipe**: this could be useful to retrieve any nft that was either minted or modified by a recipe * **List executions by recipe**: can be used to reconstruct a recipe history. * **List executions by NFT**: can be used to reconstruct a nft history ## Cel ## Randomness Whenever there is need for random number generation when a recipe is executed, a problem arises: how do we deterministically generate random numbers making it *hard* to cheat? The problem can be reduced to how the random *seed* - used to generate the sequence of random numbers required for a recipe execution - is "extracted" and how robust to cheating is this process. If this random seed can be determined algorithmically as a result of activity on confetti, then the random numbers sequence generated by this seed is safe to use as it is deterministically defined. To tackle this problem, we require recipe executions requests where RNG is involved to be multi-sig transactions where both recipe-execution requester and a recipe-execution authorized-signer need to sign for the execution to happen. Execution requests can be signed by these two in any order, with the first being the payer of gast fees *G(Tx)*, as standard for the Cosmos-SDK. Then the seed generation can be algorithmically defined by the outcome of a game played by these two actors. Of course, if seed generation is designed as a game with two players, their preferred outcome should be different. If preferred outcomes are aligned, then they would be incentivized to collude/cooperate, while we want to force non-cooperation. Therefore, we design a game whose purpose is not to generate the random seed, but to mint and receive `fooToken`s, and is a *0-sum game*, meaning that only one player can win the game and get the reward. ### Game Specs The objective of this game for each participant *A_{1,2}* is to maximize their total payoff *P_{1,2}* given as the summation of the payoff of each mini-game *p_{1,2}^i* that will be played (we will get to that shortly). In case the two payoffs *P_1* and *P_2* are equal, no one wins and no reward is given. For a participant to win she needs her payoff to be strictly greater than the one of the adversary. To enter the game each participant submits a number *N_{1,2}* made of *m* bits. A sequence of *m* sub-games is then played using these 2 numbers, one sub-game for each bit *n^i* at position *i* in *\[0, m)*. Both players play simoultaneously, having committed to the entire number *N* (i.e. the set of actions chosen for each sub-game) beforehand. Each sub-game is played using the bits *n_{1,2}^i*. A bit can only be in {0,1}, and the payoff is structured as follows 1. If *A_1* selects 0 and *A_2* selects 0, *p_1^i=0* and *p_2^i=1*, *A_2* wins 2. If *A_1* selects 0 and *A_2* selects 1, *p_1^i=1* and *p_2^i=0*, *A_1* wins 3. If *A_1* selects 1 and *A_2* selects 0, *p_1^i=1* and *p_2^i=0*, *A_1* wins 4. If *A_1* selects 1 and *A_2* selects 1, *p_1^i=0* and *p_2^i=1*, *A_2* wins Or in its matrix representation: | | **A_2: 0** | **A_2: 1** | |--|--|--| | **A_1: 0** | 0,1 | 1,0 | | **A_1: 1** | 1,0 | 0,1 | This simple game is proven to have no pure Nash equilibrium, and the only mixed equilibrium is when each agent chose actions uniformly at random (this is discussed in many places, an example [here](http://speezepearson.github.io/intro-to-game-theory/2-fast.html)). Indeed, if any of the agents can predict how the other will play, they can devise a strategy that makes them always win. Therefore, assuming agents are rational, each agent has no incentive to apply any other strategy that the one that makes them the most unpredictable, and try to win by chance. Such a game over *N_1* and *N_2* can be simply implemented as the *exclusive or* (XOR) of these two numbers. ### Outcome The outcome of playing these sub-games on *N_{1,2}* is a sequence of sub-game outcomes *o^i* where *o^i* is 0 if at sub-game *i* participant *A_1* wins, and 1 if participant *A_2* wins. Since both players' best strategy is to provide uniformly random *N*, the outcome of each sub-game *o^i* is also uniformly random as a result. Therefore, the number *O* obtained by using each outcome *o^i* as bit in position *i*, will also be random. To assign the `fooToken` reward, we compare *P_1* and *P_2* so that: * If *P_1* > *P_2*, reward is assigned to *A_1* * If *P_1* > *P_2*, reward is assigned to *A_2* * If *P_1* = *P_2*, no reward is assigned The reward will be always less than the gas fees payed *G(Tx)*. If this was not true, artificially creating recipe execution transactions could become profitable and potentially suppress truthful transactions due to the volume of artificial transactions that would be generated. The reward is computes as a percentage of total gas fees instead *G(TX)*\*`Param.fooTokenRewardAsGasFeePercentage` (total gas fees spent between all transactions needed to finalize a recipe execution). While the second transaction signer (who does not pay fees) might have an incentive to artificially create transactions to enter the possibility of winning the reward "for free", this is not true for the first signer, and this is enough since the transaction cannot happen if both parties don't sign. This reward logic forces agents to provide honestly-generated random numbers if they are really incentivized to obtain the reward. ### Random seed construction and game dynamics The actual seed is obtained by deterministically salting *O* with the `HeadBlockHash`, a [32 bytes number](https://docs.tendermint.com/master/spec/core/data_structures.html) . This introduces a second-layer level of security for the deterministic seed generation: the two agents *A_1* and *A_2* would need to collude and also be able to control/predict what will be the `HeadBlockHash` when the `endRecipe` transaction is committed. … [TODO: detail salting algorithm] At recipe execution submission (`startRecipe`), the two participants sign the transaction also providing a `secret`, that corresponds to the number *N* they have generated, encrypted with a use-once encryption key, generated on the fly and kept secret at this stage. With this step, the two participants commit to their numbers without the ability of knowing the other’s choice. After the submission is accepted, they have to send a second multi-signed transaction (`endRecipe`) to finalize execution, where they reveal the `key` to decrypt their secret. They are required to send this transaction within `Params.executionBlockInterval` blocks after `startRecipe` is submitted, or this execution will be dropped. Let's briefly discuss the scenario in which both parties agree on colluding, regardless of the system put in place to disincentivize it, and are able to control at which block the execution is finalized, say by paying very high gas fees. Why do this in the first place? To scam other people into thinking everyone gets a fair chance while instead someone is getting prioritized. If controlling the outcome of a recipe is more important than the potential reward, they would certainly collude and don't care about the reward. ### Another way? Whether disincentivized or not, a system for generating the random seed in which either directly or indirectly only the two parties of a recipe execution (recipe execution requester and signer) are involved, is subject to cheating. We need to introduce an unpredictable source of "entropy", something not necessarily random and that can very hardly be controlled without the entire blockchain being compromised. The BFT consensus implemented by Tendermint implies validators are going to **sign** to vote on blocks. In particular, the [`ctx.BlockHeader().LastCommitHash`](https://docs.tendermint.com/master/spec/core/data_structures.html#header) contains a hash of the last block [`Block.LastCommit`](https://docs.tendermint.com/master/spec/core/data_structures.html#block) which includes an array of [`Signatures`](https://docs.tendermint.com/master/spec/core/data_structures.html#commit). These signatures cannot be predicted even if the signed message is public, unless the validators private key is known, all of them to be precise. The possibility of all of them colluding would violate the BFT consensus in the first place and is therefore disregarded as a possibility. So, we can assume this field to be hardly controllable by any attacker, and to be hence a good source of unpredictability. If we develop the game to be around this hash (a post-recipe-exec-request value of it), then neither party could fully control the seed generation hence have no incentive to try to collude. Let's assume the basic operation to combine numbers is the *exclusive or* (XOR), it is provable that if either one of the numbers involved in the xor has a sufficiently high entropy, [that is preserved](https://crypto.stackexchange.com/questions/17658/mixing-entropy-sources-by-xor) when xor-ing with any other arbitrary number. At recipe execution request, message signers submit a *bet* number in plain text made of m bits. At the *beginBlock* of the next block, the first *m* bits of the *lastCommitHash* are xor-ed with *B_1*, and the second *m* bits are xor-ed with *B_2*. The two results are then xor-ed together to obtain the final *m* bit number that will be used as seed. We then count the number of 1s *X* and if *X > m/2*, *B_2* is the winning bet, if *X < m/2*, *B_1* is the winning bet, and if *X = m/2*, no-one wins. Such a game would require so many parts of confetti to be compromised that is considered safe and robust enough to generate a high-entropy seed, one different for every recipe execution. This is because predicting any subset of *m* bits of *lastCommitHash* is extremely hard and the best strategy for any player to have a chance to win is to provide a bet *B* where bits are generated randomly. One could ask: why not use *lastCommitHash* directly? Although *lastCommitHash* is highly unpredictable, the higher the entropy of the seed provided to the RNG algorithm, the better the quality of the random sequence generated. To increase the entropy of the seed and generate a separate, verifiable seed for every recipe execution, we involve directly the users that are requesting the recipe execution and reward them for their participation. ## Actors and Incentives