Mental poker for turn-based strategy games – securing fairness without a trusted third party

Stian Aleksander Ellingsen

15-12-2016 Master’s Thesis Master of Science in Applied Computer Science 30 ECTS Department of Computer Science and Media Technology Norwegian University of Science and Technology,

Supervisor: Mariusz Nowostawski Abstract

Strategy video games typically provide players with only partial information about the game world, hiding everything outside the range of vision of each player’s units. A third party is typically trusted with running the game and lim- iting the information given to players. A player colluding with the third party could gain a huge unfair advantage by accessing more information than allowed by the rules, and such cheating is generally undetectable. Between players who don’t trust a common third party, a different solution is needed. The same problem arises for card games when implemented over a computer network in a provably fair way without a trusted third party. In card games, this problem is known as mental poker, and a solution to this problem was first suggested in 1981. While turn-based strategy games and card games have some important elements in common, the methods provided in mental poker solutions do not translate directly to turn-based strategy games. Some work has been done on hiding player units in strategy games without a trusted third party, but not completely without unwanted information exposure. There is also previous work on securely generating maps for strategy games with- out a third party, but not in a way that hides unexplored terrain. This project applies the problem of mental poker to provide a protocol for playing a turn-based strategy game over a network, with computational require- ments that can be met by a normal computer. This includes securely generating random maps, hiding unexplored terrain and units with negligible information exposure during gameplay, while dissuading attempts at cheating or disruption. Mental poker for turn-based strategy games – securing fairness without a trusted third party

Contents

Contents ...... i 1 Introduction ...... 1 1.1 Problem description ...... 1 1.2 Threat model and principles ...... 2 2 Related work ...... 3 2.1 Fair play ...... 3 2.2 Obtaining randomness ...... 4 2.3 Map generation ...... 5 2.4 Consequences of the game outcome ...... 5 3 Protocol ...... 7 3.1 Game initialisation ...... 7 3.1.1 Game parametres ...... 7 3.1.2 Private keys ...... 8 3.1.3 Contract ...... 8 3.1.4 Modulus ...... 9 3.1.5 The game state ...... 10 3.1.6 Initialisation of the game state ...... 10 3.2 Privately retrieving randomness ...... 10 3.3 Updating and retrieving encrypted game state ...... 11 3.4 Verification ...... 11 3.5 Ending the game ...... 11 3.6 Cipher ...... 12 4 Map generation ...... 14 4.1 Algorithms ...... 15 5 Security parametres ...... 19 6 Performance analysis ...... 20 6.1 Blockchain scripts ...... 21 7 Enforcing fair play ...... 23 8 Conclusion and future work ...... 25 A Blockchain details ...... 27 A.1 Features ...... 27 A.2 Objects ...... 27

i Mental poker for turn-based strategy games – securing fairness without a trusted third party

A.2.1 State ...... 27 A.3 Branches ...... 28 A.3.1 REVEALp ...... 28 A.3.2 GENERIC ...... 28 A.3.3 TIMEOUTp ...... 28 A.3.4 CHEATi,j,p,q ...... 28 A.4 Programs ...... 29 A.4.1 progMain ...... 29 A.4.2 progRevealp,q ...... 29 A.4.3 progWithStatei,p,q ...... 29 A.5 Transactions ...... 29 A.5.1 txFunding ...... 29 A.5.2 txResignp,q ...... 30 A.5.3 txRevealp,q ...... 30 A.5.4 txWithStatei,p,q ...... 30 A.5.5 txDefaultp,q ...... 30 B Source code ...... 32 B.1 Blockchain.hs ...... 32 B.2 Compile.hs ...... 42 B.3 Game.hs ...... 54 Glossary ...... 59 Bibliography ...... 62

ii Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 1

Introduction

The project deals with the problem of playing a turn-based strategy (TBS) game between two or more players over a network, in such a way that each player is able to ensure that the game is fair, without players having to trust each other or a common third party. This problem is greatly complicated when each player should be restricted to seeing only the parts of the game world that their own units can see, a typical feature in strategy games.

1.1 Problem description In strategy video games, players typically control a set of units placed in a game world shared with opponents. Depending on their type, units can be commanded to explore the world, to collect resources found in the world, or to combat against opponent units. Two main sub-genres are real-time strategy (RTS) games and TBS games, which mainly differ in the way timekeeping works. In RTS, time is continuous, while in TBS, players’ actions are grouped in turns. An important feature in many strategy video games is the effect referred to as fog of war: A player can only see the part of the game world that is within their unit’s line of sight [1]. However, the whole game state is usually available to the computer which is running the game logic, and may easily be accessed by the owner, which could be one (or all) of the players, or a third party which could be colluding with one of the players. This type of cheating may give a player a huge advantage over honest players and can’t be detected directly, if at all. Mental poker, introduced by Shamir, Rivest and Adleman [20], involves play- ing a game of cards at a distance, using only messages between the players. To ensure a fair method of playing mental poker, important properties of a physical equivalent of the game are preserved, such as ensuring a fair deal, being able to hide one’s own hand from other players, and making sure that two players can’t have the same card. This is made possible with the use of . For example, securely shuffling the deck can be done by having each player encrypt

1 Mental poker for turn-based strategy games – securing fairness without a trusted third party each card in the deck, shuffle the deck and then pass it to the next player. While mental poker is mostly concerned with card games [21], the same prob- lem can be applied to other types of games as well. The element of incomplete information is similar to fog of war in strategy games, and TBS games in partic- ular are played in turns like card games. Applying the problem of mental poker to TBS games prompts the need for a protocol for playing a TBS game over a network, fairly, and with negligible information exposure, enforcing fog of war on units as well as the rest of the game world. This project seeks to provide a practical solution to this, including methods to sufficiently dissuade attempts at cheating or disruption.

1.2 Threat model and principles Two players want to play a TBS game against each other over the Internet. They do not trust each other to play fairly, nor do they have a trusted third party (TTP) in common to run the game and decide on the outcome. Each player is assumed to play the game on a computer system that they fully control and trust. For the purpose of this project, cheating involves retrieving information that should not be accessible to the player or modifying the game state in a way that is not allowed by the game rules. Other actions that may be considered cheating, such as disrupting the network connection of the other player or using additional computer software to aid in or automate selection of game actions, are not considered in this project.

2 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 2

Related work

2.1 Fair play This thesis applies the problem of mental poker to turn-based strategy (TBS) games. There has been a lot of research on mental poker, but this has mostly focused on card games [21]. It is not obvious how to apply methods of shuffling and dealing cards to a TBS game. Nonetheless, the cryptographic primitives used for these games could be useful. The mental poker protocol given by Shamir, Rivest and Adleman [20] relies on a commutative encryption scheme, meaning that the order of encryption doesn’t matter when a message is doubly encrypted with different keys – a description of such a scheme is given by Pohlig and Hellman [18]. In a game between , Bob starts by encrypting and shuffling the cards, sending the deck to Alice. To obtain her hand, Alice takes a random set of cards, encrypts them and gets Bob to decrypt them. Alice then decrypts these. This way, Alice can’t control which cards she obtains, and Bob can’t know Alice’s hand. Goldreich, Micali and Wigderson [13] give a general solution for playing any mental game as long as the majority of the participants are honest. This is done by building circuits of logic gates to implement the whole game. The purpose of their paper is to prove that such a model exists, and efficiency is not a concern. However, for something as complex as a full TBS game, performance will quickly become an issue. Chambers et al. [9] give a method for detecting when a player has bypassed the fog of war to reveal opponents’ units and positions. The method works by exchanging bitmaps of the visible areas between players, and only sending infor- mation about the units that should be seen by each opponent. During the game, players commit to all their moves, and like in the protocol of Shamir, Rivest and Adleman [20], reveal their secrets and their commited moves at the end of the game for verification. While aimed at real-time strategy (RTS) games, the method can be trivially adopted to a TBS game. However, it does not hide player locations

3 Mental poker for turn-based strategy games – securing fairness without a trusted third party completely, as each player’s visible area is exposed to the opponents. Bursztein et al. [7] provide a better solution with respect to information ex- posure by using oblivious set intersection, which limits the exposure of a player’s visible area to the parts that overlap with each opponent’s units. A potentially useful optimisation is their notion of what they call "hypergrids" to reduce the number of cells that a visibility map must contain. While their method does not expose the visibility areas directly, it still leaks information about their sizes, as well as the number of units and which opponents can see each unit. To limit this information exposure, they propose using a chaff in the form of random, mean- ingless data and queries, but this still reveals an upper bound on the visible area and number of units, and there is no way to have a false negative on whether a unit is visible to an opponent. The methods presented in the aforementioned two papers are only aimed at hiding units. They are not able to hide unexplored terrain. Rice [19] proposes a method for fairly generating a map suitable for a TBS game in a peer-to-peer fashion. This method consists of deterministically gener- ating the whole map from a securely generated seed using procedural generation algorithms before gameplay begins. The paper mentions the concept of using fog of war to hide parts of the map from each player and generating the map as it is explored, but the proposed method does not cover this due to concerns about information exposure between players. However, cryptographic methods like those used in mental poker may possibly be used together with the concepts and adapted versions of the algorithms in this paper for map generation that implements fog of war.

2.2 Obtaining randomness In the peer-to-peer map generation protocol by Rice [19], players derive a seed value from a simple XOR between random values generated by each player. Fair- ness is secured by having each player commit to their random value by a signed hash, so that no player can influence the final seed to their advantage by ad- justing their own value. This method works well when all players should have shared knowledge about the generated map, but won’t work if only some of the players should have access to some seed. Diffie and Hellman [10] give a technique for deriving a shared secret between two parties, from random numbers that are sent in the clear. Their system, known as the Diffie–Hellman exchange, is based on modular exponentiation and relies on the commutative property of combining two exponents, i.e., (xa)b = (xb)a. Pohlig and Hellman [18] describe a cryptosystem based on exponentiation modulo a prime, where the exponent and its inverse are secret numbers used to encrypt and decrypt messages. This is known as the Pohlig–Hellman exponenti- ation cipher. The Diffie–Hellman key exchange may be considered a special case

4 Mental poker for turn-based strategy games – securing fairness without a trusted third party of this cryptosystem, where the key is derived by repeatedly encrypting a public number with each party’s secret exponent. Shamir, Rivest and Adleman [20] show how cards can be dealt in a fair way in a game of mental poker by using a cryptosystem with certain properties. In partic- ular, one of the requirements is the aforementioned commutativity of encryption. In their card dealing protocol, randomness is introduced by having the players shuffle and randomly select encrypted cards without being able to distinguish them. They give the Pohlig–Hellman cipher as an example of a cryptosystem that has the required properties. While the methods of shuffling are not directly ap- plicable to procedural generation, it is an interesting demonstration of how a commutative cipher may be used in a game with incomplete information.

2.3 Map generation In the procedural map generation algorithm by Rice [19], the map is built up by several layers, each of which determines some feature of the map. While the resource layer works on each map cell independently and can thus easily be adopted to a game with incomplete information, other layers rely on noise functions that may be problematic. The terrain height layer uses the Diamond-Square algorithm, which when given the variables necessary to calculate the value at some point, reveals the exact values at the edges of each grid cell surrounding that point at all the sub- division levels. The land/water layer requires a pass through the whole terrain layer to find the average height. This is not possible when only part of the map is accessible, so a different way of finding an appropriate land/water level is required. The trees/deserts layer uses Simplex noise, in which each point gets its value from the three surrounding grid points. This algorithm is well suited for local map generation with minimal exposure of the surrounding area. A potential problem with many noise algorithms is the use of inexact opera- tions, and the use of floating-point operations in general. Such operations could behave differently across platforms, causing players to generate slightly different maps from the same seed. Rice [19] addresses this issue by having the players compare hashes of their generated maps. However, such comparison is not possi- ble when players can only access parts of the map and should have no knowledge of which parts other players have access to.

2.4 Consequences of the game outcome While active cheating can easily be detected at the end of a game by using a and revealing the secret data at the end of the game as suggested in several papers [20, 9, 7], detection in itself is not necessarily enough to prevent cheating or other unwanted behaviour such as aborting the game. Chambers et al. [9] and Bursztein et al. [7] suggest the use of a central server

5 Mental poker for turn-based strategy games – securing fairness without a trusted third party which can verify the game and has the power to penalise cheaters somehow. This requires all players to trust a common third party to make fair decisions against them during the game. As with the ability to check that a game was played fairly, this project will attempt to get rid of the need for a single trusted third party (TTP) to decide the consequences of the game. Interfacing with a cryptocurrency such as Bitcoin could allow for a game to require players to make a deposit at the beginning of the game. At the end of the game, those who played by the rules would be refunded and receive a share of the deposits of any non-compliant players. Central to Bitcoin and its security is a public ledger of transactions (TXs), individually validated by every node on the network. This ledger is backed by a blockchain maintained by the network, where the difficulty of reverting the chain grows exponentially with the number of blocks to revert. [3] Implementing a protocol for playing a game entirely in Bitcoin is made possi- ble by scripted transactions, and has been done by Andrychowicz et al. [3] and Bentov and Kumaresan [4] in the form of lotteries. However, as these games en- code the entire set of rules and inputs in TXs on the blockchain, scalability quickly becomes a problem. Every single node on the Bitcoin network must simulate the game with all its rules and the inputs of players, in the form of transaction scripts, to verify the transactions. The language used in the transaction scripts is limited, and inefficient workarounds often have to be used [8]. A much more complex program like a TBS game would be impractical to simulate completely in the form of Bitcoin TXs, if at all possible. Butterin et al. [8] address the limitations of using Bitcoin to implement trans- actions with arbitrary rules. Their platform, Ethereum, uses a blockchain like Bitcoin, but with more complete scripting abilities. However, scalability is still an issue, as all game rules and inputs must still be encoded in the block chain and verified by every node in the network. A possible way of interfacing with Bitcoin with minimal trust would be to have a set of oracles each evaluate the outcome of a game and sign transactions based on this evaluation. This method would use a multi-signature scheme requiring a majority of the oracles to sign each transaction. Implementations of such oracles include Orisi and First Temple [16, 5]. This method does require trust in the majority of the oracles, but it is an improvement over a single TTP.

