From 6312e74dde0ac3b136b63e89051e8fd3ba8e3dfc Mon Sep 17 00:00:00 2001 From: cusma Date: Tue, 8 Nov 2022 12:26:50 +0100 Subject: [PATCH] Only "real" ideal dices. --- README.md | 17 ++++++++++++----- algodices.py | 6 +++++- algodices_dapp.py | 15 ++++++++++++++- artifacts/application.json | 6 +++--- artifacts/approval.teal | 33 +++++++++++++++++++++++++++++++-- artifacts/contract.json | 4 ++-- 6 files changed, 67 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3cad69e..c43228f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Let's roll your dices on Algorand! ## Overview -[AlgoDices](https://testnet.algoexplorer.io/application/120974808) application +[AlgoDices](https://testnet.algoexplorer.io/application/121287966) application let players **roll dices on-chain** (TestNet), thanks the [*Randomness Beacon Application*](https://developer.algorand.org/articles/usage-and-best-practices-for-randomness-beacon/). Starting from AVM 7, Algorand enables [turstless randomness on chain](https://developer.algorand.org/articles/randomness-on-algorand), @@ -55,9 +55,9 @@ mandate `future_round` to be greater than current round plus `N`. the previous booked randomness is unused. ### Let's roll a die! -Once the booked randomness is available, players can roll a die of **any number -of faces** (e.g. usally `d4`, `d6`, `d8`, `d10`, `d12`, `d20`, `d100` are -common dices in board games). +Once the booked randomness is available, players can roll a die with a *"real" +number of faces* (`d4`, `d6`, `d8`, `d10`, `d12`, `d20`, are common dices in +board games). ⚠️ Players should wait at least for `future_round + 8` round to be sure that their booked randomness is available in the Randomness Beacon App. @@ -74,7 +74,7 @@ their booked randomness is available in the Randomness Beacon App. { "type": "uint64", "name": "faces", - "desc": "Number of faces (e.g. 2, 4, 6, 8, 10, 12, 20, 100, ...)" + "desc": "Number of faces (2, 4, 6, 8, 10, 12, 20)" } ], "returns": { @@ -90,6 +90,13 @@ AlgoDice `die_roll` returns an array containing: 2. Die's faces; 3. The result. +## 🎰 Games +On-chain games can _call_ AlgoDices imposing their own rules, for example: +a `GameApp` calls `roll_die` two times (on behalf of players Alice and Bob +participating in the game thourgh proxy Contract Accounts controlled by +GameApp) as Inner Transactions executed in the same GameApp Call, requiring +`faces = 6`. The highest result wins the match. + ## CLI You can easily roll a die with the Python 🎲 AlgoDices CLI. diff --git a/algodices.py b/algodices.py index bbed555..1467008 100644 --- a/algodices.py +++ b/algodices.py @@ -29,8 +29,9 @@ from algodices_dapp import AlgoDices -ALGO_DICES_APP_ID = 120974808 +ALGO_DICES_APP_ID = 121287966 RANDOMNESS_BEACON_DELAY = 8 +FACES = [2, 4, 6, 8, 10, 12, 20] def args_types(args: dict) -> dict: @@ -87,6 +88,9 @@ def main(): return print(f" --- Result can be revealed from round: {reveal_round}") elif args["roll"]: + if args[""] not in FACES: + quit(f"\n⚠️ Number of faces must be either: {FACES}") + current_round = testnet.algod().status()["last-round"] booked_round = algo_dices.get_account_state(user_address)[ AlgoDices.randomness_round.key.byte_str[1:-1] diff --git a/algodices_dapp.py b/algodices_dapp.py index 190f124..b9f9e75 100644 --- a/algodices_dapp.py +++ b/algodices_dapp.py @@ -82,7 +82,7 @@ def roll_die( Args: randomness_beacon: Randomness Beacon App ID (110096026) - faces: Number of faces (e.g. 2, 4, 6, 8, 10, 12, 20, 100, ...) + faces: Number of faces (2, 4, 6, 8, 10, 12, 20) Returns: Die roll: (booked_round, faces, result) @@ -90,11 +90,24 @@ def roll_die( randomness = Btoi( Extract(string=self.get_randomness(), start=Int(0), length=Int(8)) ) + is_valid_faces = Or( + faces.get() == Int(2), + faces.get() == Int(4), + faces.get() == Int(6), + faces.get() == Int(8), + faces.get() == Int(10), + faces.get() == Int(12), + faces.get() == Int(20), + ) return Seq( Assert( randomness_beacon.application_id() == self.beacon_app_id, comment="Randomness Beacon App ID must be correct.", ), + Assert( + is_valid_faces, + comment="Number of faces must be equal to real ideal dices.", + ), (booked_round := abi.Uint64()).set(self.randomness_round[Txn.sender()]), (result := abi.Uint64()).set(randomness % faces.get() + Int(1)), self.randomness_round[Txn.sender()].set_default(), diff --git a/artifacts/application.json b/artifacts/application.json index e256a89..b23e699 100644 --- a/artifacts/application.json +++ b/artifacts/application.json @@ -1,7 +1,7 @@ { "hints": {}, "source": { - "approval": "I3ByYWdtYSB2ZXJzaW9uIDcKaW50Y2Jsb2NrIDAgMQpieXRlY2Jsb2NrIDB4NzI2MTZlNjQ2ZjZkNmU2NTczNzM1ZjcyNmY3NTZlNjQgMHg2MjY1NjE2MzZmNmU1ZjYxNzA3MDVmNjk2NAp0eG4gTnVtQXBwQXJncwppbnRjXzAgLy8gMAo9PQpibnogbWFpbl9sNgp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweGQ0YjE3OWNjIC8vICJib29rX2RpZV9yb2xsKHVpbnQ2NCl2b2lkIgo9PQpibnogbWFpbl9sNQp0eG5hIEFwcGxpY2F0aW9uQXJncyAwCnB1c2hieXRlcyAweDZhMTYzNGQzIC8vICJyb2xsX2RpZShhcHBsaWNhdGlvbix1aW50NjQpdWludDY0WzNdIgo9PQpibnogbWFpbl9sNAplcnIKbWFpbl9sNDoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQppbnRjXzAgLy8gMApnZXRieXRlCnN0b3JlIDIKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMgpidG9pCnN0b3JlIDMKbG9hZCAyCmxvYWQgMwpjYWxsc3ViIHJvbGxkaWVfNApzdG9yZSA0CnB1c2hieXRlcyAweDE1MWY3Yzc1IC8vIDB4MTUxZjdjNzUKbG9hZCA0CmNvbmNhdApsb2cKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDU6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKIT0KJiYKYXNzZXJ0CnR4bmEgQXBwbGljYXRpb25BcmdzIDEKYnRvaQpjYWxsc3ViIGJvb2tkaWVyb2xsXzMKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDY6CnR4biBPbkNvbXBsZXRpb24KaW50Y18wIC8vIE5vT3AKPT0KYm56IG1haW5fbDEwCnR4biBPbkNvbXBsZXRpb24KaW50Y18xIC8vIE9wdEluCj09CmJueiBtYWluX2w5CmVycgptYWluX2w5Ogp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQphc3NlcnQKY2FsbHN1YiBvcHRpbl8xCmludGNfMSAvLyAxCnJldHVybgptYWluX2wxMDoKdHhuIEFwcGxpY2F0aW9uSUQKaW50Y18wIC8vIDAKPT0KYXNzZXJ0CmNhbGxzdWIgY3JlYXRlXzAKaW50Y18xIC8vIDEKcmV0dXJuCgovLyBjcmVhdGUKY3JlYXRlXzA6CmludGNfMCAvLyAwCmJ5dGVjXzEgLy8gImJlYWNvbl9hcHBfaWQiCmFwcF9nbG9iYWxfZ2V0X2V4CnN0b3JlIDEKc3RvcmUgMApsb2FkIDEKIQphc3NlcnQKYnl0ZWNfMSAvLyAiYmVhY29uX2FwcF9pZCIKcHVzaGludCAxMTAwOTYwMjYgLy8gMTEwMDk2MDI2CmFwcF9nbG9iYWxfcHV0CnJldHN1YgoKLy8gb3B0X2luCm9wdGluXzE6CnR4biBTZW5kZXIKYnl0ZWNfMCAvLyAicmFuZG9tbmVzc19yb3VuZCIKaW50Y18wIC8vIDAKYXBwX2xvY2FsX3B1dApyZXRzdWIKCi8vIGdldF9yYW5kb21uZXNzCmdldHJhbmRvbW5lc3NfMjoKdHhuIFNlbmRlcgpieXRlY18wIC8vICJyYW5kb21uZXNzX3JvdW5kIgphcHBfbG9jYWxfZ2V0CnN0b3JlIDkKdHhuIFNlbmRlcgpzdG9yZSAxMApsb2FkIDEwCmxlbgppdG9iCmV4dHJhY3QgNiAwCmxvYWQgMTAKY29uY2F0CnN0b3JlIDEwCml0eG5fYmVnaW4KcHVzaGludCA2IC8vIGFwcGwKaXR4bl9maWVsZCBUeXBlRW51bQpieXRlY18xIC8vICJiZWFjb25fYXBwX2lkIgphcHBfZ2xvYmFsX2dldAppdHhuX2ZpZWxkIEFwcGxpY2F0aW9uSUQKcHVzaGJ5dGVzIDB4NDdjMjBjMjMgLy8gIm11c3RfZ2V0KHVpbnQ2NCxieXRlW10pYnl0ZVtdIgppdHhuX2ZpZWxkIEFwcGxpY2F0aW9uQXJncwpsb2FkIDkKaXRvYgppdHhuX2ZpZWxkIEFwcGxpY2F0aW9uQXJncwpsb2FkIDEwCml0eG5fZmllbGQgQXBwbGljYXRpb25BcmdzCmludGNfMCAvLyAwCml0eG5fZmllbGQgRmVlCml0eG5fc3VibWl0Cml0eG4gTGFzdExvZwpleHRyYWN0IDQgMApyZXRzdWIKCi8vIGJvb2tfZGllX3JvbGwKYm9va2RpZXJvbGxfMzoKc3RvcmUgOApsb2FkIDgKZ2xvYmFsIFJvdW5kCj4KLy8gRGllIHJvbGwgcm91bmQgbXVzdCBiZSBpbiB0aGUgZnV0dXJlLgphc3NlcnQKdHhuIFNlbmRlcgpieXRlY18wIC8vICJyYW5kb21uZXNzX3JvdW5kIgpsb2FkIDgKYXBwX2xvY2FsX3B1dApyZXRzdWIKCi8vIHJvbGxfZGllCnJvbGxkaWVfNDoKc3RvcmUgNQp0eG5hcyBBcHBsaWNhdGlvbnMKYnl0ZWNfMSAvLyAiYmVhY29uX2FwcF9pZCIKYXBwX2dsb2JhbF9nZXQKPT0KLy8gUmFuZG9tbmVzcyBCZWFjb24gQXBwIElEIG11c3QgYmUgY29ycmVjdC4KYXNzZXJ0CnR4biBTZW5kZXIKYnl0ZWNfMCAvLyAicmFuZG9tbmVzc19yb3VuZCIKYXBwX2xvY2FsX2dldApzdG9yZSA2CmNhbGxzdWIgZ2V0cmFuZG9tbmVzc18yCmV4dHJhY3QgMCA4CmJ0b2kKbG9hZCA1CiUKaW50Y18xIC8vIDEKKwpzdG9yZSA3CnR4biBTZW5kZXIKYnl0ZWNfMCAvLyAicmFuZG9tbmVzc19yb3VuZCIKaW50Y18wIC8vIDAKYXBwX2xvY2FsX3B1dApsb2FkIDYKaXRvYgpsb2FkIDUKaXRvYgpjb25jYXQKbG9hZCA3Cml0b2IKY29uY2F0CnJldHN1Yg==", + "approval": "I3ByYWdtYSB2ZXJzaW9uIDcKaW50Y2Jsb2NrIDAgMSA2CmJ5dGVjYmxvY2sgMHg3MjYxNmU2NDZmNmQ2ZTY1NzM3MzVmNzI2Zjc1NmU2NCAweDYyNjU2MTYzNmY2ZTVmNjE3MDcwNWY2OTY0CnR4biBOdW1BcHBBcmdzCmludGNfMCAvLyAwCj09CmJueiBtYWluX2w2CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4ZDRiMTc5Y2MgLy8gImJvb2tfZGllX3JvbGwodWludDY0KXZvaWQiCj09CmJueiBtYWluX2w1CnR4bmEgQXBwbGljYXRpb25BcmdzIDAKcHVzaGJ5dGVzIDB4NmExNjM0ZDMgLy8gInJvbGxfZGllKGFwcGxpY2F0aW9uLHVpbnQ2NCl1aW50NjRbM10iCj09CmJueiBtYWluX2w0CmVycgptYWluX2w0Ogp0eG4gT25Db21wbGV0aW9uCmludGNfMCAvLyBOb09wCj09CnR4biBBcHBsaWNhdGlvbklECmludGNfMCAvLyAwCiE9CiYmCmFzc2VydAp0eG5hIEFwcGxpY2F0aW9uQXJncyAxCmludGNfMCAvLyAwCmdldGJ5dGUKc3RvcmUgMgp0eG5hIEFwcGxpY2F0aW9uQXJncyAyCmJ0b2kKc3RvcmUgMwpsb2FkIDIKbG9hZCAzCmNhbGxzdWIgcm9sbGRpZV80CnN0b3JlIDQKcHVzaGJ5dGVzIDB4MTUxZjdjNzUgLy8gMHgxNTFmN2M3NQpsb2FkIDQKY29uY2F0CmxvZwppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sNToKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAohPQomJgphc3NlcnQKdHhuYSBBcHBsaWNhdGlvbkFyZ3MgMQpidG9pCmNhbGxzdWIgYm9va2RpZXJvbGxfMwppbnRjXzEgLy8gMQpyZXR1cm4KbWFpbl9sNjoKdHhuIE9uQ29tcGxldGlvbgppbnRjXzAgLy8gTm9PcAo9PQpibnogbWFpbl9sMTAKdHhuIE9uQ29tcGxldGlvbgppbnRjXzEgLy8gT3B0SW4KPT0KYm56IG1haW5fbDkKZXJyCm1haW5fbDk6CnR4biBBcHBsaWNhdGlvbklECmludGNfMCAvLyAwCiE9CmFzc2VydApjYWxsc3ViIG9wdGluXzEKaW50Y18xIC8vIDEKcmV0dXJuCm1haW5fbDEwOgp0eG4gQXBwbGljYXRpb25JRAppbnRjXzAgLy8gMAo9PQphc3NlcnQKY2FsbHN1YiBjcmVhdGVfMAppbnRjXzEgLy8gMQpyZXR1cm4KCi8vIGNyZWF0ZQpjcmVhdGVfMDoKaW50Y18wIC8vIDAKYnl0ZWNfMSAvLyAiYmVhY29uX2FwcF9pZCIKYXBwX2dsb2JhbF9nZXRfZXgKc3RvcmUgMQpzdG9yZSAwCmxvYWQgMQohCmFzc2VydApieXRlY18xIC8vICJiZWFjb25fYXBwX2lkIgpwdXNoaW50IDExMDA5NjAyNiAvLyAxMTAwOTYwMjYKYXBwX2dsb2JhbF9wdXQKcmV0c3ViCgovLyBvcHRfaW4Kb3B0aW5fMToKdHhuIFNlbmRlcgpieXRlY18wIC8vICJyYW5kb21uZXNzX3JvdW5kIgppbnRjXzAgLy8gMAphcHBfbG9jYWxfcHV0CnJldHN1YgoKLy8gZ2V0X3JhbmRvbW5lc3MKZ2V0cmFuZG9tbmVzc18yOgp0eG4gU2VuZGVyCmJ5dGVjXzAgLy8gInJhbmRvbW5lc3Nfcm91bmQiCmFwcF9sb2NhbF9nZXQKc3RvcmUgOQp0eG4gU2VuZGVyCnN0b3JlIDEwCmxvYWQgMTAKbGVuCml0b2IKZXh0cmFjdCA2IDAKbG9hZCAxMApjb25jYXQKc3RvcmUgMTAKaXR4bl9iZWdpbgppbnRjXzIgLy8gYXBwbAppdHhuX2ZpZWxkIFR5cGVFbnVtCmJ5dGVjXzEgLy8gImJlYWNvbl9hcHBfaWQiCmFwcF9nbG9iYWxfZ2V0Cml0eG5fZmllbGQgQXBwbGljYXRpb25JRApwdXNoYnl0ZXMgMHg0N2MyMGMyMyAvLyAibXVzdF9nZXQodWludDY0LGJ5dGVbXSlieXRlW10iCml0eG5fZmllbGQgQXBwbGljYXRpb25BcmdzCmxvYWQgOQppdG9iCml0eG5fZmllbGQgQXBwbGljYXRpb25BcmdzCmxvYWQgMTAKaXR4bl9maWVsZCBBcHBsaWNhdGlvbkFyZ3MKaW50Y18wIC8vIDAKaXR4bl9maWVsZCBGZWUKaXR4bl9zdWJtaXQKaXR4biBMYXN0TG9nCmV4dHJhY3QgNCAwCnJldHN1YgoKLy8gYm9va19kaWVfcm9sbApib29rZGllcm9sbF8zOgpzdG9yZSA4CmxvYWQgOApnbG9iYWwgUm91bmQKPgovLyBEaWUgcm9sbCByb3VuZCBtdXN0IGJlIGluIHRoZSBmdXR1cmUuCmFzc2VydAp0eG4gU2VuZGVyCmJ5dGVjXzAgLy8gInJhbmRvbW5lc3Nfcm91bmQiCmxvYWQgOAphcHBfbG9jYWxfcHV0CnJldHN1YgoKLy8gcm9sbF9kaWUKcm9sbGRpZV80OgpzdG9yZSA1CnR4bmFzIEFwcGxpY2F0aW9ucwpieXRlY18xIC8vICJiZWFjb25fYXBwX2lkIgphcHBfZ2xvYmFsX2dldAo9PQovLyBSYW5kb21uZXNzIEJlYWNvbiBBcHAgSUQgbXVzdCBiZSBjb3JyZWN0Lgphc3NlcnQKbG9hZCA1CnB1c2hpbnQgMiAvLyAyCj09CmxvYWQgNQpwdXNoaW50IDQgLy8gNAo9PQp8fApsb2FkIDUKaW50Y18yIC8vIDYKPT0KfHwKbG9hZCA1CnB1c2hpbnQgOCAvLyA4Cj09Cnx8CmxvYWQgNQpwdXNoaW50IDEwIC8vIDEwCj09Cnx8CmxvYWQgNQpwdXNoaW50IDEyIC8vIDEyCj09Cnx8CmxvYWQgNQpwdXNoaW50IDIwIC8vIDIwCj09Cnx8Ci8vIE51bWJlciBvZiBmYWNlcyBtdXN0IGJlIGVxdWFsIHRvIHJlYWwgaWRlYWwgZGljZXMuCmFzc2VydAp0eG4gU2VuZGVyCmJ5dGVjXzAgLy8gInJhbmRvbW5lc3Nfcm91bmQiCmFwcF9sb2NhbF9nZXQKc3RvcmUgNgpjYWxsc3ViIGdldHJhbmRvbW5lc3NfMgpleHRyYWN0IDAgOApidG9pCmxvYWQgNQolCmludGNfMSAvLyAxCisKc3RvcmUgNwp0eG4gU2VuZGVyCmJ5dGVjXzAgLy8gInJhbmRvbW5lc3Nfcm91bmQiCmludGNfMCAvLyAwCmFwcF9sb2NhbF9wdXQKbG9hZCA2Cml0b2IKbG9hZCA1Cml0b2IKY29uY2F0CmxvYWQgNwppdG9iCmNvbmNhdApyZXRzdWI=", "clear": "I3ByYWdtYSB2ZXJzaW9uIDcKcHVzaGludCAwIC8vIDAKcmV0dXJu" }, "schema": { @@ -54,7 +54,7 @@ { "type": "uint64", "name": "faces", - "desc": "Number of faces (e.g. 2, 4, 6, 8, 10, 12, 20, 100, ...)" + "desc": "Number of faces (2, 4, 6, 8, 10, 12, 20)" } ], "returns": { @@ -65,7 +65,7 @@ } ], "networks": { - "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=": { "appID": 120974808 } + "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=": { "appID": 121287966 } }, "desc": "Algorand Dices Application" } diff --git a/artifacts/approval.teal b/artifacts/approval.teal index 1984401..be1c00c 100644 --- a/artifacts/approval.teal +++ b/artifacts/approval.teal @@ -1,5 +1,5 @@ #pragma version 7 -intcblock 0 1 +intcblock 0 1 6 bytecblock 0x72616e646f6d6e6573735f726f756e64 0x626561636f6e5f6170705f6964 txn NumAppArgs intc_0 // 0 @@ -120,7 +120,7 @@ load 10 concat store 10 itxn_begin -pushint 6 // appl +intc_2 // appl itxn_field TypeEnum bytec_1 // "beacon_app_id" app_global_get @@ -162,6 +162,35 @@ app_global_get == // Randomness Beacon App ID must be correct. assert +load 5 +pushint 2 // 2 +== +load 5 +pushint 4 // 4 +== +|| +load 5 +intc_2 // 6 +== +|| +load 5 +pushint 8 // 8 +== +|| +load 5 +pushint 10 // 10 +== +|| +load 5 +pushint 12 // 12 +== +|| +load 5 +pushint 20 // 20 +== +|| +// Number of faces must be equal to real ideal dices. +assert txn Sender bytec_0 // "randomness_round" app_local_get diff --git a/artifacts/contract.json b/artifacts/contract.json index 9bdcea3..1345802 100644 --- a/artifacts/contract.json +++ b/artifacts/contract.json @@ -26,7 +26,7 @@ { "type": "uint64", "name": "faces", - "desc": "Number of faces (e.g. 2, 4, 6, 8, 10, 12, 20, 100, ...)" + "desc": "Number of faces (2, 4, 6, 8, 10, 12, 20)" } ], "returns": { @@ -37,7 +37,7 @@ } ], "networks": { - "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=": { "appID": 120974808 } + "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=": { "appID": 121287966 } }, "desc": "Algorand Dices Application" }