Dapp-a-day 15: Maker Darts

in #ethereum8 years ago

dapp-a-day-15 2x


Today's dapp is a bit bigger than the dapps we've covered previous days,
so this post is commensurately longer.

Maker Darts is a RANDAO implemented as a
gambling game where participants are incentivized to both provide "as random as
possible" input and to punish participants who are insufficiently random. It's a
fairly complex set of contracts developed with an eye toward both allowing
simplifying layers to be built on top and ferreting out any potential
misalignments of incentive which might detract from the randomness of the
RANDAO's output.

There are simpler RANDAOs out there, but this one is significantly less trusting
than those. No oracles are consulted, and user experience has been sacrificed in
the name of flexibility and security at every possible opportunity. (Hence the
eye toward allowing others to build simplifying layers on top of this!)

This dapp works as a good example of how one might use
Nexus Development's ERC20
smart contracts.

A high-level description of the game's mechanics from the README:

A game is initiated when a user posts a bet of arbitrary size, a hash of a
salt value plus a target value, the minimum number of other participants they
desire, the winner's reward (an integer percentage of the losers' bets the
winners may claim), the number of winners this game, the lengths in blocks of
the commitment, revelation, and tabulation rounds, and the symbol of the token
being used for placing bets. They may also optionally offer a fee to
incentivize participants with lower risk tolerance to join. (It is not
recommended that the fee exceed half the size of the bet, as this attenuates
the incentive participants have to provide unpredictable input.)

To join a game, a prospective participant must post a bet equal to the bet of
the person who created the game, a hash of a salt value plus a target value,
and the ID of the game they wish to join.

New participants may only join during the commitment round. If the commitment
round ends without enough participants joining, the round is cancelled and
participants may request refunds.

Once the commitment round ends, the revelation round begins. During this
round, participants submit their salts and target values. Following this, each
participant calls a function to calculate their random number. Each random
number consists of the salts and targets of every other participant sha3'ed
together in order of commitment hash, with the salt and target of the calling
participant transposed to the beginning of the list.

Though at this point each participant has received a unique random number from
the DAO, the smart contract still holds the participants' bets. At this point,
the half of the participants whose chosen targets end up furthest from their
random numbers may call a function to reclaim half their bet. The half closest
to their targets call the same function to reclaim their original bet plus
half, as well as the bets of users who submitted commitment transactions but
then failed to reveal. The function also distributes participation fees to
each player. Participants have a number of blocks equal to the sum of the
commitment, revelation, and tabulation round lengths to call this function.
Any bets and fees not collected during this time are lost forever.

As described above, Maker Darts provides the unique mechanic of allowing users
to directly "beat the house" by better predicting the output of a round than the
other players. The whole game, after all, boils down to one of predicting the
RANDAO's output. Particpants may be incentivized to participate, and the game's
creator may even choose to waive the winner's reward, which would of course
amount to paying people to give you any input whatsoever and is thus not
particularly recommended. (Why should I waste the cycles to submit a random
number if I'm not disincentivized from just submitting '0' along with everyon
else? Ideally Maker Darts becomes the haunt of degenerate gamblers armed with
hardware RNGs and huge neural networks to suss out correlations in the RANDAO's
inputs and eventual output. Ideally.)

Returning to the format of previous posts, there are two primary contracts
involved in making Maker Darts work.

The lobby,
as the name suggests, allows people with similar preferences regarding game
settings to find each other. It also provides two MakerDartsGame factory
functions. The first, createZeroSumGame, creates a simple 5 participant game
with no participation reward, 12 block rounds, 3 winners, and 50% of the losing
players' bets being distributed to the winners (the other 50% being held as
incentive to report in despite an obvious loss). It takes the size of the bet
which each participant must make as its first parameter, expressed in the
smallest unit of the ERC20 token specified via the function's second parameter,
the address of an ERC20 token, and a boolean representing whether to create the
game in debug mode as its third. It returns the address of the resulting game.

Note: the third parameter should always be false unless you're running unit
tests and need to be able to fast-forward the game by spoofing the current block
number. This parameter is exposed on the game contract as the public debug
property. When playing the game live, never join a game whose debug property
is set to true! End-user-facing UIs should hide such games entirely.

The second, createGame, takes the same parameters as the other function and
also returns a game address. However, it does not set any of the game's other
properties. The caller must call the necessary set functions themself.