6 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 3

Protocol

The following proposed protocol requires a cipher with the following properties:

1.E k(x) is the of a message x under key k. 2.D k(Ek(x)) = x for all messages x and keys k. 3.E k(Ej(x)) = Ej(Ek(x)) for all messages x and keys j and k. 4. Given any number of messages xi and their corresponding Ek(H(xi)) and a message y, it is impossible to derive Ek(H(y)), for all k.

5. Given any messages x and y, it is impossible to find keys j and k such that Ej(x) = Ek(y). 6. Given messages x and y, it is impossible to distinguish Ek(x) and Ek(y). 7. Given message x, it is impossible to correlate Ek(x) and Ej(x) as long as k and j are not related. Properties 1 to 5 are identical to those required by Shamir, Rivest and Adle- man [20], except that their property 4 is replaced here by a stronger property. Properties 6 and 7 are necessary to ensure that a player can obtain randomness privately, i.e. without any other party learning anything about what the random- ness is for. In addition, the solution uses some function H(x) to convert x into something that can be encrypted by the cipher. A cipher with the above properties is given in section 3.6.

3.1 Game initialisation 3.1.1 Game parametres Before starting, the players agree on the specific game rules and key sizes. The values agreed upon include  l, the security level in bits, e.g. 64,  µ, the size of the modulus in bits, e.g. 768,

7 Mental poker for turn-based strategy games – securing fairness without a trusted third party

 the amount of funds that each player should lock into the contract. m should have a value closely matching the expected requirement for a se- curity level of s bits, as a value that is too low will decrease the overall security level, while a higher value will decrease performance without increasing the se- curity level. Key sizes are discussed in chapter 5. 3.1.2 Private keys The players generate the private keys that they will use for the cipher described in section 3.6:

 Alice selects a cryptographically secure random integer kA from the interval [1, 22s − 1],  Bob selects a cryptographically secure random integer kB from the interval [1, 22s − 1]. These keys will be revealed at the end of the game, either voluntarily or as required by the contract. 3.1.3 Contract The players now generate and sign the initial transactions (TXs) that make up the contract described in appendix A. For signing TXs that are part of the contract, the players use a different set of keys than the ones used in the cipher:

 Alice generates a private key dA and reveals the corresponding public key QA for use with the contract.  Bob generates a private key dB and reveals the corresponding public key QB for use with the contract. These keys do not necessarily have to be unique to a game session, but gener- ating new keypairs whenever possible improves privacy. To be able to know the address that the first TX, txFunding, should pay to, the players need each other’s blockchain public keys and the hashes of the two merklised abstract syntax tree (MAST) subscripts that provide kA and kB.

 Alice reveals PA = Hash256(xkkA), where x is the byte length of kA when serialised,  Bob reveals PB = Hash256(ykkB), where y is the byte length of kB when serialised. From this information, the players are each able to construct the MAST and the corresponding output of txFunding. Each player also needs to provide the inputs they want txFunding to redeem, as well as a change output if necessary. Each player  provides their inputs and change outputs,  verifies that the other player covers a fair amount of the contract output

8 Mental poker for turn-based strategy games – securing fairness without a trusted third party

plus any TX fee deemed necessary,  constructs an unsigned version of txFunding by arranging the inputs and outputs in a particular deterministic way. The players should typically provide equal amounts of funding, but a player that increases the TX size significantly by e.g. adding many inputs may have to cover a larger part of the TX fee. Before signing txFunding, each player needs to make sure that they can ter- minate the contract without losing their funds.  Alice signs txResignB,A, txRevealB,A and txDefaultB,A,  Bob signs txResignA,B, txRevealA,B and txDefaultA,B. These signatures use the hash type SINGLE, which means the other player can add other inputs and outputs to the TX, as long as the already signed input– output pair remains unchanged. Once the players have received the correctly generated and signed TXs, they can finally sign the txFunding. Each player  provides the signatures and other witness data necessary for txFunding to redeem their inputs,  broadcasts the fully signed TX to the network if necessary. 3.1.4 Modulus For reasons described in chapter 5, the cipher given in section 3.6 needs each game session to generate a new modulus that cannot be guessed in advance by either player. This modulus is generated as follows:

1. Alice calculates hA = H(kAk“MODULUS”) and reveals cA = H(hA). Bob calculates hB = H(kBk“MODULUS”) and reveals cB = H(hB). 2. Alice reveals hA. Bob reveals hB. 3. Alice verifies that cB = H(hB). Bob verifies that cA = H(hA). 4. The modulus p is calculated as the lowest value found to be a safe prime µ such that p  2 − (hA XOR hB). Finding a large safe prime may take several seconds of CPU time, as the average difference between adjacent safe primes in a given range increases quadratically with the distance between primes in the same range. The work- load can be divided among the players, e.g. by having Alice search assuming that p mod 24 = 11 and Bob search assuming that p mod 24 = 23.1 If Bob finds a valid safe prime, is not strictly necessary for Alice to verify that this is the lowest safe prime Bob could find, as long as she verifies the result to be a safe prime and has not found a lower safe prime in her part of the search space.

1Given a safe prime p > 7, p mod 12 = 11, therefore p mod 24 2 11, 23.

9 Mental poker for turn-based strategy games – securing fairness without a trusted third party

3.1.5 The game state All progress in the game happens through passing modifications of the game state object back and forth between the players. Along with every state update, the player responsible for the new state generates and signs a txWithState TX which the other player can use to redeem the full amount of funds locked in the contract if the update is later found to have been against the game rules. To be able to provide compact proofs in case of cheating, the game state is stored in a Merkle tree. Depending on the amount of computation that may fit in a TX, the steps in- volving private computation according to game rules may result in a series of intermediate game state objects, each of which has a signed TX associated with it. 3.1.6 Initialisation of the game state The initial game state is fully known by both players. The first two updates con- sist of initialising the private state of each player. The private state of a player is used to hold the data that should be available only to that particular player, along with computations involving such data.

1. Alice takes the initial state s0 and derives s1 by performing a state update as follows: 1. Set the StateMessage field to InitPrivate. 2. In the StatePrivateA subtree, set the PrivateExponent field to kA.

Alice generates and signs txWithState1,A,B and sends it to Bob along with state1. 2. Bob takes s1 and derives s2 by performing a state update as follows:

1. In the StatePrivateB subtree, set the PrivateExponent field to kB.

Bob generates and signs txWithState2,B,A and sends it to Alice along with state2. 3.2 Privately retrieving randomness

If Alice needs to derive deterministic randomness rx from some identifier x, she performs the following:

1. Let f = H(“BLINDING FACTOR”kkAkH(x)).

2. Let c = EfkA (H(x)). 0 3. Query Bob for the value of c = EkB (c). 0 4. Calculate the result as rx = H(Df(c )).

Bob does not learn the value of x or rx from the query, since the data Bob operates on has been encrypted with f which is used only for this single query. Properties 6 and 7 ensures that Bob is unable to distinguish this data from any other encrypted data. Due to property 4, Alice only learns rx from the query and

10 Mental poker for turn-based strategy games – securing fairness without a trusted third party

0 0 0 6 can’t use it to learn rx, for any x where x = x. 3.3 Updating and retrieving encrypted game state

The encrypted game state EGSi is a mapping used to store dynamic parts of the game state si which either player should be able to access privately (i.e. without the other player’s knowledge) when allowed by the game rules. This includes buildings, units and removal of depleted resources. 0 If, in state si, Alice needs to update the state to si such that some EGSi(x) decrypts to the value y, she performs the following:

1. Let f = H(“BLINDING FACTOR”kkAki).

2. Let c = EfkA (AONT(y)). 0 3. Query Bob for the value of c = EkB (c). 0 0 4. Update the encrypted game state such that EGSi(x) = Df(c ). Alice reveals that she is writing some value to x, but Bob has no way to know the unencrypted value from this process alone. To also hide what Alice is updat- ing unless Bob already has access to the thing being updated, such as a particular location, x may itself be derived from private randomness retrieval as described in section 3.2. Retrieval and decryption of a particular value reveals neither the identity nor the value to the other player. If, in state si, Alice instead needs to retrieve the decrypted value of EGSi(x), she performs the following:

1. Let f = H(“BLINDING FACTOR”kkAki). 0 2. Let c = DfkA(EGSi(x)). 0 3. Query Bob for the value of c = DkB (c ). 0 4. Read the decrypted value as y = AONT(Ef(c )). 3.4 Verification A player could easily try to cheat by retrieving randomness data that the player shouldn’t have access to. Such cheating can only be detected once the game has ended and the players reveal their private keys. However, other types of cheating may be detected during gameplay. In such cases, the player detecting the cheating may decide to immediately force the contract to close as described in section 3.5, as they are then guaranteed to be able to redeem the funds of the cheating player and probably have no interest in playing further.

3.5 Ending the game The game normally ends when dictated by the game rules, such as when a player has reached a winning condition or a player chooses to resign. Once Alice and Bob agree that the game has ended, they reveal kA and kB, respectively. After obtaining each other’s private keys, they examine each other’s actions to verify that they were all according to the rules. If so, they agree to redeem the funds

11 Mental poker for turn-based strategy games – securing fairness without a trusted third party locked in the contract using progMain(GENERIC), splitting the funds between each other. However, either player can also forcibly close the contract at any time, e.g. because the other player is not responsive. This is done by publishing a txResign TX, as described in the following example: If Alice resigns and reveals kA by publishing txResignA,B, Bob must respond by publishing either txRevealB,A or, if Alice cheated in some state si, txWithStatei,A,B within some previously agreed upon number of confirmations. Otherwise Al- ice gets to spend Bob’s funds by redeeming the output of txResignA,B using progRevealA,B(TIMEOUTA). Then, similarly, if Bob has responded by publishing txRevealB,A, revealing his private key kB, Alice must respond by publishing either txDefaultA,B or, for some state sj in which Bob cheated, txWithStatej,B,A within the same agreed upon number of confirmations. Otherwise Bob can spend the funds using progRevealB,A(TIMEOUTB) If no player has cheated, they will end up with txDefaultA,B, which was previ- ously signed by Bob as described in section 3.1.3. This TX pays half of the contract funds to outputs previously chosen by Bob, with the rest going to outputs that Alice may choose. If either player decides to publish a txWithState TX, they then need to pro- vide another TX containing the evidence of cheating. If e.g. Bob publishes txWithStatei,A,B, he must then create some TX that redeems the output of txWithStatei,A,B using progWithStatei,A,B(CHEATi,j,A,B), for some j, within the agreed upon number of confirmations. Otherwise Alice can spend the funds us- ing progWithStatei,A,B(TIMEOUTA). 3.6 Cipher While the Pohlig–Hellman cipher has most of the required properties, in partic- ular property 3, it does not fully meet the requirements for properties 6 and 7, k because the ciphertext Ek(x) = x mod p reveals whether the plaintext x is a quadratic residue modulo p [6]. Properties 6 and 7 depend on the decisional Diffie–Hellman (DDH) assump- tion. In the case of exponentiation modulo a safe prime p as recommended by Pohlig and Hellman [18], the assumption is in general only believed to hold if the messages are limited to the group of quadratic residues modulo p [6]. When working modulo a safe prime, turning quadratic nonresidues into quadratic residues can be done simply by negation2. The DDH assumption can therefore be maintained by negating any nonresidue before encrypting. The same can be achieved by using even exponents as keys3. If all original messages

2For any modulus p mod 4 = 3, the negative of a quadratic nonresidue is a quadratic residue [14]. p mod 4 = 3 for all safe primes p =6 5, since p = 2q + 1 where q is odd. 3 k k k For any even exponent k, x is congruent to the square of x 2 modulo m. x is therefore a quadratic residue modulo m.

12 Mental poker for turn-based strategy games – securing fairness without a trusted third party

 p−1 are kept within 1 < x 2 , then property 2 can still be maintained. Using conditional negation modulo p, it is possible to define a variant of the Pohlig–Hellman cipher that meet the requirements for properties 6 and 7 [11]:

Modulus: p = 2q + 1, where p and q are large primes. Key: 1 < k < q − 1. Message: 1 < m  q.

k k Encryption: Ek(m) = min(m mod p, p − m mod p).

Decryption: Dk(c) = Ex(C), where kx mod q = 1.

The modulus is a safe prime, as recommended by Pohlig and Hellman [18]. However, there is no requirement that the key be an odd number, as would be required in the original cipher. An even exponent projects the number onto the group of quadratic residues, and has no multiplicative inverse modulo p−1 = 2q. A multiplicative inverse modulo q is used instead, which may negate the result with respect to the original. Since the modified cipher conditionally negates re- sults such that Ek(m)  q, a negation in the exponentiation step has no effect. k Since Ek(m) behaves exactly like the Pohlig–Hellman cipher when m mod p  q and simply negates otherwise (and (−m)k = ¦mk), properties 2 and 3 remain valid. For property 4 to hold, an appropriate function is needed for H(m). If, for example, H(m) = m, then due to the multiplicative property of the cipher, given x, y,Ek(H(x)) and Ek(H(y)), one would also know Ek(H(x ¡ y)). Any secure hash function will do, but a suggestion is H(m) = SHA-256(m) + 2256. This function works for all m as long as p > 2258 and is simple to evaluate in a Bitcoin TX.

13 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 4

Map generation

