bonomat on Nostr: Best and only game on #nostr? ...
Best and only game on #nostr?
quoting note1p6d…k5ec
TL;DR: NostrDice is a provably fair game built on Nostr and Lightning.
# Why build NostrDice
Nostr has been around for a while now, but we’ve only used it as a social network. We had not had a chance to look under the hood to see how things work and what you can do with it.
We wanted to get our hands dirty. But we didn’t just want to follow some tutorials; we wanted to build something fun and see how far Nostr could take us.
After some brainstorming, we landed on the legendary game SatoshiDICE. Why not bring the OG of all Bitcoin games to Lightning using Nostr?
> We are going to build our own SatoshiDICE, with blackjack and hookers
NostrDice is SathoshiDICE on steroids. I mean, it’s built with Nostr and Lightning.
## Design Goals of NostrDice
Before we jump into it, these are our design goals for the game.
1. Provably fair. The NostrDice server must not be able to control the outcome of a die roll. Players must be able to verify that the NostrDice server is not cheating.
2. Easy to use. The game should be compatible with any Nostr client (which supports Zaps). No new clients should be required to play the game.
3. Have fun building it! This was the main goal if we’re honest :D
Looking into how Zaps work we quickly figure out a way to use them to place bets and pay out lucky winners.
### How NostrDice Works
We set up 3 accounts on Nostr:
- NostrDice Game: This is where you play. Zap your chosen multiplier note, roll the die and win some sats, sometimes. NostrDice-Game (npub1nst…ns84)
- NostrDice Nonces: Here you can find proof that the server is playing fair with your dice rolls. NostrDice-Nonces (npub1nst…zcwp)
- NostrDice Social: A place for us to promote the game. Follow us to get the latest updates. NostrDice (npub1nst…76qj)
1. Commitment to a Secret Nonce
A new round starts when the NostrDice server posts a commitment to a nonce on NostrDice-Nonces (npub1nst…zcwp). The server does this *before* receiving any information from the player, so it can choose the nonce based on the player’s input.
2. **Placing a Bet**
The player chooses their multiplier and wager by zapping one of 11 notes on NostrDice-Game (npub1nst…ns84). The multiplier also determines the target the die roll needs to hit. For example, to win a 2x multiplier, the player must roll a number lower than 31784 (out of 65535 possibilities).
3. **Determining the Outcome**
The server calculates the player’s die roll based on the secret nonce, the player’s npub and the zap memo. Player’s are encouraged to use the zap memo to make it impossible for the server to anticipate their input to the die roll.
4. **Immediate Payouts**
If the player hits the target, the NostrDice server will pay out instantly.
The server zaps the player’s npub to credit them with their winnings. To this end, the player must have a valid LNURL configured if they want to get paid!
5. **House Edge**
The server retains a small house edge on every bet. For a specific
target, the payout is slightly less than the true odds to account for
this edge.
### NostrDice as a Provably Fair Game
The key to NostrDice being fair is that the outcome of a die roll is random and verifiable. For an in-depth explanation on this, check out the https://github.com/nostrdice/nostrdice/blob/4419cd8f4592fd7d443a22d594b95f80aee5287c/GAME.md in our repository https://github.com/nostrdice/nostrdice/. But here is a summary of the main ideas.
**Commitment Scheme**
NostrDice commits to a nonce before the round starts by hashing it and publishing the hash on Nostr for everyone to see:
```
nonce := gen_32_bytes()
commitment := sha256(nonce)
```
**Roll Formula**
When the player places their bet, the nonce is combined with the player's npub and zap memo to generate the die roll:
```
roll = bytes_to_decimal(first_2_bytes(sha256(nonce | player_npub | zap_memo | index)))
```
To prevent the server from predicting outcomes based on the player's npub, the player submits their own randomness via the zap memo.
Additionally, a roll index is included to allow the player to roll more than once with the same nonce.
**Verification**
The player can independently compute their roll after the nonce is revealed. They can also verify that the revealed nonce matches the original commitment:
```
original_commitment == sha256(revealed_nonce)
```
### Lessons learned
Building NostrDice was very fun and not that hard! It’s easy to see why the Nostr ecosystem is flourishing: the Nostr protocol is super simple.
- Dev environment: Building a Nostr client is super straight forward. There are plenty of libraries available, but we went with the https://github.com/rust-nostr/. However, setting up a dev environment was not so easy. We ended up with an involved `docker-compose` file and a `just` file to spin up a dev environment consisting of:
- `bitcoind`.
- Two `lnd` nodes: one for the player and one for the server.
- A Nostr relay via `nostr-rs-relay`.
- `nostr-wallet-connect-lnd` so that the player can zap a multiplier note with their LND node.
- An LNURL pay server for the player to receive payouts.
- `nostrdice`: the game server itself, acting as a modified LNURL server.
- Note limits: Our nonce account needs to publish notes regularly and we quickly ran into the limits of publicly available relays: we got banned. Because of this we decided to post less and run our own relay which again, was super simple. Since Rust is our first language, we decided to go with https://github.com/scsibug/nostr-rs-relay.
- Production testing: This is the biggest hurdle we encountered. While for “normal” social media usage clients like Primal, Snort or Damus work well, when wanting to jump between different accounts, we quickly ran into UX issues. The setup which worked best for us was using the Alby browser extension with different profiles. I believe things can be improved here. For example, a browser extension supporting multiple Nostr profiles, where profiles are locked to one tab/website only. This way we can be logged into multiple accounts on different profiles at the same time. We also ended up using noStrudel as our favorite browser-based client.
- No signet support on web clients. Zaps are always assumed to be on mainnet.
Overall it was great fun to build on Nostr for the first time, and we learned a lot. I hope you also enjoy this nostalgic tribute to SatoshiDICE on Nostr.
cheers,
the 10101 team
## Links
- NostrDice Social: An account only used for social posts, follow along to stay up-to-date on any announcements. NostrDice (npub1nst…76qj).
- NostrDice Game: The main game account. Where the static multiplier notes are hosted. NostrDice-Game (npub1nst…ns84).
- NostrDice Nonces: This account publishes nonce commitments and reveals nonces at a regular interval. NostrDice-Nonces (npub1nst…zcwp).
- NostrDice on X: x.com/nostrdice
- NostrDice source code: https://github.com/nostrdice/nostrdice/.
- NostrDice relay: wss://relay.nostrdice.com.
- Carman 🏴☠ (npub1u8l…turz) we just can’t help ourselves, but we noticed that a lot of the libraries we used were either built by or contributed to by Ben. Among others, we used and built on:
- https://github.com/benthecarman/lnurl-rs: An LNURL pay server.
- https://github.com/benthecarman/nostr-wallet-connect-lnd: A NWC server to control LND.