Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Staging #57

Merged
merged 21 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 73 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,95 @@
# Dice Demonstration
# PKDice

Random numbers, dice images and animations can be taken from the dice challenge repo here:<br>
https://github.com/scaffold-eth/se-2-challenges/tree/challenge-3-dice-game
## Description

Live view of this repo for context:<br>
https://buidlguidl-g33iv38ol-brycehytans-projects.vercel.app/dice
This game is designed to demonstrate the difficulty of guessing or brute-forcing a wallet's private key. A host creates a game with a reward and a concealed private key, and participants join the race to guess this hidden key.

### Project Overview
This will become a demonstration to use during presentations with a live audience using their phones to participate. A host will control the metrics of the game, and control a private key that will contain some value the users are trying to win. The host determines how many dice can be rolled and what rolling mode the users will be using. The users will try to roll their dice to match the hosts. If they do, some of the prize money is sent to them along with a winning message. Eventually the hosts number will have too many characters (dice) for the users to be able to match. This will show the security of a 64 character private key.
## Project Overview

This project is a live demonstration intended for use during presentations with a live audience using their phones to participate. A host controls the game metrics and a private key that contains some value the users are trying to win. The host determines the number of dice that can be rolled and the rolling mode the users will use. Participants try to roll their dice to match the host's. If they succeed, they receive a portion of the prize money and a winning message. As the number of dice increases, it becomes increasingly difficult for users to match the host's dice, demonstrating the security of a 64-character private key.

## Features