While any procedural generation algorithm could be adapted to work with the method in section 3.2, the degree to which generation can be kept local varies. For example, an algorithm that determines sea level by taking the average of all points on a height map would require the whole height map to be generated, providing no locality at all. At the other extreme would be an algorithm where all sample points are independent of each other. A typical game map consists of features of different sizes – e.g., different biomes at a very large scale, and certain resources like individual materials and food at a very small scale. Some features, such as the height of the terrain, vary both at large and small scales. Such features may be combined by several noise functions operating at different scales, each generated independently with optimal locality. When used for the purpose of generating maps deterministically and with fog of war, a good noise function  keeps information exposure as local as possible when sampling,  has a consistent probability distribution from cell to cell,  does not require more queries than real-time performance allows,  is simple to implement using exact operations so that inconsistencies do not occur between players. Figure 1a shows a texture generated by the midpoint displacement algorithm as used by Rice [19] (there referred to as diamond-square). As can be seen in figure 1b, this algorithm is unsuitable for keeping map information local. Ideally this figure would show a single dark spot concentrated around one point. How- ever, in the midpoint displacement algorithm, sampling a single point anywhere on the map requires the values at the corners of the map, which result in full knowledge of the values along the edges of the map. At each subdivision level, the midpoint in the square surrounding the sampling point creates a cross filling the square, along which map values are known exactly. In fact, there is no point of the map where knowledge is zero, since the value at the midpoint of the map

14 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Figure 1: Midpoint displacement texture

(a) Fully generated texture (b) Uncertainty after sampling one point. Black corresponds to full knowledge, white corresponds to no knowledge (in re- lation to the map’s maximum variance) must be known and affects every part of the map. Extending the midpoint displacement algorithm to a diamond-square algo- rithm would add some uncertainty along the distant edges, but the corners at each subdivision level would remain. Approximations of fractional Brownian motion (fBm) can be achieved by us- ing other noise functions as well. This is generally done by combining noise functions at different scales and different amplitudes, such that the amplitude is proportional to the scale, thus approximating the frequency spectrum of fBm [12]. Figure 2 shows a texture generated in this way, using convolution as de- scribed further in section 4.1. The non-local nature of fBm means that it is not possible to achieve true fBm without revealing some non-local information. However, by combining noise functions at different scales, each layer of noise can have its locality optimised. Figure 2b shows knowledge of the map after sampling a single point. Compared to the results from the midpoint displacement algorithm in figure 1b, this layer- based algorithm is able to keep knowledge of the map much more local.

4.1 Algorithms At the lowest noise scale, each cell simply gets its own value independent of the other cells. At higher scales, however, values change more slowly from cell to cell. This can be achieved in two ways: either by interpolating between points in a larger grid, or by averaging across multiple nearby points in the underlying grid to get the value for each cell. Combining the two methods is also possible. The interpolation algorithm consists of making a grid and assigning each grid

15 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Figure 2: Approximation of fBm by convolution

(a) Fully generated texture (b) Uncertainty after sampling one point point a random value. The grid size is some integral multiple of the size of the map cells. Map cells that lie between grid points get their values by interpolating between the surrounding grid points. In a map of hexagonal cells, each cell may be surrounded by up to three grid points that make a triangle. By converting the coordinates of the cell to triangular coordinates within the surrounding grid points, the value from each grid point can be weighted to determine the inter- polated value. This conversion is simple, since the hexagons can be represented using axial coordinates that translate directly to triangular coordinates. Interpolation can be a problem in some cases, because it results in a varying probability distribution. In the simple case of linear interpolation between two independent variables each with the same uniform distribution, the midpoint has a triangular probability distribution, with only half the variance compared to the two variables. This may create visible artefacts or otherwise complicate the process of converting these values to map data. Averaging across multiple cells to blur the noise texture can be done in a con- volution process. Figure 3 shows some masks that can be used to filter hexagonal cells. Figure 4 shows the same noise texture filtered with each of these masks (adjusted to maintain equal variance). Some artefacts revealing the edges of the masks can be seen, particularly with the largest masks. This effect can be reduced by softening the edges of the kernels. In figure 5, the masks have been modified such that each of the outermost cells only contributes half as much to the final value as each inner cell. The same noise texture filtered with the modified masks can be seen in figure 6. The convolution algorithm is quite simple: Each cell is assigned a random value. A suitable convolution mask, such as one shown in figure 5, is chosen. Then, regular convolution is performed. For each cell to be sampled, the mask is centred on the cell, and each cell value of the mask is multiplied with the value

16 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Figure 3: Convolution kernels

(a) Single cell (b) 3 cells wide (c) 5 cells wide (d) 9 cells wide

Figure 4: Convolution filtering

(a) No filter (b) 3-cell filter (c) 5-cell filter (d) 9-cell filter

Figure 5: Modified convolution kernels

(a) 3 cells wide (b) 5 cells wide (c) 9 cells wide

Figure 6: Filtering with modified kernels

(a) No filter (b) 3-cell filter (c) 5-cell filter (d) 9-cell filter

17 Mental poker for turn-based strategy games – securing fairness without a trusted third party of the underlying cell. These products are then averaged to get the sample value. An additional step is to adjust for the loss of variance when averaging: Assuming that the average is zero, the value is multiplied by

n w p i=1 i , n w2 P i=1 i where w1 . . . wn are the values of theP convolution mask. This multiplication uses a square root, which is a potentially inexact operation. If the masks are constant, this can be solved by having the players agree on this value before starting the game. Otherwise, an integer square root may have to be used. At very large scales, convolution may become a performance issue. Sampling a texture for the first time takes as many queries as there are cells in the convo- lution mask, i.e. O(N2), where N is the width of the mask. Further samples will generally be taken next to existing samples, thus requiring only O(N) queries. Performance is therefore mostly an issue in the beginning of the game, although O(N) queries per move could still be too demanding. Reducing the size of the required mask while keeping noise at the same scale requires scaling up the underlying grid, introducing the need for interpolation. The aforementioned effect on the probability distribution is reduced, however, as the convolution filtering causes neighbouring cells to be highly correlated. Convolution and interpolation may be combined by making a grid like in the interpolation algorithm, performing convolution across this grid (by treat- ing each grid point as a cell), and then interpolating from the filtered grid.

18 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 5

Security parametres

When deciding on the security parametres of a cryptosystem, the security level is often set with the goal of ensuring protection against the strongest possible ad- versaries for the next several years to decades. The cryptosystem in this project only protects the secrecy of game data for the duration of a game, which is typ- ically on the order of hours. In the current design, breach of this cryptosystem only results in one party gaining read access to game state that should have been hidden. A player that manages to break the cryptosystem cannot take advantage of this to e.g. steal the funds that the other player has deposited in the contract, as the contract is protected by a separate, more secure cryptosystem. The com- bination of a very limited timeframe and low impact in case of a breach means that a relatively low security level may be acceptable. In their system designed for real-time strategy (RTS) games, Bursztein et al. [7] use a 127-bit elliptic curve for securing a game lasting about an hour. They estimate an attack to take about 6400 core-years1. In a modular exponentiation cipher, a 768-bit modulus would give comparable security with an estimated 36,500 core-years to crack [2]. Such an attack would break the security of that particular modulus: after the first step, any key using the same modulus may be derived in only 2 core-days [2]. This problem can be addressed by ensuring that each game session uses a unique modulus, preferably such that no player can guess the modulus ahead of the game. Smaller exponents result in faster exponentiation [2]. In order to maintain the security of the cipher, the key should have a size of at least twice the security level provided by the modulus [17]. For a 768-bit modulus, this corresponds roughly to a 128-bit key2.

13200 machine-years on a machine with 2 physical cores. 2  127 This comes from comparing the 768-bit modulus to the 127-bitp elliptic curve (q 2 ), which has a security level of approximately 64 bits (cracking takes O( q) steps) [7].

19 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 6

Performance analysis

The following calculations concern the algorithms for approximating fractional Brownian motion (fBm) on a 2D map of hexagonal cells. N is the number of levels above single-cell, and the scale doubles at each level. Calculations of the upper bounds for a single sample are given in table 1. With pure convolution and masks like those in figure 5, the number of cells n in a mask with width 1 + 2n cells is 1 + i=1 6i = 3n(n + 1) + 1. At level l > 0, l−1 N−1 i i N N n = 2 . To sample a single point, 1 + i=0 3(4 + 2 ) + 1 = N + 3(2 − 1) + 4 queries must be made. To sample a pointP next to a previously sampled point, N i−1 N+1 P 1 + i=1 1 + 2(2 ) = N + 2 − 1 new queries must be made. Using interpolation without convolution, up to 3 queries are needed at each levelP above 0. Sampling a single point takes between N + 1 and 3N + 1 queries, while sampling a point next to a previously sampled point takes between 1 and N + 1 new queries. Combining convolution and interpolation, limiting the mask to level M, is equivalent to M-level pure convolution plus 1 to 3 level-M convolution sam- plings at each of the N − M interpolation layers. For a single sample, the inter- polation layers need an additional (N − M)(3(4M + 2M) + 1) to (N − M)(3(4M + 2M) + 1 + 2M + 4(2M) − 2) samples1, while sampling a point next to a previously sampled point takes an additional 0 to (N − M)(M + 2M+1 − 1) queries for the interpolation layers. Taking into account the performance of encryption, convolution is feasible up to level 5. Level 6 may require M = 4, level 7 M = 3.

1The upper bound is a pessimistic approximation based on one first sample and two neigh- bouring samples, which doesn’t take into account the overlapping queries of the neighbouring samples.

20 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Table 1: Number of queries for a single sample

N 1 2 3 4 5 6 7 8 convolution 8 27 88 305 1122 4291 16772 66309 M = 4 - - - 305 1192 2079 2966 3853 M = 3 - - 88 341 594 847 1100 1353 M = 2 - 27 106 185 264 343 422 501 M = 1 8 35 62 89 116 143 170 197 interpolation 4 7 10 13 16 19 22 25

6.1 Blockchain scripts For the blockchain to be able to determine the outcome of a game, a player needs to be able produce a transaction that contains the information necessary to show that the other player has cheated. For a complex game, the whole set of rules and game state will typically be too large to fit in a single transaction. However, only the parts of the rules and game state relevant to a single action need to be evaluated on the blockchain. By using a merklised abstract syntax tree (MAST), a transaction output can be made valid for a large set of shorter scripts, each covering their part of the game rules, and only the relevant script needs to be specified in the redeeming transaction. BIP 114 [15] is a proposal to add MAST to Bitcoin. Similarly, the full game state can be kept in a Merkle tree and only have the evaluated parts revealed to a script. This technique can already be used with the operations available to Bitcoin scripts today. Arithmetic in Bitcoin scripts is currently limited to addition and subtraction on operands limited to 32 bits (including a sign bit). In particular, multiplica- tion and remainder operations are not available. A single script is also limited to 201 operations. While it is theoretically possible to implement a contract un- der these limits, the number of scripts for intermediate states that would have to be produced and signed for each encryption operation would quickly become a bottleneck for game performance. The current reference implementation for BIP 114 [15] does enable more arithmetic operations, including multiplication and remainder, and extends the range of operands to 56 bits. These extensions reduce the complexity of implementing modular exponentiation, although a sin- gle modular exponentiation with a modulus large enough for security would still not fit in a transaction (TX). A single modular exponentiation should ideally be able to fit in a TX along with two signature verifications and some simpler arithmetic and logic. The blockchain design described in the appendix includes an additional operation to perform modular arithmetic. However, as this operation is computationally expensive compared to any other currently available operation, its implementa- tion may require putting additional limits on a TX using this operation.

21 Mental poker for turn-based strategy games – securing fairness without a trusted third party

The current design described in appendix A, if used together with the cur- rent MAST proposal specified in BIP 114 [15], has a performance issue that may limit how fast a player can generate txWithState TXs. A TX of this kind must be generated for every state update for the other player to accept it. The output program of this TX, progWithState, may have a large number of CHEAT branches, each of which must contain the new state and must be individually hashed and then combined in order to obtain the program hash to use as the output in the txWithState TX. A small change to the contract fixes this issue for intermediate states, i.e. those states which are updated by the same player who made them: a single txWithState may include the root of a Merkle tree containing several intermediate states rather than a single state. That way multiple state updates may be provided for each MAST of a txWithState.

22 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 7

Enforcing fair play

With the peer-to-peer protocol alone, players have a way to provide evidence to each other at the end of the game showing that they have played by the rules. However, since the players cannot necessarily rely on a common trusted third party (TTP) to rule against a cheating player, there may be little incentive a player to play fairly and provide evidence of fair play. By integrating a blockchain-enforceable contract with the protocol, the con- tract can take funds from the players at the beginning of the game, to be returned to the players once they have verified each other’s actions. If e.g., at the end of the game, Bob refuses to provide his key kB, Alice can invoke the contract to make Bob either reveal the key or lose his part of the funds. If the key then re- veals that Bob cheated, Alice can use this evidence with the contract in order to redeem Bob’s funds. Appendix A describes a contract designed to run on a version of the Bitcoin blockchain with some extensions, one of which is currently specified in BIP 114 [15]. An important thing to note is that, while the game rules enforced by the con- tract may be Turing complete, it is not necessary for the scripting system to be Turing complete. Bitcoin’s scripting system is not Turing complete; Turing completeness makes the execution of a program difficult to reason about and is therefore avoided where program execution should be kept simple. The scripting system in Bitcoin is part of the consensus layer, meaning that every node in the network must agree exactly with the other nodes in how a script executes. The current contract does not attempt to enforce progress of the game. En- forcing progress mainly becomes a problem if the game involves a prize to be paid to the winner. If Bob thinks that Alice is going to win the game, he may de- cide to halt the game by not providing updates to Alice. At that point it becomes a waiting game to see who first resigns in order to get their locked funds back. Instead, either player has the opportunity to resign at any time by broadcast-