The lobby emits a GamePending event with the game's address upon game creation.

The game
encapsulates a single Maker Darts game. Breaking apart the lobby and the game
contract like this allows a single game to potentially break without breaking
any other games. End-user-facing UIs can then change in order to remove and
disallow any potentially problematic games in the future.

Each game has an owner whose job it is to set up and start the game. Game set up
consists of calling the following functions in any order:

  • setParticipants(uint256): The number of participants this game will accept
    and needs before it can be started.
  • setParticipantReward(uint256): The amount to pay each participant for
    participating. Can be useful for attracting participants faster.
  • setCommitmentBlocks(uint256): How long the commmitment round will be, in
    blocks.
  • setRevealBlocks(uint256): How long the reveal round will be, in blocks.
  • setCalculationBlocks(uint256): How long the calculation round will be, in
    blocks.
  • setWinnerCut(uint8): What percentage of each loser's bet will be put into
    the winner's pot.
  • setWinners(uint256): How many winners will be chosen.

createZeroSumGame handles calling all these functions for the game owner, but
they may call any of them again in order to change any of the game's parameters.

At this point, the game owner may start the commitment round by first granting
the game an allowance on the ERC20 token of betSize + participants * participantReward and then calling startGame with their own commitment hash.
The game will emit a GameStarted event. The game's parameters are locked at
this point.

Participants may then join by granting an allowance on the chosen ERC20 token to
the game of bidSize and calling joinGame with their commitment hash.

If the game fails to attract enough participants during the commitment round, or
if the game fails such that nobody can claim their winnings after all the rounds
have completed, participants may call the requestRefund function with their
commitment hash and get back the money they put into it. The requestRefund
function will only pay out in the event of a stalled game after twice the sum of
each round's blocks has passed. This gives winners time to prove the game isn't
stalled by claiming their rewards. Once a reward has been claimed, no refund may
be claimed. In addition, if the game stalls and rewards are paid out, the game
owner forfeits the portion of their stake set aside for participant rewards and
the participants are granted their promised rewards.

A Commit event is emitted each time a new player joins.

Once the commitment round is past, the reveal round begins. Participants must
then call revealBet with their commitment hash, the target number they bet on
(as a bytes32 value), and the salt they added to the number to obfuscate their
guess (which, together with the target, constitutes the commitment hash's
preimage).

A Reveal event is emitted each time a player reveals.

After the reveal round ends, the calculation round begins. Each participant
calls calculateResult with their commitment hash, receiving a unique random
number. The unique random number is bitwise XOR'ed with the user's target. The
participants with the smallest resulting numbers win.

A Result event is emitted each time a player calculates their random number.

Finally, once the reveal round ends, participants may call claim with their
commit hash in order to receive either their winnings or the remainder of their
bet, plus any participation reward. At this point the game has ended.

A Claim event is emitted each time a player makes a claim.

There are also a few extra getter functions which UI developers may find useful:

  • getBet(bytes32) constant returns (address, bytes32, bytes32, bytes32, uint)
  • getDistance(bytes32) constant returns (uint256)
  • getResult(bytes32) constant returns (bytes32)

For more details, please consult the Maker Darts
repository
.


https://nexusdev.us
https://github.com/nexusdev

Sort:  

I am the creator of etheroll.com - it is a provably fair dice game, built on the Ethereum platform (you can view my story on etheroll here (I was in the middle of my 14 day crowdfund when the DAO hack begun), and I have been keeping a close eye on RANDAO progress etc.

RNG in computing is hard. On a blockchain, damn near impossible,due to the deterministic nature of the blockchain. I have spent a year looking into solutions for it.

It is (one of) the holy grails of blockchain, as far as I am concerned. I currently use oraclize.it and random.org to create RNG using atmospheric noise to create our RNG, however on-chain RNG is a much more suitable solution, as it removes the need for trust in a centralized entity, which is, of course, what we are all trying to do in the crypto space.

This is probably the best video from Vitalik Buterin I have seen. He touches on RNG for the first few mins...

Blockchain and Ethereum Security on the Higher Level - Vitalik Buterin

Thanks for this post. Followed and upvoted.

This post has been linked to from another place on Steem.

Learn more about linkback bot v0.3

Upvote if you want the bot to continue posting linkbacks for your posts. Flag if otherwise. Built by @ontofractal