### Dice
- Each dice will have a value in HEX (0-F)
- Dice images and rolling animations can be grabbed from the Dice Challenge repo at the top of this page.
- Each die has a value in HEX (0-F).
- Dice images and rolling animations can be sourced from the Dice Challenge [repo](https://github.com/scaffold-eth/se-2-challenges/tree/challenge-3-dice-game).

### Host
- The host can at any time change the number of dice, in a range from 1-64.
- The host can update the rolling mode.
- The host can enable/disable user rolling. This may be necessary for updating the dice count and modes.
- The host computer will be on display at the presentations. What information is necessary to show/hide?
- Maybe the host can do an initial roll on the big screen to get their number?
- We should display winners on the host screen so everyone can see when there was a winner.
- Can change the number of dice (1-64 range).
- Can update the rolling mode.
- Can enable/disable user rolling (useful for updating dice count and modes).
- Host computer will be displayed during presentations.
- Initial roll can be displayed on the big screen to generate the host's number.
- Winner is displayed on the host screen for visibility.

### Users
- Unsure of the number of users we will have at one time, a good guess is in the 10-30 range.
- Users will get a burner wallet when they connect that we will use for display purposes, and to send any winnings.
- The users should see a cool dice rolling display on their (phone) screens. This will get tricky as more dice are added, up to a max of 64.
- If they match the hosts, a cool winning message will be display and some of the value in the hosts private key will be sent to them. Need a cool message they can show off to others.
- All the user's dice will roll at the same time. e.g if the count of dice is currently 24, and example roll from a user would be F18053525893D3537EAB615C
- They should see a cool animation when rolling
- Each user gets a burner wallet upon connecting for display purposes and to receive winnings.
- Users see a dice rolling display on their phones, which scales with the number of dice (up to 64).
- Cool winning message and some value from the host's private key are sent to users upon winning.
- All user dice roll simultaneously.

### Roll Modes
- Users click a roll button to manually roll the dice once. This will mostly be used when there is a small number of dice.
- Users click a button to start the continous rolling of dice. It will automatically keep rolling until manually stopped or it finds the match.
- Users click a button to start continous rolling, but this time all users are coordinating trying to guess different numbers of the hosts instead of racing. Namespaces can be used here. Austin can put together a quick algorithm to accompolish this, or feel free to tackle it yourself.
- **Manual Roll:** Users click a roll button to manually roll the dice once. Best for a small number of dice.
- **Auto Rolling:** Users click a button to start continuous rolling until manually stopped or a hidden character(s) are found.
- **Brute Continuous Rolling:** Users click a button to start rolling continuously as quickly as possible, coordinating with each other to guess different numbers of the host’s dice.

## Instructions for Hosts

1. **Setup:** Configure the number of dice and rolling mode.
3. **Enable Rolling:** Allow users to start rolling.
4. **Monitor:** Display winners on the host screen for everyone to see.
5. **Adjust Settings:** Enable/disable user rolling as necessary to update dice count and modes.

## Instructions for Users

1. **Connect:** Join the game using your phone.
2. **Burner Wallet:** Receive a burner wallet for display and receiving winnings.
3. **Rolling Dice:** Click the roll button to roll the dice once.
4. **Winning:** If your roll matches the host’s, receive a cool winning message and some of the host’s prize money.

## Rolling Modes

## Contributing
1. **Manual Roll:**
- Click to roll the dice once.
2. **Auto Rolling:**
- Click to start rolling until stopped or a match is found.
3. **Brute Rolling:**
- Click to start rolling as quickly as possible.

Step by step "fork-and-pull" Github contributing using CLI refresher here:
https://gist.github.com/ZakGriffith/69d1eb8baebddd7d370b87a65a7e3ec0
## Credits

- Dice images and rolling animations sourced from the Dice Challenge [repo](https://github.com/scaffold-eth/se-2-challenges/tree/challenge-3-dice-game).


## License

This project is licensed under the MIT License. See the LICENSE file for more information.

---

## Setting Up Locally


1. Clone this repo & install dependencies

```
git clone https://github.com/Buidlguidl/private-key-dice.git
cd private-key-dice
yarn install
```

### initial notes from austin:
2. Run the backend in the first terminal:

the essence of what I want to demonstrate is that technically you can guess someone's private key but if you use enough bytes randomness it becomes impossible
```
yarn backend
```

the idea comes from my buddy griffin who does a "guess the number" kind of game that is similar -- I want to use our amazing hex dice from mr dee and SE2 to make a pretty mobile experience that is controlled from and admin screen
This command starts a local backend on port `6001` and can be used for testing and development. You can customize the configuration in `backend.config.ts` and add your very own `.env` file following the `packages/backend/.env.example`.

this would be a demo for technical groups and normies - I lock money up in a private key where we know all of the bytes except one and everyone has to click to roll the dice and if they land on the right hex character it makes a private key, checks if there is money on it, and sweeps it with a celebration screen
3. On a third terminal, start your NextJS app:

then from the admin side I lock up a little money and we roll two dice and then three and then four - we'll have to experiment with where it gets so difficult a room wont be able to get it and then we can allow them to upgrade their roller to *automatic roll* and *group brute force roll* where everyone works on different sections trying to find the solution
```
yarn start
```

the payoff is when I have 64 dice on a single screen and I show some smart contract with billions of dollars in it -- all your phone has to do is guess the right 64 hex characters and that billion dollars is yours
Visit your app on: `http://localhost:3000`. Change `isLocal` variable to `true` in the config in `packages/nextjs/server.config.ts`. You can also add your very own `.env.local` file following the `packages/nextjs/.env.example`.
11 changes: 11 additions & 0 deletions packages/backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

# Create a database at https://www.mongodb.com/
MONGO_URL=

# Hard string for for signing verification tokens
JWT_SECRET=

PORT=6001

# create a realtime app at https://ably.com/
ABLY_API_KEY=
8 changes: 8 additions & 0 deletions packages/backend/backend.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const backendConfig = {
ablyApi: "Fbq6sA.xC_GgQ:a9uQJKCunyvMmh1nVvcZaZFuZw_2LYbcHvTno5uPV5c",
mongo_url: "mongodb+srv://dicedemo:[email protected]/?retryWrites=true&w=majority",
port: "6001",
jwt_secret: "superhardstring",
};

export default backendConfig;
9 changes: 4 additions & 5 deletions packages/backend/controllers/Admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import bcrypt from "bcrypt";
import { Request, Response } from "express";
import jwt from "jsonwebtoken";
import { ably } from "..";
import backendConfig from "../backend.config";

const JWT_SECRET = process.env.JWT_SECRET || "superhardstring";
const JWT_SECRET = process.env.JWT_SECRET || backendConfig.jwt_secret;

async function generateUniqueInvite(length: number) {
let invites = await Invites.findOne();
Expand Down Expand Up @@ -41,7 +42,7 @@ async function generateUniqueInvite(length: number) {

export const createGame = async (req: Request, res: Response) => {
try {
const { diceCount, privateKey, hiddenPrivateKey, mode, adminAddress } = req.body;
const { diceCount, hiddenPrivateKey, mode, adminAddress } = req.body;

const salt = await bcrypt.genSalt();
// const privateKeyHash = await bcrypt.hash(privateKey, salt);
Expand All @@ -52,7 +53,6 @@ export const createGame = async (req: Request, res: Response) => {
inviteCode: await generateUniqueInvite(8),
diceCount,
mode,
privateKey,
hiddenPrivateKey,
});

Expand All @@ -69,7 +69,7 @@ export const createGame = async (req: Request, res: Response) => {

export const restartWithNewPk = async (req: Request, res: Response) => {
try {
const { diceCount, privateKey, hiddenPrivateKey, adminAddress } = req.body;
const { diceCount, hiddenPrivateKey, adminAddress } = req.body;
const { id } = req.params;
const game = await Game.findById(id);

Expand All @@ -78,7 +78,6 @@ export const restartWithNewPk = async (req: Request, res: Response) => {
}

game.diceCount = diceCount;
game.privateKey = privateKey;
game.hiddenPrivateKey = hiddenPrivateKey;
game.mode = "manual";
game.adminAddress = adminAddress;
Expand Down
8 changes: 2 additions & 6 deletions packages/backend/controllers/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import Game from "../models/Game";
import { Response, Request } from "express";
import jwt from "jsonwebtoken";
import { ably } from "..";
import backendConfig from "../backend.config";

const JWT_SECRET = process.env.JWT_SECRET || "superhardstring";
const JWT_SECRET = process.env.JWT_SECRET || backendConfig.jwt_secret;

export const join = async (req: Request, res: Response) => {
try {
Expand Down Expand Up @@ -36,8 +37,3 @@ export const join = async (req: Request, res: Response) => {
res.status(500).json({ error: (err as Error).message });
}
};

export const leave = () => {};

export const sweepPrize = () => {};
export const markSlotsAsFoundPerPlayer = () => {};
7 changes: 4 additions & 3 deletions packages/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import gameRoutes from "./routes/game";
import http from "http";
import Ably from "ably";
import path = require("path");
import backendConfig from "./backend.config";

declare global {
namespace Express {
Expand All @@ -28,12 +29,12 @@ app.use(cors());

/**Ably Setup */

export const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY });
export const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY || backendConfig.ablyApi });

const server = http.createServer(app);
/* MONGOOSE SETUP */
const PORT = process.env.PORT || 6001;
const MONGO_URL = process.env.MONGO_URL || "";
const PORT = process.env.PORT || backendConfig.port;
const MONGO_URL = process.env.MONGO_URL || backendConfig.mongo_url;

app.use("/admin", adminRoutes);
app.use("/player", playerRoutes);
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import jwt from "jsonwebtoken";
import { Request, Response } from "express";
import backendConfig from "../backend.config";

const JWT_SECRET = process.env.JWT_SECRET || "superhardstring";
const JWT_SECRET = process.env.JWT_SECRET || backendConfig.jwt_secret;

export const verifyToken = async (req: Request, res: Response, next: () => void) => {
try {
Expand Down
4 changes: 0 additions & 4 deletions packages/backend/models/Game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ const gameSchema = new mongoose.Schema(
enum: ["auto", "manual", "brute"],
required: true,
},
privateKey: {
type: String,
required: true,
},
hiddenPrivateKey: {
type: String,
required: true,
Expand Down
4 changes: 0 additions & 4 deletions packages/backend/models/Player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ const playerSchema = new mongoose.Schema(
enum: ["auto", "manual"],
required: true,
},
privateKey: {
type: String,
required: true,
},
type: {
key: String,
value: String,
Expand Down
5 changes: 4 additions & 1 deletion packages/nextjs/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@
# More info: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables
NEXT_PUBLIC_ALCHEMY_API_KEY=
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=
NEXT_PUBLIC_ABLY_API_KEY=

# create a realtime app at https://ably.com/
# Api must tally with backend
NEXT_PUBLIC_ABLY_API_KEY=
3 changes: 0 additions & 3 deletions packages/nextjs/components/dicedemo/GameCreateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { notification } from "~~/utils/scaffold-eth";
interface FormData {
diceCount: number;
mode: "auto" | "manual" | "brute";
privateKey: string;
hiddenPrivateKey: string;
adminAddress: string | undefined;
}
Expand All @@ -25,7 +24,6 @@ const GameCreationForm = () => {
diceCount: 1,
mode: "manual",
hiddenPrivateKey: "*" + initialPrivateKey.slice(1),
privateKey: initialPrivateKey,
adminAddress,
});

Expand Down Expand Up @@ -82,7 +80,6 @@ const GameCreationForm = () => {
setFormData({
diceCount: 0,
mode: "auto",
privateKey: loadBurnerSK(),
hiddenPrivateKey: "",
adminAddress,
});
Expand Down
23 changes: 6 additions & 17 deletions packages/nextjs/components/dicedemo/GameJoinForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { useRouter } from "next/router";
import { InputBase } from "../scaffold-eth";
import QrReader from "react-qr-reader-es6";
import { useAccount } from "wagmi";
import serverConfig from "~~/server.config";
import { saveGameState } from "~~/utils/diceDemo/game";
import { joinGame } from "~~/utils/diceDemo/apiUtils";
import { notification } from "~~/utils/scaffold-eth";

const GameJoinForm = ({
Expand All @@ -25,24 +24,14 @@ const GameJoinForm = ({
};

const { address: playerAddress } = useAccount();
const serverUrl = serverConfig.isLocal ? serverConfig.localUrl : serverConfig.liveUrl;

const handleJoinGame = async (invite: string) => {
setLoading(true);
const response = await fetch(`${serverUrl}/player/join`, {
method: "PATCH",
headers: {
Authorization: `Bearer`,
"Content-Type": "application/json",
},
body: JSON.stringify({ inviteCode: invite, playerAddress }),
});

const updatedGame = await response.json();
saveGameState(JSON.stringify(updatedGame));
setLoading(false);
if (updatedGame.error) {
notification.error(updatedGame.error);
try {
await joinGame(invite, playerAddress as string);
setLoading(false);
} catch (error) {
setLoading(false);
return;
}
await router.push({
Expand Down
3 changes: 0 additions & 3 deletions packages/nextjs/components/dicedemo/RestartWithNewPk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { notification } from "~~/utils/scaffold-eth";

interface FormData {
diceCount: number;
privateKey: string;
hiddenPrivateKey: string;
adminAddress: string | undefined;
}
Expand All @@ -27,7 +26,6 @@ const RestartWithNewPk = ({ isOpen, setIsOpen }: { isOpen: boolean; setIsOpen: D
const [formData, setFormData] = useState<FormData>({
diceCount: 0,
hiddenPrivateKey: "",
privateKey: "",
adminAddress: undefined,
});

Expand Down Expand Up @@ -92,7 +90,6 @@ const RestartWithNewPk = ({ isOpen, setIsOpen }: { isOpen: boolean; setIsOpen: D
diceCount: 1,
adminAddress: account.address,
hiddenPrivateKey: "*" + pk.slice(1),
privateKey: pk,
}));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down
Loading
Loading