23 Mental poker for turn-based strategy games – securing fairness without a trusted third party ing their version of a particular transaction (TX), txResign, which reveals their private key. Given a blockchain with more advanced features, the contract could try to enforce progress by forcing players to communicate over the blockchain in case they are unable to communicate directly. For a complex game such as a turn- based strategy (TBS) game, this would likely end up being both slow and ex- pensive, however. In the simplest variant of such a contract, the non-responding player could wait almost the whole duration of some timeout before providing their update. Such timeouts need to be long, on the order of hours or days, e.g. in case there are capacity issues on the blockchain or in case many blocks happen to appear in quick succession. An improvement over this design could apply in- creasing costs depending on how many blocks an update is provided. A stalling player not willing to pay more than the other player would then be likely to respond within the next block. Further, the contract could make use of replace- by-fee (RBF) signalling to let the players pass updates back and forth in the form of multiple unconfirmed TXs over the network as if in a game of hot potato, with the player getting a TX of theirs onto the blockchain having to pay less than the other player. However, even with this improvement the process would likely end up being too slow and expensive, as each TX needs a higher fee than the TX it replaces and there’s a limit to how many replacements can happen in a row.

24 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Chapter 8

Conclusion and future work

The work on this project has resulted in the design of a protocol for playing a turn-based strategy (TBS) game between two players over a network, includ- ing map generation under fog of war, in a way that dissuades cheating and lets the players know whether the game was played fairly. This protocol consists of several modular components, including privately retrieving deterministic ran- domness and using a blockchain contract to make cheating prohibitively costly. One of the obvious next steps to make from this work is to implement a work- ing game that fully implements the designed protocol. A large part of such a project would be to make all the contract scripts necessary to evaluate game state updates. Perhaps the project should begin with developing better tools for generating such scripts. The modular nature of the protocol should make it easy to adapt parts of the protocol to other applications. Adaptations of the protocol as a whole may be suitable for certain other types of games. Real-time strategy (RTS) games in particular are very much like TBS games other than how time is handled. Other games of interest may be games in the style of Minecraft, where players can explore a 3D world, digging below the surface and collecting resources. The current protocol is designed for two players for simplicity. Extending the protocol to three or more players is a potential task for future work. Such an extension opens up for some interesting possibilities. One is the anonymisation of in-game actions, so that a player cannot tell which of the other players is providing a certain state update. Another is continuing the game with one player less if a player drops out without revealing their private key. A potential challenge in enabling such a feature would be map re-generation. One issue that comes up when there are more than two players is that some players could collude in ways that are out of the control of the game rules. Enforcing game progress has been deemed infeasible as part of this project, but simpler games that may use a similar protocol could potentially implement

25 Mental poker for turn-based strategy games – securing fairness without a trusted third party the such enforcement via the blockchain.

26 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Appendix A

Blockchain details

The following shows an example of a blockchain design and necessary transac- tions (TXs) for a 2-player game. It is not meant to provide a complete specifica- tion.

A.1 Features For the blockchain to be able to securely determine the outcome of a game, it needs to have all operations necessary to evaluate the game rules. However, Turing completeness is not necessary in the scripting system.  Big integer arithmetic, up to e.g. 1024 bits  Basic modular arithmetic  Modular exponentiation  Merklised abstract syntax tree (MAST), e.g. as specified in BIP 114 [15] Arithmetic operands are currently limited to 32-bit signed integers in Bitcoin. Some operations are disabled, in particular multiplication and remainder. Native support of big integer arithmetic is not strictly necessary, but the scripts necessary to prove certain cheating get more complicated and require more intermediate states without this support.

A.2 Objects Objects are structured in the form of multi-level Merkle trees. That way, TXs only include the parts of the objects that are relevant to validation. A.2.1 State The state object contains the whole game state and history. Given a state s in a game between two players a and b,  π(s) is the state that directly precedes s,  δ(s) is the update that α(s) performed on π(s) to produce s,

27 Mental poker for turn-based strategy games – securing fairness without a trusted third party

 εp(s), for each player p 2 {A, B}, is the secret exponent of p, Every state object contains the secret exponents of each player for easy access by the CHEAT branches described below. This may seem like a problem as each player has a copy of the state object but is not supposed to know the secret exponent of the other player until the game has finished. However, as the object is constructed as a Merkle tree, only the hashes of the exponents are shared during the game.

A.3 Branches A program consists of one or more branches, one of which is selected for execu- tion by the redeeming TX. Each point under each branch signifies a subscript. The subscripts are individ- ually hashed when constructing the MAST, so one subscript can go into multiple branches without having to process large amounts of data for each branch.

A.3.1 REVEALp

 cRevealPushScript Pp AB See Game.hs for the definition of cRevealPushScript. Using Compile.hs, this code generates the following script: push QA; OpCheckSigVerify; push QB; OpCheckSigVerify; push Pp; OpSize; OpDup; OpToAltStack; Op1; Op16; OpWithin; OpVerify OpFro- mAltStack; OpSwapCat; OpHash256; OpEqualVerify; Op1; OpGreaterThan; OpVerify

A.3.2 GENERIC

 push QA; OpCheckSigVerify; push QB; OpCheckSigVerify

A.3.3 TIMEOUTp

 push x; OpCheckSequenceVerify; push Qp; OpCheckSigVerify, where x is the TX input sequence number that encodes a relative locktime of a certain number of blocks.

A.3.4 CHEATi,j,p,q

 push kp  push si  push Qq; OpCheckSigVerify; cheatedj

cheatedj here expands to one of several strings of operations, essentially one for each rule in the game. See cCheat in Game.hs which implements some of these branches. The push kp subscript is initially only known by player p. The hash of this subscript is the commitment that p must reveal in the input TX. Since player q knows the hash and that this hash must correspond to kp, they know the script

28 Mental poker for turn-based strategy games – securing fairness without a trusted third party will work as intended.

A.4 Programs Each output of a TX defines a program that must execute successfully in a TX trying to redeem that particular output. A program is structured as a MAST, so that only the branch selected for execution needs to be provided. A.4.1 progMain

 {REVEALA, REVEALB, GENERIC}

A.4.2 progRevealp,q  {REVEALq, GENERIC, TIMEOUTp}

A.4.3 progWithStatei,p,q  {CHEATi,j,p,q | j 2 {0..n}} [ {TIMEOUTp} A.5 Transactions As in Bitcoin, a TX consists of one or more inputs and one or more outputs. Each input contains a script and a reference to the output of an earlier TX for this TX to redeem. The script consists of the branch to be evaluated in a MAST program whose root hash is specified by the output, along with data to be passed to this program, such as the variables in the contract described below. A TX is only valid if all of the following are true:  The root hash of the MAST in each input script matches that specified by the output it refers to.  Every input script evaluates to true.  The sum of output values is equal to the sum of input values minus the TX fee. A.5.1 txFunding The game starts with a funding TX that establishes the contract that can be used to enforce all the rules of the game. Typically each player provides half of the funds available to the contract and receives their part back once the game has finished. If a player p is found to have cheated or has dropped out without prop- erly ending the game, p loses the funds to the other player q. Besides change outputs, the TX will typically include one output:  progMain, which receives the sum of funds that should be locked by the contract. While the funding TX determines all rules and further possible TXs, the TX will usually be quite small. The defining part of the TX is the output described above, which only contains the 32-byte root hash of the combination of possible scripts.

29 Mental poker for turn-based strategy games – securing fairness without a trusted third party

A.5.2 txResignp,q 1. Signatures

 σp, with hash type ALL  σq, with hash type SINGLE 2. In: txFunding

 Stack: σA, σB, kp  progMain(REVEALp)

3. Out: progRevealp,q

A.5.3 txRevealp,q 1. Signatures

 σp, with hash type ALL  σq, with hash type SINGLE

2. In: txResignq,p

 Stack: σA, σB, kp  progRevealq,p(REVEALp)

3. Out: progRevealp,q

A.5.4 txWithStatei,p,q

 Produced and signed by p and given to q for every state si generated by p. To be signed and broadcast by q once q knows kp (revealed in input TX) if q finds a way to spend it using CHEAT. q would never sign and broadcast this TX unless p has cheated, otherwise q could not spend and p could spend everything via TIMEOUTp.  Problem: CHEAT creates a large hash tree where every branch must be recreated for every state. 1. Signatures

 σp, with hash type SINGLE + ANYONECANPAY  σq, with hash type ALL

2. In: {txResignp,q, txRevealp,q}

 Stack: σA, σB  progReveal(GENERIC)

3. Out: progWithStatei,p,q

A.5.5 txDefaultp,q 1. Signatures

 σp, with hash type ALL  σq, with hash type SINGLE

30 Mental poker for turn-based strategy games – securing fairness without a trusted third party

2. In: txRevealq,p

 Stack: σA, σB  progReveal(GENERIC)

3. Out: 50% of funds minus fee: spendable by Qq

31 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Appendix B

Source code

B.1 Blockchain.hs {-# LANGUAGE DeriveGeneric, FlexibleInstances, FunctionalDependencies, GeneralizedNewtypeDeriving, MultiParamTypeClasses, TemplateHaskell #-} module Blockchain where import Control.Applicative (Applicative, pure,(<*>)) import Control.Lens (ASetter, Getting, abbreviatedFields, assign, makeFields, makeLenses, makeLensesWith, over, use, view,(%=),(%~),(.=),(^.)) import Control.Monad (ap, forM, forM_, guard, mapM_, replicateM, void, when) import Crypto.Error (CryptoFailable(CryptoPassed)) import Crypto.Hash (Digest, HashAlgorithm, RIPEMD160(RIPEMD160), SHA256(SHA256), digestFromByteString, hash, hashDigestSize, hashWith) import Crypto.Number.ModArithmetic (expFast) import Crypto.PubKey.Ed25519 (PublicKey, SecretKey, Signature, publicKey, signature, verify) import Data.Bits (complement, shiftL, shiftR, xor,(.&.),(.|.)) import Data.ByteArray (ByteArrayAccess, Bytes, convert, pack, unpack) import Data.ByteString (ByteString) import Data.Function (on) import Data.Functor (Functor, fmap,(<$>)) import Data.Maybe (isJust) import Data.Monoid (Monoid( mappend, mempty),(<>)) import Data.Serialize (Get, Putter, Serialize, encode, get, getByteString, getWord16le, getWord32le, getWord8, put, putByteString, putWord16le, putWord32le, putWord8, remaining, runGet, runGetState, runPut) import Data.Word (Word16, Word32, Word64, Word8) import GHC.Generics (Generic) import qualified Control.Monad.State as MS import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as X import qualified Data.Map as Map data StackItem= MkStackItem[Word8]

32 Mental poker for turn-based strategy games – securing fairness without a trusted third party

deriving(Generic, Eq) instance Serialize StackItem data Op= OpP OpPush | OpD ByteString | OpC OpCode instance Show Op where showo = case o of OpP p -> showp OpC c -> showc showList [o] s = showo ++ s showList (o:os) s = showo ++ "" ++ showList oss showList [] s = s instance Serialize Op where put (OpP p)= case p of OpX0 -> putWord8 0 OpX x | x >=-1&& x <= 16 -> putWord8 $ 80+ fromIntegralx OpPushData nd | n >0&& n < 76 -> putWord8n >> putByteStringd OpPushData1 nd -> putWord8 76 >> putWord8n >> putByteStringd OpPushData2 nd -> putWord8 77 >> putWord16len >> putByteStringd OpPushData4 nd -> putWord8 78 >> putWord32len >> putByteStringd put (OpC c)= putWord8 . fromIntegral $ 97+ fromEnumc get = getWord8 >>= go where gob | b < 97= OpP <$> case b of 0 -> return $ OpX0 _| b < 76 -> OpPushData b <$> getByteString (fromIntegralb ) 76 -> getWord8 >>= \n -> OpPushData n <$> getByteString (fromIntegraln ) 77 -> getWord16le >>= \n -> OpPushData2 n <$> getByteString (fromIntegraln ) 78 -> getWord32le >>= \n -> OpPushData4 n <$> getByteString (fromIntegraln ) 80 -> undefined _ -> return . OpX$ fromIntegralb - 80 | otherwise = return . OpC$ toEnum (fromIntegralb - 97) data OpPush= OpX Int | OpPushData Word8 ByteString | OpPushData1 Word8 ByteString | OpPushData2 Word16 ByteString | OpPushData4 Word32 ByteString deriving Eq instance Show OpPush where show (OpX(-1))= "Op1Negate" show (OpX n)= "Op" ++ shown showp = case p of OpPushData nd -> f""nd OpPushData1 nd -> f "Data1 "nd OpPushData2 nd -> f "Data2 "nd OpPushData4 nd -> f "Data4 "nd where fsnd = "OpPush" ++ s ++ shown ++ "" ++ show (X.encoded ) data OpCode= OpVerify | OpToAltStack | OpFromAltStack | OpDepth | OpDrop | OpDup | OpNip

33 Mental poker for turn-based strategy games – securing fairness without a trusted third party

| OpOver | OpPick | OpRoll | OpRot | OpSwap | OpTuck | Op2Drop | Op2Dup | Op3Dup | Op2Over | Op2Rot | Op2Swap | OpCat | OpSubstr | OpLeft | OpRight | OpSize | OpSwapCat | OpInvert | OpAnd | OpOr | OpXor | OpEqual | OpEqualVerify | Op1Add | Op1Sub | Op2Mul | Op2Div | OpNegate | OpAbs | OpNot | Op0NotEqual | OpAdd | OpSub | OpMul | OpDiv | OpMod | OpPowMod | OpLShift | OpRShift | OpBoolAnd | OpBoolOr | OpNumEqual | OpNumEqualVerify | OpNumNotEqual | OpLessThan | OpGreaterThan | OpLessThanOrEqual | OpGreaterThanOrEqual | OpMin | OpMax | OpWithin | OpHash160 | OpHash256 | OpCodeSeparator | OpCheckSig | OpCheckSigVerify | OpCheckMultiSig | OpCheckMultiSigVerify | OpCheckLockTimeVerify | OpCheckSequenceVerify deriving(Enum, Eq, Generic, Show)

34 Mental poker for turn-based strategy games – securing fairness without a trusted third party

data OutputHashtype= SighashAll| SighashNone| SighashSingle deriving Generic data Hashtype= MkHashtype OutputHashtype Bool deriving Generic data TxSig= MkTxSig Signature Hashtype type Outpoint=(Hash256, Word32) data Input= MkInput Outpoint Word32 data Output= MkOutput{ _value :: Word64, _program :: Hash256} deriving Generic data Transaction= MkTransaction{ _inputs ::[Input], _outputs ::[Output], _nSequence :: Word32, _witness :: Witness} data Witness= MkWitness[WitnessField] data WitnessField= MkWitnessField{ wfStack ::[StackItem], wfSubscripts ::[ByteString], wfPath ::[Hash256]} newtype Hash160= MkHash160(Digest RIPEMD160) deriving(Eq, Ord, Show, ByteArrayAccess) newtype Hash256= MkHash256(Digest SHA256) deriving(Eq, Ord, Show, ByteArrayAccess) type UTXOSet= Map.Map Outpoint Output data MAST= MLeaf[ByteString] | MTree MAST MAST

data ScriptState= MkScriptState{ ssStack ::[StackItem], ssAltstack ::[StackItem], ssInputValue :: Word64, ssInputIx :: Int, ssHashCode :: ByteString, ssCode :: ByteString, ssTx :: Transaction} data Exec a = MkExec(ScriptState -> Maybe( a, ScriptState)) | ExecInvalid makeLenses ’’Output makeLenses ’’Transaction makeLensesWith abbreviatedFields ’’WitnessField makeLensesWith abbreviatedFields ’’ScriptState class FromStackVal a where fromStackValue :: StackItem -> a class ToStackVal a where toStackValue :: a -> StackItem

35 Mental poker for turn-based strategy games – securing fairness without a trusted third party

instance Serialize OutputHashtype instance Serialize Hashtype instance Serialize Output instance Serialize Hash160 where put = putByteString . convert get = MkHash160 <$> getDigest RIPEMD160 instance Serialize Hash256 where put = putByteString . convert get = MkHash256 <$> getDigest SHA256 instance Monad Exec where ExecInvalid >>=_= ExecInvalid MkExec f >>= g = MkExec( \s -> do (r, s’) <- fs runExec (gr ) s’) returna = MkExec$ \s -> Just( a, s) instance Applicative Exec where pure = return (<*>)= ap instance Functor Exec where fmapf xs = xs >>= return . f instanceMS.MonadState ScriptState Exec where statef = MkExec$ Just. f instance FromStackVal Integer where fromStackValue (MkStackItem x)= fromStackIntx instance ToStackVal Integer where toStackValue = MkStackItem. stackInt instance FromStackVal Bool where fromStackValue =((0:: Integer) /=). fromStackValue instance ToStackVal Bool where toStackValue = toStackValue . fromEnum instance ToStackVal Int where toStackValue = toStackValue . toInteger instance FromStackVal Bytes where fromStackValue (MkStackItem x)= packx instance FromStackVal ByteString where fromStackValue (MkStackItem x)= packx instance ToStackVal ByteString where toStackValue = MkStackItem. unpack instance ToStackVal PublicKey where toStackValue =( toStackValue :: ByteString -> StackItem). convert

decodeScript :: Get[Op] decodeScript = remaining >>= \n -> case n of 0 -> return [] _ -> get >>= \o ->( o :) <$> decodeScript

36 Mental poker for turn-based strategy games – securing fairness without a trusted third party

getDigest :: HashAlgorithm a => a -> Get(Digest a) getDigesta = do Just d <- digestFromByteString <$> getByteString (hashDigestSizea ) returnd txValidate :: UTXOSet -> Transaction -> Bool txValidatest @(MkTransaction is os nl (MkWitness w))= isJust $ do guard $ length is == lengthw utxos <- forM is $ \(MkInput op _) -> Map.lookup ops guard $((>=)‘ on‘( sum .( toInteger <$> view value <$>))) utxos os forM_ (zip3 [0..] utxosw )$ \(i, MkOutput iv ip, MkWitnessField stack subscripts path) -> do let sh =( hash256 . encode) <$> subscripts ln = hash256 $ encode sh mr = merkleRootFromBranch ln path guard $ mr == ip r <- evalScript stack ivi (BS.concat subscripts) t guard $ nullr merkleRootFromBranch :: Hash256 ->[Hash256] -> Hash256 merkleRootFromBranch leaf []= leaf merkleRootFromBranch leaf (x:xs)= merkleRootFromBranch (hash256 $ encode ns) xs where ns | leaf < x =( leaf, x) | otherwise =( x, leaf) putByteArray :: ByteArrayAccess a => Putter a putByteArray =( put :: Putter ByteString). convert stackInt :: Integer ->[Word8] stackIntn | n <0= let x : xs = stackInt (-n) in 128+ x : xs | n ==0=[] | n < 128=[ fromIntegern ] | otherwise = fromIntegern : stackInt (n ‘div‘ 256) fromStackInt ::[Word8] -> Integer fromStackInt []=0 fromStackInt xs@(x:_)| x < 128= go xs | otherwise =- go xs where go []=0 go (n:ns)= toIntegern + 256* go ns hash160 :: ByteArrayAccess ba => ba -> Hash160 hash160 = MkHash160. hash . hashWith SHA256 hash256 :: ByteArrayAccess ba => ba -> Hash256 hash256 = MkHash256. hash . hashWith SHA256 evalScript ::[StackItem] -> Word64 -> Int -> ByteString -> Transaction -> Maybe[StackItem] evalScripts ivic tx = let state = MkScriptState s [] ivicc tx in evalExec (go >> use stack) state where go = exNextOp >>= \o -> case o of Just(OpP p) -> exOpPushp >> go Just(OpC c) -> evalc >> go Nothing -> return() runExec :: Exec a -> ScriptState -> Maybe( a, ScriptState) runExec ExecInvalid_= Nothing runExec (MkExec f) s = fs

37 Mental poker for turn-based strategy games – securing fairness without a trusted third party

evalExec :: Exec a -> ScriptState -> Maybe a evalExece = fmap fst . runExece exDecodeScript :: Get a -> Exec a exDecodeScriptg = do c <- use code (r, c’) <- case runGetStategc 0 of Left_ -> ExecInvalid Right x -> returnx code .= c’ returnr exNextOp :: Exec(Maybe Op) exNextOp = exDecodeScript $ remaining >>= \n -> case n of 0 -> return Nothing _ -> get exPeek :: Getting[ r] ScriptState[ r] -> Exec r exPeekg = do s <- useg when (nulls ) ExecInvalid return $ heads exPop :: Exec StackItem exPop = exPop’ stack exPop’ :: Getting[ r] ScriptState[ r] -> Exec r exPop’g = do x <- exPeekg stack %= tail returnx exPush :: StackItem -> Exec () exPush = exPush’ stack exPush’ ::MS.MonadState sm => ASetter ss [a][ a] -> a -> m() exPush’gx = g %=( x :) exEnsure :: Int -> Exec () exEnsuren = do s <- use stack when (lengths < n) ExecInvalid exPopVal :: FromStackVal a => Exec a exPopVal = fromStackValue <$> exPop exPopKey :: Exec PublicKey exPopKey = do b <- exPopVal case publicKey (b :: Bytes) of CryptoPassed k -> returnk _ -> ExecInvalid exPopSig :: Exec TxSig exPopSig = do b <- exPopVal let rg = runGet $ do bs <- signature <$> getByteString 32

38 Mental poker for turn-based strategy games – securing fairness without a trusted third party

ht <- get return (bs, ht) case rgb of Right(CryptoPassed s, ht) -> return $ MkTxSig s ht Left_ -> ExecInvalid exPopSize :: Exec Int exPopSize = do n <- exPopVal when (n <0 || n >=2^31) ExecInvalid return $ fromIntegern exZipWith ::(Word8 -> Word8 -> Word8) -> Exec () exZipWithf = do MkStackItem a <- exPop MkStackItem b <- exPop when (lengtha /= lengthb ) ExecInvalid exPush . MkStackItem$ zipWithfab exPushVal :: ToStackVal a => a -> Exec () exPushVal = exPush . toStackValue exModifyVal ::(FromStackVal a, ToStackVal b) =>( a -> b) -> Exec () exModifyValf = exPopVal >>= exPushVal . f exModifyInt :: ToStackVal a =>(Integer -> a) -> Exec () exModifyInt = exModifyVal exBinOp ::(FromStackVal a, FromStackVal b, ToStackVal c) =>( a -> b -> c) -> Exec () exBinOpf = do b <- exPopVal a <- exPopVal exPushVal $ fab exBinOpInt :: ToStackVal a =>(Integer -> Integer -> a) -> Exec () exBinOpInt = exBinOp exOpPush :: OpPush -> Exec () exOpPush (OpX x)= exPushValx exOpPushp = case p of OpPushData_ d -> exPushVald OpPushData1_ d -> exPushVald OpPushData2_ d -> exPushVald OpPushData4_ d -> exPushVald eval :: OpCode -> Exec () eval OpVerify= do MkStackItem x <- exPop when (fromStackIntx ==0) ExecInvalid eval OpToAltStack= exPop >>= exPush’ altstack eval OpFromAltStack= exPop’ altstack >>= exPush eval OpDepth= use stack >>= exPushVal . length eval OpDrop= void exPop eval OpDup= exPeek stack >>= exPush eval OpNip= exEnsure 2 >> stack %= \s -> heads : drop 2 s eval OpOver= exEnsure 2 >> stack %= \s -> s !!1: s eval OpPick= do n <- exPopSize

39 Mental poker for turn-based strategy games – securing fairness without a trusted third party

exEnsure $ n +1 stack %= \s -> s !! n : s eval OpRoll= do n <- exPopSize exEnsure $ n +1 stack %= \s -> s !! n : takens ++ drop (n +1) s eval OpRot= exEnsure 3 >> stack %= \s -> s !!2: take 2 s ++ drop 3 s eval OpSwap= exEnsure 2 >> stack %= \s -> s !!1: heads : drop 2 s eval OpTuck= exEnsure 2 >> stack %= \s -> take 2 s ++[ heads ] ++ drop 2 s eval Op2Drop= eval OpDrop >> eval OpDrop eval Op2Dup= exEnsure 2 >> stack %= \s -> take 2 s ++ s eval Op3Dup= exEnsure 3 >> stack %= \s -> take 3 s ++ s eval Op2Over= exEnsure 4 >> stack %= \s -> take 2( drop 2 s) ++ s eval Op2Rot= exEnsure 6 >> stack %= \s -> take 2( drop 4 s) ++ take 4 s ++ drop 6 s eval Op2Swap= exEnsure 4 >> stack %= \s -> take 2( drop 2 s) ++ take 2 s ++ drop 4 s eval OpCat= do MkStackItem b <- exPop MkStackItem a <- exPop exPush . MkStackItem$ a ++ b eval OpSubstr= do s <- exPopSize b <- exPopSize MkStackItem x <- exPop when (b + s > lengthx ) ExecInvalid exPush . MkStackItem. takes $ dropbx eval OpLeft= do s <- exPopSize MkStackItem x <- exPop when (s > lengthx ) ExecInvalid exPush . MkStackItem$ takesx eval OpRight= do s <- exPopSize MkStackItem x <- exPop let n = s - lengthx when (n <0) ExecInvalid exPush . MkStackItem$ dropnx eval OpSize= do MkStackItem x <- exPeek stack exPushVal $ lengthx eval OpSwapCat= eval OpSwap >> eval OpCat eval OpInvert= do MkStackItem x <- exPop exPush . MkStackItem$ complement <$> x eval OpAnd= exZipWith (.&.) eval OpOr= exZipWith (.|.) eval OpXor= exZipWith xor eval OpEqual= do a <- exPop b <- exPop exPushVal $ a == b eval OpEqualVerify= eval OpEqual >> eval OpVerify eval Op1Add= exModifyInt (1+) eval Op1Sub= exModifyInt (‘subtract‘1) eval Op2Mul= exModifyInt (2*) eval Op2Div= exModifyInt (‘div‘2) eval OpNegate= exModifyInt negate eval OpAbs= exModifyInt abs

40 Mental poker for turn-based strategy games – securing fairness without a trusted third party

eval OpNot= exModifyInt (0==) eval Op0NotEqual= exModifyInt (0/=) eval OpAdd= exBinOpInt (+) eval OpSub= exBinOpInt (-) eval OpMul= exBinOpInt (*) eval OpDiv= do x <- exPopVal when (x ==0) ExecInvalid exModifyInt (‘div‘ x) eval OpMod= do x <- exPopVal when (x ==0) ExecInvalid exModifyInt (‘rem‘ x) eval OpPowMod= do m <- exPopVal e <- exPopVal b <- exPopVal when (mineb <0 || maxeb >= m) ExecInvalid exPushVal $ expFastbem eval OpLShift= exPopSize >>= \x -> exModifyInt (‘shiftL‘ x) eval OpRShift= exPopSize >>= \x -> exModifyInt (‘shiftR‘ x) eval OpBoolAnd= exBinOp (&&) eval OpBoolOr= exBinOp (||) eval OpNumEqual= exBinOpInt (==) eval OpNumEqualVerify= eval OpNumEqual >> eval OpVerify eval OpNumNotEqual= exBinOpInt (/=) eval OpLessThan= exBinOpInt (<) eval OpGreaterThan= exBinOpInt (>) eval OpLessThanOrEqual= exBinOpInt (<=) eval OpGreaterThanOrEqual= exBinOpInt (>=) eval OpMin= exBinOpInt min eval OpMax= exBinOpInt max eval OpWithin= do h <- exPopVal l <- exPopVal exModifyInt (\x -> l < x && x < h) eval OpHash160= do MkStackItem x <- exPop exPush . MkStackItem. unpack $ hash160 (packx :: Bytes) eval OpHash256= do MkStackItem x <- exPop exPush . MkStackItem. unpack $ hash256 (packx :: Bytes) eval OpCodeSeparator= use code >>= assign hashCode eval OpCheckSig= do pk <- exPopKey MkTxSig s ht <- exPopSig iv <- use inputValue i <- use inputIx hc <- use hashCode t <- use tx let h = txHash ht ivi hct exPushVal $ verify pkhs eval OpCheckSigVerify= eval OpCheckSig >> eval OpVerify eval OpCheckMultiSig= do nk <- exPopSize ks <- replicateM nk exPopKey ns <- exPopSize ss <- replicateM ns exPopSig iv <- use inputValue

41 Mental poker for turn-based strategy games – securing fairness without a trusted third party

i <- use inputIx hc <- use hashCode t <- use tx exPushVal $ go ivi hct ks ss where go iviot (k:ks’) ss@(MkTxSig s ht:ss’) | verifyk (txHash ht iviot ) s = go iviot ks’ ss’ | otherwise = go iviot ks’ ss go _____[]= True go ______= False eval OpCheckMultiSigVerify= eval OpCheckSig >> eval OpVerify txHash :: Hashtype -> Word64 -> Int -> ByteString -> Transaction -> Hash256 txHash ht@(MkHashtype oh acp) ivi hc (MkTransaction is os nlw )= hash256 . runPut $ do put $ case acp of False -> hash256 . encode $( \(MkInput op _) -> op) <$> is True -> null256 put $ case( acp, oh) of (False, SighashAll) -> hash256 $ encode $( \(MkInput_ ns) -> ns) <$> is _ -> null256 put $ let MkInput op _= is !! i in op put hc put iv put $ case oh of SighashAll -> hash256 $ encode os SighashSingle| i < length os -> hash256 . encode $ os !! i _ -> null256 put nl put ht null256 = MkHash256 d where Just d = digestFromByteString (pack $ replicate 320:: Bytes) B.2 Compile.hs {-# LANGUAGE FlexibleContexts, ImpredicativeTypes, LiberalTypeSynonyms, OverloadedStrings, RankNTypes, TemplateHaskell #-} module Compile where import Blockchain hiding( value) import Control.Monad import Data.Either import Data.Function import Data.List import Data.Monoid import Data.Word import Control.DeepSeq (force) import Control.Lens (Getter, Lens’, Setter’, anyOf, at, contains, each, filtered, makeLenses, over, setting, to, use, uses, view,(%=),(+=),(-=),(.=), (.~),(<%=),(<+=),(<-=),(<<+=),(<>=),(?=),(^.)) import Control.Monad.Except (ExceptT(ExceptT), runExceptT, throwError) import Control.Monad.List (ListT, runListT) import Control.Monad.Random (Rand, StdGen, getSplit, newStdGen, runRand) import Control.Monad.State (StateT(StateT), runStateT) import Control.Monad.Trans (lift) import Data.ByteString (ByteString) import Data.IntMap.Strict (IntMap) import Data.IntSet (IntSet)

42 Mental poker for turn-based strategy games – securing fairness without a trusted third party

import Data.Map.Strict (Map) import Data.Serialize (Put, put, runGet, runPut) import System.Random.Shuffle (shuffleM)

--import qualified Debug.Trace import qualified Control.Monad.State as MS import qualified Data.Array as A import qualified Data.ByteString as BS import qualified Data.IntMap.Strict as IntMap import qualified Data.IntSet as IntSet import qualified Data.Map.Strict as Map data Subscript= ScriptCode ByteString | ScriptHash Hash256 deriving Eq instance Show Subscript where show (ScriptCode c)= "Subscript: " ++ show (let Right x = runGet decodeScriptc in x) show (ScriptHash h)= "Hash: " ++ showh data CompilerState= MkCompilerState{ _refData :: IntMap RefData, _nRefs :: Int, _depth :: Int, _altDepth :: Int, _output ::[Subscript], _opCount :: Int, _marks ::[String], _branch ::[Int], _nextWit :: Ref} deriving(Eq, Show) data RefData= MkRefData{ _stackLocations :: IntSet, _altLocations :: IntSet, _value :: Maybe ByteString, _refCount :: Int, _pushLimit :: Int} deriving(Eq, Show) data Ref= MkRef Int deriving(Eq,Show) type Compile a = StateT CompilerState(ExceptT String(ListT(Rand StdGen))) a makeLenses ’’CompilerState makeLenses ’’RefData traceM :: Monad f => String -> f() traceM = const $ return() -- Debug.Trace.traceM traceShowM ::(Show a, Monad f) => a -> f() traceShowM = const $ return() -- Debug.Trace.traceShowM

43 Mental poker for turn-based strategy games – securing fairness without a trusted third party

emptyCompilerState :: CompilerState emptyCompilerState = MkCompilerState (IntMap.singleton 0( emptyRefData & stackLocations .~ IntSet.singleton (-1)))100[]0[][](MkRef0) emptyRefData :: RefData emptyRefData = MkRefData IntSet.empty IntSet.empty Nothing00 mergeCompiles :: CompilerState -> Map[Int] CompilerState -> Map[Int] CompilerState mergeCompiless = force $ Map.insertWith (\ab -> minimumBy (compare ‘on‘ view badness)[ a, b]) (s ^. branch) s runCompiles :: Int -> Compile a -> StdGen -> CompilerState ->[Map[Int] CompilerState] runCompiles n0c g0s = nub $ go n0 Map.empty g0 where go 0__=[] gonmg = let( es, g’)= runCompilecgs m’ = foldr mergeCompilesm (snd <$> rights es) in m’ : go (n -1) m’ g’ testCompile :: Int -> Compile a ->IO[Map[Int] CompilerState] testCompilenm = do g <- newStdGen return $ runCompilesnmg emptyCompilerState runCompile :: Compile a -> StdGen -> CompilerState ->([Either String( a, CompilerState)], StdGen) runCompilemg = flip runRandg . runListT . runExceptT . runStateTm badness :: Getter CompilerState Int badness = to $ do c <- view opCount s <- view scriptLength return $ c + s -- 50 * c + s scriptLength :: Getter CompilerState Int scriptLength = to $ do os <- view output return . sum $( \s -> 32+ case s of ScriptCode d ->BS. lengthd ScriptHash_ -> 32) <$> os refStacks :: Compile([Int],[Int]) refStacks = do rds <- uses refData IntMap.toList d <- use depth ad <- use altDepth nw <- uses marks length let ms =[( d - i -1, k) |( k, rd) <- rds, i <- IntSet.elems $ rd ^. stackLocations] as =[( ad - i -1, k) |( k, rd) <- rds, i <- IntSet.elems $ rd ^. altLocations] return (f (1+ nw + d) ms, f ad as) where fn =A. elems .A. accumArray (flip const)(-1)(0, n -1) witnessRef :: String -> Compile Ref

44 Mental poker for turn-based strategy games – securing fairness without a trusted third party

witnessRefl = do r@(MkRef i) <- use nextWit p <- getRefDatar when (IntSet.null $ p ^. stackLocations)$ throwError $ "Witness ref " ++ showi ++ " not in stack" ms <- marks <%=( l :) n <- absRef $- length ms -1 nextWit .= n returnr mkRef :: Compile Ref mkRef = do nr <- nRefs <<+=1 refData . at nr ?= emptyRefData return $ MkRef nr

findRef :: Eq b => Lens’ RefData b -> b -> Compile Ref findReflv = do rd <- use refData case find ((v ==). viewl . snd)(IntMap. toList rd) of Just( k,_) -> return $ MkRef k _ -> do r@(MkRef nr) <- mkRef refData . at nr %= fmap (l .~ v) returnr absRef :: Int -> Compile Ref absRefn = findRef (stackLocations . containsn ) True stackRef :: Int -> Compile Ref stackRefn = do d <- uses depth pred absRef $ d - n literal :: ToStackVal a => a -> Compile Ref literalv = do let MkStackItem x = toStackValuev r <- findRef value (Just$BS. packx ) refDataOfr . pushLimit . filtered (>=0) %= succ returnr refOnStack :: RefData -> Bool refOnStackr = not . IntSet.null $ r ^. stackLocations refOnAlt :: RefData -> Bool refOnAltr = not . IntSet.null $ r ^. altLocations refCanPush :: RefData -> Bool refCanPushr = r ^. pushLimit /=0&& r ^. value /= Nothing refCanUse :: RefData -> Bool refCanUser = refOnStackr || refOnAltr || refCanPushr refStackCount :: RefData -> Int refStackCountr = IntSet.size (r ^. stackLocations)+ IntSet. size (r ^. altLocations) refCanPop :: Int -> RefData -> Bool refCanPopnr | pl <0= True | otherwise = sc + pl >= n + r ^. refCount where pl | refCanPushr = r ^. pushLimit

45 Mental poker for turn-based strategy games – securing fairness without a trusted third party

| otherwise =0 sc = refStackCountr refPos :: Ref -> Compile Int refPosr = do rd <- getRefDatar unless (refOnStack rd)$ throwError $ showr ++ " not in stack" d <- uses depth pred return $ d - IntSet.findMax (rd ^. stackLocations) refPopAlt :: Ref -> Compile () refPopAltr = do rd <- getRefDatar unless (refOnAlt rd)$ throwError $ showr ++ " not in altstack" d <- use altDepth replicateM_ (d - IntSet.findMax (rd ^. altLocations))$ op OpFromAltStack refPush :: Ref -> Compile () refPushr = do rd <- getRefDatar unless (refCanPush rd)$ throwError $ showr ++ " has no value to push" d <- use depth refDataOfr . stackLocations . containsd .= True refDataOfr . pushLimit %= pred let Just x = rd ^. value void $ pushx refDataOf :: Ref -> Setter’ CompilerState RefData refDataOf (MkRef i)= setting $ over (refData . ati ). fmap refPick :: Ref -> Compile () refPickr = refPosr >>= opPick refRoll :: Ref -> Compile () refRollr = refPosr >>= opRoll getRefData :: Ref -> Compile RefData getRefDatar @(MkRef i)= use (refData . ati ) >>= maybe (throwError $ showr ++ " not found") return fetch :: Ref -> Compile () fetchr = do rd <- getRefDatar let s | refCanPop 1 rd =[ refRollr ] | otherwise =[ refPickr ] p | refCanPush rd =[ refPushr >> l] | otherwise =[] a | refOnAlt rd =[ refPopAltr >> l] | otherwise =[] l | refCanPop 1 rd = return() | otherwise = void $ op OpDup >> op OpToAltStack variants $ concat [s, p, a] moveToStack :: Ref -> Compile () moveToStackr = do rd <- getRefDatar case () of _| refOnStack rd -> return() | refCanPush rd -> refPushr | refOnAlt rd -> refPopAltr

46 Mental poker for turn-based strategy games – securing fairness without a trusted third party

| otherwise -> throwError $ showr ++ " value not found" fetchUnord ::[Ref] -> Compile () fetchUnord rs = variants [fetch’ rs’ | rs’ <- permutations rs] fetch’ ::[Ref] -> Compile () fetch’ rs = do traceM $ "fetch’ " ++ show rs forM_ rs $ \r -> refDataOfr . refCount -=1 rds <- mapM getRefData rs let nrds = filter (\(_, rd) -> not $( refCanPush rd && refCanPop 1 rd) || refOnStack rd)$ zip rs rds variants [forM_ ps (moveToStack . fst) >> go | trds <- nrds : subsequences nrds, ps <- permutations trds] where go = do l <- uses depth pred rds <- mapM getRefData rs traceM "go" traceM $ "rds = " ++ show rds let rdos = takeWhile refOnStack rds rps = reverse $ ((l -). IntSet. findMax . view stackLocations) <$> rdos nmrps = takeWhile (not .(‘ isPrefixOf‘[0..])). tails $ rps mr = length rps - length nmrps nr = length rs - mr cvs = do rp <- nmrps let x = map fst . filter (not . refCanPop 1. snd). zip rp $ reverse rdos xt <- subsequencesx xtp <- permutations xt xbp <- permutations (x \\ xt) (d, p, c) <- stackMap guard $( xtp ++ rp ++ xbp)‘ isPrefixOf‘( p ++[ d ..]) return . void $ do traceM "cv" opc forM_ xtp $ \_ -> op OpToAltStack go dv = do traceM "dv" traceM $ "rps = " ++ show rps let x = map snd . filter (not . refCanPop 1. fst). take mr $ zip rds rs traceM $ "x = " ++ showx oc <- use opCount traceM $ "opCount = " ++ show oc forMx $ \r -> refPickr >> op OpToAltStack when (nr >0)$ do traceM $ "fetch " ++ show (rs !! mr) fetch $ rs !! mr go if nr ==0&& all (refCanPop 1) rds then return() else do traceM $ "variants (dv : cvs)" variants (dv : cvs) stackMap ::[(Int,[Int], OpCode)] stackMap =[(1,[], OpDrop),

47 Mental poker for turn-based strategy games – securing fairness without a trusted third party

(0,[0], OpDup), (2,[0], OpNip), (0,[1], OpOver), (3,[2,0,1], OpRot), (2,[1,0], OpSwap), (2,[0,1,0], OpTuck), (2,[], Op2Drop), (0,[0,1], Op2Dup), (0,[0,1,2], Op3Dup), (0,[2,3], Op2Over), (6,[4,5,0,1,2,3], Op2Rot), (4,[2,3,0,1], Op2Swap), (1,[], OpToAltStack)] refCat :: Ref -> Ref -> Compile Ref refCatab = variants [fetch’ [a, b] >> head <$> op OpCat, fetch’ [b, a] >> head <$> op OpSwapCat] refDataNull :: RefData -> Bool refDataNull rd = IntSet.null (rd ^. stackLocations)&& IntSet.null (rd ^. altLocations)&& rd ^. value == Nothing variants ::[Compile a] -> Compile a variants ms = do ms’ <- lift . lift . lift $ shuffleM ms go ms’ where go []= traceM "All variants failed" >> throwError "All variants failed" go (n:ns)= do g <- lift . lift . lift $ getSplit s <-MS. get let( es,_)= runCompilengs case rights es of [] -> go ns rs -> msum [MS.put s’ >> returna |( a, s’) <- rs] branches ::[Compile a] -> Compile a branches cs = StateT$ \s -> ExceptT$ msum [runExceptT $ runStateT (branch <>=[ i] >> m) s |( i, m) <- zip [0..] cs] opPick :: Int -> Compile () opPick 0= void $ op OpDup opPick 1= void $ op OpOver opPickn = void $ do pushn >> op OpPick stackDupMovement $ \i -> case i of 0 ->[] _| i == n +1 ->[0, n +1] _| otherwise ->[ i] opRoll :: Int -> Compile () opRoll 0= return() opRoll 1= void $ op OpSwap opRoll 2= void $ op OpRot opRolln = do freezeRefs $ pushn >> op OpRoll stackMovement $ \i -> case () of _| i == n ->0 | i < n -> i +1 | otherwise -> i

48 Mental poker for turn-based strategy games – securing fairness without a trusted third party

return() freezeRefs :: Compile a -> Compile a freezeRefsc = do rd <- use refData d <- use depth ad <- use altDepth r <- c refData .= rd depth .= d altDepth .= ad returnr stackMovement ::(Int -> Int) -> Compile () stackMovementf = do d <- uses depth pred refData . each . stackLocations %= IntSet.map (\i -> d - f (d - i)) stackDupMovement ::(Int ->[Int]) -> Compile () stackDupMovementf = do d <- uses depth pred refData . each . stackLocations %= IntSet.fromList . concat . map (map (d -). f .( d -)). IntSet. toList op :: OpCode -> Compile[Ref] opc = do traceM $ "op " ++ showc writeToScript . put $ OpC c trackOpc writeToScript :: Put -> Compile () writeToScriptx = let b = runPutx in output %= \p -> case p of [] ->[ScriptCode b] (ScriptCode c : z) -> ScriptCode( c <> b): z (ScriptHash_:_) ->(ScriptCode b): p trackOp :: OpCode -> Compile[Ref] trackOpc = do q >> o >> pops ni >> pushes no >> p oc <- opCount <+=1 when (oc > 201)$ throwError "Max op count reached" mapM stackRef [0.. no -1] where o = case c of OpFromAltStack -> do d <- altDepth <-=1 m <- use depth refData . each %= \x -> if x ^. altLocations . containsd then x &( altLocations . containsd ) .~ False &( stackLocations . containsm ) .~ True else x OpToAltStack -> do d <- altDepth <<+=1 m <- uses depth pred refData . each %= \x -> if x ^. stackLocations . containsm then x &( altLocations . containsd ) .~ True else x OpDup -> stackDupMovement $ \i -> case i of 0 ->[-1,0] _ ->[ i] OpOver -> stackDupMovement $ \i -> case i of 1 ->[-1,1]

49 Mental poker for turn-based strategy games – securing fairness without a trusted third party

_ ->[ i] OpTuck -> stackDupMovement $ \i -> case i of 0 ->[-1,1] 1 ->[0] _ ->[ i] Op2Dup -> stackDupMovement $ \i -> case i ‘div‘2 of 0 ->[ i -2, i] _ ->[ i] Op3Dup -> stackDupMovement $ \i -> case i ‘div‘3 of 0 ->[ i -3, i] _ ->[ i] Op2Over -> stackDupMovement $ \i -> case i ‘div‘2 of 1 ->[ i -4, i] _ ->[ i] OpNip -> trackOp OpSwap >> pops 1 OpPick -> stackFilter (0<) OpRoll -> do r <- stackRef 0 rd <- getRefDatar j <- case rd ^. value of Nothing -> traceM "Unknown argument for OpRoll" >> throwError "Unknown argument for OpRoll" Just v -> return . fromInteger . fromStackValue . MkStackItem$ BS.unpackv stackMovement $ \i -> case () of _| i == j +1 ->0 | i <= j -> i +1 | otherwise -> i OpRot -> stackMovement $ \i -> case i of 0 ->1 1 ->2 2 ->0 _ -> i OpSwap -> stackMovement $ \i -> case i of 0 ->1 1 ->0 _ -> i Op2Rot -> stackMovement $ \i -> case () of _| i <4 -> i +2 | i <6 -> i -4 | otherwise -> i Op2Swap -> stackMovement $ \i -> case () of _| i <2 -> i +2 | i <4 -> i -2 | otherwise -> i OpCheckMultiSig -> stackClear OpCheckMultiSigVerify -> stackClear _ -> return() (ni, no)= opInOutsc q = do rds <- use refData when (anyOf each (\rd -> rd ^. refCount >0&& not (refCanUse rd)) rds)$ do traceM "Protected ref disappeared before op" throwError "Protected ref disappeared before op" p = do rds <- use refData when (anyOf each (\rd -> rd ^. refCount >0&& not (refCanUse rd)) rds)$ do traceM "Protected ref disappeared" throwError "Protected ref disappeared"

50 Mental poker for turn-based strategy games – securing fairness without a trusted third party

opInOuts :: OpCode ->(Int, Int) opInOutsc = case c of OpVerify ->(1,0) OpToAltStack ->(1,0) OpFromAltStack ->(0,1) OpDepth ->(0,1) OpDrop ->(1,0) OpDup ->(0,1) OpOver ->(0,1) OpRoll ->(1,0) OpTuck ->(0,1) Op2Drop ->(2,0) Op2Dup ->(0,2) Op3Dup ->(0,3) Op2Over ->(0,2) OpCat ->(2,1) OpSubstr ->(3,1) OpLeft ->(2,1) OpSize ->(0,1) OpSwapCat ->(2,1) OpInvert ->(1,1) OpAnd ->(2,1) OpOr ->(2,1) OpXor ->(2,1) OpEqual ->(2,1) OpEqualVerify ->(2,0) Op1Add ->(1,1) Op1Sub ->(1,1) Op2Mul ->(1,1) Op2Div ->(1,1) OpNegate ->(1,1) OpAbs ->(1,1) OpNot ->(1,1) Op0NotEqual ->(1,1) OpAdd ->(2,1) OpSub ->(2,1) OpMul ->(2,1) OpDiv ->(2,1) OpMod ->(2,1) OpPowMod ->(3,1) OpLShift ->(2,1) OpRShift ->(2,1) OpBoolAnd ->(2,1) OpBoolOr ->(2,1) OpNumEqual ->(2,1) OpNumEqualVerify ->(2,0) OpNumNotEqual ->(2,1) OpLessThan ->(2,1) OpLessThanOrEqual ->(2,1) OpGreaterThan ->(2,1) OpGreaterThanOrEqual ->(2,1) OpMin ->(2,1) OpMax ->(2,1) OpWithin ->(3,1) OpHash160 ->(1,1) OpHash256 ->(1,1) OpCheckSig ->(2,1) OpCheckSigVerify ->(2,0) OpCheckMultiSig ->(0,1) OpCheckLockTimeVerify ->(1,0) OpCheckSequenceVerify ->(1,0)

51 Mental poker for turn-based strategy games – securing fairness without a trusted third party

_ ->(0,0) pops :: Int -> Compile () popsn = do when (n >0)$ stackFilter (n<=) depth -= n pushes :: Int -> Compile () pushesn = depth += n stackFilter ::(Int -> Bool) -> Compile () stackFilterf = do d <- uses depth pred refData . each . stackLocations %= IntSet.filter (\i -> f (d - i)) stackClear :: Compile () stackClear = refData . each . stackLocations .= IntSet.empty scriptHash :: Hash256 -> Compile () scriptHashh = output %=(ScriptHash h :) >> stackClear opPush :: OpPush -> Compile Ref opPusho = do writeToScript $ put . OpP$ o pushes 1 stackRef 0 mkPushOp ::[Word8] -> OpPush mkPushOp []= OpX0 mkPushOp [81]= OpX(-1) mkPushOp [v]| v >0&& v < 17= OpX$ fromIntegralv mkPushOpx | n < 76= OpPushData( fromIntegraln ) d | n < 256= OpPushData1( fromIntegraln ) d | n < 65536= OpPushData2( fromIntegraln ) d | otherwise = OpPushData4( fromIntegraln ) d where n = lengthx d =BS. packx push :: ToStackVal a => a -> Compile Ref pusha = do let MkStackItem x = toStackValuea r <- opPush $ mkPushOpx refDataOfr . value ?=BS. packx returnr opf :: OpCode ->[Ref] -> Compile[Ref] opfo rs = fetch’ rs >> opo opfu :: OpCode ->[Ref] -> Compile[Ref] opfuo rs = fetchUnord rs >> opo opPowMod :: Ref -> Ref -> Ref -> Compile Ref opPowModbem = traceM "opPowMod" >> head <$> opf OpPowMod[ b, e, m] opSub :: Ref -> Ref -> Compile Ref opSubms = traceM "opSub" >> head <$> opf OpSub[ m, s] opMul :: Ref -> Ref -> Compile Ref opMulab = traceM "opMul" >> head <$> opfu OpMul[ a, b] opMod :: Ref -> Ref -> Compile Ref

52 Mental poker for turn-based strategy games – securing fairness without a trusted third party

opModnm = traceM "opMod" >> head <$> opf OpMod[ n, m] opMin :: Ref -> Ref -> Compile Ref opMinab = traceM "opMin" >> head <$> opfu OpMin[ a, b] opNumNotEq :: Ref -> Ref -> Compile Ref opNumNotEqab = head <$> opfu OpNumNotEqual[ a, b] opVerify :: Ref -> Compile () opVerifyx = void $ opf OpVerify[ x] opEqVerify :: Ref -> Ref -> Compile () opEqVerifyab = void $ opfu OpEqualVerify[ a, b] opCheckSigVerify :: Ref -> Ref -> Compile () opCheckSigVerifysk = void $ opf OpCheckSigVerify[ s, k] opHash256 :: Ref -> Compile Ref opHash256m = head <$> opf OpHash256[ m] opNot :: Ref -> Compile Ref opNotx = head <$> opf OpNot[ x] opEq :: Ref -> Ref -> Compile Ref opEqab = head <$> opfu OpEqual[ a, b] opSize :: Ref -> Compile Ref opSizes = head <$> opf OpSize[ s] opWithin :: Ref -> Ref -> Ref -> Compile Ref opWithinxlh = head <$> opf OpWithin[ x, l, h] opGt :: Ref -> Ref -> Compile Ref opGtab = head <$> variants [opf OpGreaterThan[ a, b], opf OpLessThan[ b, a]] opDrop :: Int -> Compile () opDrop 0= return() opDrop 1= void $ op OpDrop opDropn | n >1= op Op2Drop >> opDrop (n -2) opDrop _= throwError "Negative opDrop" cleanStack :: Compile a -> Compile a cleanStackc = do d <- use depth m <- uses marks length r <- c d’ <- use depth m’ <- uses marks length opDrop $ d’ + m’ - d - m returnr unordered ::[Compile ()] -> Compile () unordered = variants . map sequence_ . permutations ref’ :: Int -> Compile Ref -> Compile Ref ref’nc = do r <- c refDataOfr . refCount += n returnr incRef :: Ref -> Compile ()

53 Mental poker for turn-based strategy games – securing fairness without a trusted third party

incRefr = refDataOfr . refCount %=( \n ->1+ max 1 n) B.3 Game.hs {-# LANGUAGE OverloadedStrings #-} module Game where import Blockchain import Compile import Control.Arrow import Control.Monad import Data.Bits import Data.List import Data.Word import Crypto.Error (CryptoFailable(CryptoPassed)) import Crypto.PubKey.Ed25519 (PublicKey, secretKey, toPublic) import Data.Bits.Extras (msb, w32) import Data.ByteString (ByteString) import Data.Ord (comparing) import Data.Serialize (encode) import qualified Data.ByteString as BS data TreeFieldType= SubTree | VarField | Field Int data StateField= StatePrev | StateMessage | StatePrivateA | StatePrivateB deriving(Enum, Show) data PrivateField= PrivateExponent deriving Enum data MessageType= MsgInitPrivate | MsgQEncrypt | MsgQDecrypt | MsgAnswer deriving Enum class Enum a => TreeField a where treeFieldType :: a -> TreeFieldType instance TreeField StateField where treeFieldTypef = case f of StatePrev -> SubTree StateMessage -> VarField StatePrivateA -> SubTree StatePrivateB -> SubTree instance TreeField PrivateField where treeFieldTypef = case f of PrivateExponent -> VarField

fieldSize :: TreeFieldType -> Int

54 Mental poker for turn-based strategy games – securing fairness without a trusted third party

fieldSizet = case t of SubTree -> 32 VarField -> 32 Field s -> s treeFieldTypes :: TreeField a => a ->[TreeFieldType] treeFieldTypesa = treeFieldType <$>[ toEnum 0‘ asTypeOf‘ a ..] treeFieldCounts ::[TreeFieldType] ->[Int] treeFieldCounts ts0 = go 3200 ts0 where go _ c _[]=[ c] gogcn (t:ts)| n ==0= gos (c +1) s ts |( n + s) <= l = go g’ (c +1)( n + s) ts | otherwise = c : go 321 s ts where s = fieldSizet g’ = mings l = 32+ g’ treePathFromIndex :: Int -> Int ->(Int, Word32) treePathFromIndexin | ln <1=(0,0) | msb (w32i ) == ln =( \(d, p) ->( d +1, p ‘setBit‘ d))$ treePathFromIndex (i ‘clearBit‘ ln)( n ‘clearBit‘ ln) | otherwise =( lp, fromIntegrali ) where lp =1+ msb (w32 $ n -1) ln = msb $ w32n treeFieldPos :: TreeField a => a ->((Int, Word32), Int, Int) treeFieldPosf =( treePathFromIndex pf (length fcs), i, c) where fcs = treeFieldCounts $ treeFieldTypesf fi = fromEnumf (pf, cc)= foldr (\n (p, s) -> if s > fi then( p, s) else( p +1, s + n))(-1,0) fcs c = fcs !! pf i = fi - cc + c cTreeFieldsToRoot :: TreeField a => String ->[( a, Ref)] -> Compile Ref cTreeFieldsToRootm fs = do [r] <- cTreeFieldUpdatesToRoots (m, m)( second (\a ->( a, a)) <$> fs) returnr cTreeFieldUpdatesToRoots :: TreeField a =>(String, String) ->[( a,(Ref, Ref))] -> Compile[Ref] -- TODO: The generated code is suboptimal when the new tree contains a -- reference to the old tree. Should be possible to include the -- constructed hash of the old tree when building the new tree and not -- require it to be provided explicitly. cTreeFieldUpdatesToRoots _[]= return [] --e cTreeFieldUpdatesToRoots (ma, mb) fs = go (first treeFieldPos <$> fs) where go [(((-1,_),_,_),(la,lb))]= return $ if la == lb then[ la] else[ la, lb] go (n@((p,_,c),_): pics)= let( dops, ndops)= partition dop pics (ddops, nddops)= partition dp dops in case ddops of [] -> do let ilas =( \((_,i’,_),(l’,_)) ->( i’, l’)) <$>( n : dops) ilbs =( \((_,i’,_),(_,l’)) ->( i’, l’)) <$>( n : dops) mp = "@" ++ show (p ::(Int, Word32))

55 Mental poker for turn-based strategy games – securing fairness without a trusted third party

na <- cAssemble (ma ++ mp)( sortBy (comparing fst) ilas) c la’ <- opHash256 na lb’ <- if ilbs == ilas then return la’ else do nb <- cAssemble (mb ++ mp)( sortBy (comparing fst) ilbs) c opHash256 nb go ((ppic,( la’, lb’)): ndops) _ -> go (ddops ++ n : nddops ++ ndops) where dop ((p’,_,_),_)= let ld = fst p’ - fstp in ld >=0&& sndp == snd p’ ‘shiftR‘ ld dp ((p’,_,_),_)= p’ /= p ppic =(( fstp -1, sndp ‘shiftR‘1), fromIntegral $ sndp .&.1,2) cAssemble :: String ->[(Int, Ref)] -> Int -> Compile Ref cAssemblem ((i0,l0):ps0) c | i0 >0= do e <- witnessRef $ mr 0 i0 l’ <- refCate l0 go (i0 +1) l’ ps0 | otherwise = go 1 l0 ps0 where gobl []| b < c = do e <- witnessRef $ mrbc refCatle | otherwise = returnl gobl ((i,k):ps) | i > b = do e <- witnessRef $ mrbi l’ <- refCatle goi l’ ((i,k):ps) | otherwise = do l’ <- refCatlk go (i +1) l’ ps mrij = m ++ ":" ++ showi ++ if j /= i +1 then ".." ++ show (j -1) else "" cAgree :: PublicKey -> PublicKey -> Compile () -- Verify that signature s matches key a and that signature t matches -- key b. cAgreeab = void $ do t <- witnessRef "t" fetch’ [t] pushb op OpCheckSigVerify s <- witnessRef "s" fetch’ [s] pusha op OpCheckSigVerify cCheckPushScript :: Ref -> Ref -> Compile () -- Verify that h is the hash of the subscript that uses -- OpPush1..OpPush16 to push e onto the stack. cCheckPushScripthe = void $ do s <- ref’ 2$ opSizee lMin <- literal (1:: Int) lMax <- literal (16:: Int) opWithins lMin lMax >>= opVerify --lpc <- literal . BS.take 1 . encode $ OpP (OpPushData1 32 BS.empty) --lps <- refCat lpc s lpe <- refCatse

56 Mental poker for turn-based strategy games – securing fairness without a trusted third party

opHash256 lpe >>= opEqVerifyh cRevealPushScript :: Hash256 -> PublicKey -> PublicKey -> Compile () cRevealPushScripthab = unordered [do lh <- literal $ encodeh e <- ref’ 2$ witnessRef "e" unordered [cCheckPushScripte lh , literal (1:: Int) >>= opGte >>= opVerify], cAgreeab ] cPushHash :: Hash256 -> Compile Ref cPushHashh = do freezeRefs $ scriptHashh pushes 1 stackRef 0 cBadEncryption :: Integer -> Ref -> Ref -> Compile () cBadEncryptionmse = do lm <- literalm fetch lm b <- ref’ 2$ witnessRef "base" r <- ref’ 2$ witnessRef "result" unordered [cVerifyCryptoMismatchbe lmr , cVerifyCryptoMessages MsgQEncrypt brs ] cBadDecryption :: Integer -> Ref -> Ref -> Compile () cBadDecryptionmse = do lm <- literalm fetch lm b <- ref’ 2$ witnessRef "base" r <- ref’ 2$ witnessRef "result" unordered [cVerifyCryptoMismatchre lmb , cVerifyCryptoMessages MsgQDecrypt rbs ] cVerifyCryptoMessages :: MessageType -> Ref -> Ref -> Ref -> Compile () cVerifyCryptoMessages qtbrs = do lqt <- literal $ fromEnum qt bm <- refCat lqtb p <- cTreeFieldsToRoot "prev" [(StateMessage, bm)] lat <- literal $ fromEnum MsgAnswer rm <- refCat latr s’ <- cTreeFieldsToRoot "s" [(StateMessage, rm),(StatePrev, p)] opEqVerifys s’ cVerifyCryptoMismatch :: Ref -> Ref -> Ref -> Ref -> Compile () cVerifyCryptoMismatchbemr = do incRefm r’ <- ref’ 2$ opPowModbem opSubm r’ >>= opMin r’ >>= opNumNotEqr >>= opVerify cEvalInitPrivate :: StateField -> Ref -> Ref -> Compile Ref cEvalInitPrivatefep = do z <- literal (0:: Int) he <- opHash256e [ppt,spt] <- cTreeFieldUpdatesToRoots ("prev." ++ showf , "s." ++ showf ) [(PrivateExponent,( z, he))] hpm <- witnessRef $ "H(prev." ++ show StateMessage ++ ")" pp <- witnessRef $ "prev." ++ show StatePrev sm <- literal $ fromEnum MsgInitPrivate+ 256* fromEnumf hsm <- opHash256 sm [p’,s] <- cTreeFieldUpdatesToRoots ("prev", "s") [(f,( ppt, spt)),(StateMessage,( hpm, hsm)),(StatePrev,( pp, p))]

57 Mental poker for turn-based strategy games – securing fairness without a trusted third party

opEqVerifyp p’ returns cBadEval :: Integer -> StateField -> Ref -> Ref -> Compile () cBadEvalmfse = do p <- witnessRef "prev" s’ <- branches [cEvalInitPrivatefep ] opEqs s’ >>= opNot >>= opVerify cCheat :: Integer -> Hash256 -> Hash256 -> PublicKey -> Compile () cCheatm hesq = cleanStack . void $ do e <- cPushHash he unordered [do sig <- witnessRef "signature" rq <- pushq opCheckSigVerify sig rq, do ls <- push $ encodes -- literal $ encode s branches [cBadEncryptionm lse , cBadDecryptionm lse , cBadEvalm StatePrivateA lse ]] cTest :: Compile () cTest = cCheat modulus h0 h1 pubAlice pubAlice :: PublicKey pubAlice = let CryptoPassed secret = secretKey $ hash256 ("Alice secret" :: ByteString) in toPublic secret pubBob :: PublicKey pubBob = let CryptoPassed secret = secretKey $ hash256 ("Bob secret" :: ByteString) in toPublic secret modulus :: Integer -- From https://www.rfc-editor.org/rfc/rfc2412.txt modulus = read $ concat ["0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1", "29024E088A67CC74020BBEA63B139B22514A08798E3404DD", "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245", "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"] h0 :: Hash256 h0 = hash256 ("h0" :: ByteString) h1 :: Hash256 h1 = hash256 ("h1" :: ByteString)

58 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Glossary

Bitcoin improvement proposal (BIP) Numbered design document proposing changes to or documenting specific parts of Bitcoin. Blockchain A transaction (TX) ledger shared and validated by nodes in a network, where TXs are continuously collected into blocks which are deliberately difficult to create, each new block being based on the latest block in the longest valid chain, making old blocks more and more difficult to revert as new blocks are added.

Contract Agreement between parties consisting of TXs that are linked together and enforced by the blockchain.

Decisional Diffie–Hellman (DDH) In a cyclic group G of order q, the assumption that given ga and gb for ab uniformly and independently chosen a, b 2 Zq, the value g is indistin- guishable from a random element in G.

Fee Part of a TX’s total inputs going to the miner that manages to include the TX in a block. Fractional Brownian motion (fBm) A type of noise that may be approximated by successive octaves of noise with exponentially decaying amplitude.

Hash Fixed-size data derived from arbitrary data via a deterministic function such that different input leads to completely different output with high probability.

59 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Merkle tree A tree in which every non-leaf node is a hash of its child nodes, which means that a leaf node can be demonstrated to be part of a given tree only knowing the sibling nodes along the branch up to the root node.

Merklised abstract syntax tree (MAST) A Merkle tree whose nodes encode individual branches of a script, so the redeeming TX only has to include the evaluated branch of a potentially much larger script.

Quadratic residue a is a quadratic residue modulo m if there exists an integer x such that x2 mod m = a mod m [14].

Real-time strategy (RTS) Like a turn-based strategy (TBS) game, with the main difference that time is continuous and players control their units in real time.

Safe prime A prime of the form 2q + 1, where q is also prime. Security level Estimated amount of resources required to break a cryptosystem, e.g., a security level of 80 bits means that approximately 280 operations are re- quired. Seed Number used to initialise a pseudorandom number generator.

Transaction (TX) Transfer of e.g. Bitcoin value that may be broadcast to the network and which miners may include into the blockchain.

Trusted third party (TTP) Entity which facilitates interactions between parties who trust the third party. Turing completeness The ability of a system to simulate a universal Turing machine or any other Turing complete system, which includes all commonly used general- purpose programming languages.

60 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Turn-based strategy (TBS) A genre of video games played in turns, where each player typically con- trols their own set of units and must use the units to e.g. explore the game world, collect resources, build infrastructure and combat against opponent units.

61 Mental poker for turn-based strategy games – securing fairness without a trusted third party

Bibliography

[1] Ernest Adams. Fundamentals of game design. Pearson Education, 2013, p. 510. [2] David Adrian et al. Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice. 2015. URL: https : / / weakdh . org / imperfect - forward - secrecy.pdf (visited on 28/05/2015). [3] Marcin Andrychowicz et al. “Secure multiparty computations on Bitcoin”. In: Security and Privacy (SP), 2014 IEEE Symposium on. IEEE. 2014, pp. 443–458. [4] Iddo Bentov and Ranjit Kumaresan. “How to use Bitcoin to design fair protocols”. In: Advances in Cryptology–CRYPTO 2014. Springer, 2014, pp. 421–439. [5] Bitcoin Wiki. Contracts. 2015. URL: https://en.bitcoin.it/w/index. php?title=Contracts&oldid=53752. [6] Dan Boneh. “The decision Diffie-Hellman problem”. In: Algorithmic num- ber theory. Springer, 1998, pp. 48–63. [7] Elie Bursztein et al. “OpenConflict: Preventing real time map hacks in online games”. In: Security and Privacy (SP), 2011 IEEE Symposium on. IEEE. 2011, pp. 506–520. [8] Vitalik Butterin et al. A next-generation smart contract and decentralized application platform. 2014. [9] Chris Chambers et al. “Mitigating information exposure to cheaters in real- time strategy games”. In: Proceedings of the international workshop on Net- work and operating systems support for digital audio and video. ACM. 2005, pp. 7–12. [10] Whitfield Diffie and Martin E Hellman. “New directions in cryptography”. In: Information Theory, IEEE Transactions on 22.6 (1976), pp. 644–654.

62 Mental poker for turn-based strategy games – securing fairness without a trusted third party

[11] fgrieu. A: Cryptographic system with double keys with reversible order. 2014. URL: http : / / crypto . stackexchange . com / a / 15293 (visited on 12/12/2016). [12] Alain Fournier, Don Fussell and Loren Carpenter. “Computer rendering of stochastic models”. In: Communications of the ACM 25.6 (1982), pp. 371– 384. [13] Oded Goldreich, and Avi Wigderson. “How to play any men- tal game”. In: Proceedings of the nineteenth annual ACM symposium on Theory of computing. ACM. 1987, pp. 218–229. [14] Kenneth Ireland and Michael Ira Rosen. A classical introduction to modern number theory. Quadratic reciprocity. Springer Science & Business Media, 1990. Chap. 5. [15] Johnson Lau. Merkelized Abstract Syntax Tree. BIP 114. Bitcoin Project, 2016. URL: https://github.com/bitcoin/bips/blob/9da1f65e39 0ca18c1d63abea8c4a112a6c95049d/bip-0114.mediawiki (visited on 18/11/2016). [16] Orisi. Orisi White Paper. 2014. URL: https://github.com/orisi/wiki/ wiki/Orisi-White-Paper. [17] Hilarie Orman. The OAKLEY key determination protocol. RFC 2412. RFC Editor, Nov. 1998. URL: http://www.rfc-editor.org/rfc/rfc2412. txt (visited on 29/05/2015). [18] Stephen C Pohlig and Martin E Hellman. “An improved algorithm for computing logarithms over GF(p) and its cryptographic significance (Cor- resp.)” In: Information Theory, IEEE Transactions on 24.1 (1978), pp. 106– 110. [19] Stephen L Rice. Secure Map Generation for Multiplayer, Turn-Based Strategy Games. University of Denver. 2014. [20] , Ronald L Rivest and Leonard M Adleman. Mental poker. Springer, 1981. URL: http : / / people . csail . mit . edu / ~rivest / ShamirRivestAdleman-MentalPoker.pdf. [21] Heiko Stamer. Bibliography on Mental Poker. 2007. URL: http://archive .cone.informatik.uni-freiburg.de/teaching/teamprojekt/dog- w10/literature/MentalPoker-survey.pdf.

63