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

PoW Faucet #2

Merged
merged 64 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1c41920
WIP
harryttd Jun 12, 2023
ff15112
WIP POW
harryttd Jun 23, 2023
aff3288
Working multi challenge
harryttd Jun 29, 2023
fd633b0
Cleanup
harryttd Jun 29, 2023
03bbfc9
Split pow logic into functions
harryttd Jun 29, 2023
47898c2
Validate address
harryttd Jun 29, 2023
d24c9db
Add status field
harryttd Jun 30, 2023
be8d7de
Send challenge counter with response
harryttd Jul 5, 2023
bdffb07
npm rm @subspace/vdf npm
harryttd Jul 5, 2023
e66374f
npm audit fix
harryttd Jul 5, 2023
919670a
Return error if no challenge exists when verifying
harryttd Jul 5, 2023
efbf410
Cleanup
harryttd Jul 11, 2023
be16e79
Track if captcha was used + export redis client
harryttd Jul 14, 2023
1d4d55a
Make sure challenge is deleted before Tez is sent
harryttd Jul 14, 2023
9bdb453
Start code to determine a challenge's difficulty
harryttd Jul 17, 2023
81af554
WIP challenges needed logic
harryttd Jul 18, 2023
228a893
Only check 0's in solution
harryttd Jul 18, 2023
12c5030
Rename values and modify difficulties
harryttd Jul 18, 2023
e6dce98
Fix google DNS not resolving when running in k8s
harryttd Jul 25, 2023
4e01306
Cleanup packages
harryttd Jul 28, 2023
4deae8c
Don't track dist/
harryttd Jul 28, 2023
1c61143
Fix /info bug
harryttd Jul 28, 2023
f60f04c
Cleanup
harryttd Jul 28, 2023
aff0cfc
Improve dockerfile
harryttd Aug 1, 2023
09a0fb2
Run dotenv immediately
harryttd Aug 1, 2023
2eba83c
Setup Taquito TezosToolkit once
harryttd Aug 1, 2023
0539bb0
Cleanup captcha logic and check first if it's enabled
harryttd Aug 1, 2023
63fa5ce
Fix typings and Tezos amount conversions + cleanup
harryttd Aug 2, 2023
a903892
Make redis url and password configurable
harryttd Aug 2, 2023
c6488e7
Use winston instead of morgan
harryttd Aug 4, 2023
3aa2f93
Create winston http logger
harryttd Aug 4, 2023
9d54507
Move 'USER node' to top of Dockerfile'
harryttd Aug 4, 2023
9f9826b
Better logic when user has too much Tez
harryttd Aug 4, 2023
8bca1b1
Use ts 'satisfies'
harryttd Aug 9, 2023
0dc5840
Make sure challenge was deleted before sending Tez
harryttd Aug 10, 2023
8b61206
Revamp readme and add .env.example
harryttd Aug 10, 2023
809532a
Add graceful shutdown handler + update .dockerignore
harryttd Aug 11, 2023
321e430
Better env var names
harryttd Aug 11, 2023
42364fe
aider: Add profile to challenge state data in Redis and modify create…
harryttd Aug 11, 2023
e283f31
aider: Ensure that if a user requests a challenge with a different pr…
harryttd Aug 11, 2023
608986f
Fix aider code
harryttd Aug 11, 2023
13caf90
Add profile to httpLogger
harryttd Aug 11, 2023
606785c
Make code that sends Tez DRY
harryttd Aug 11, 2023
a222bd2
Add ability to disable needing to solve challenges
harryttd Aug 11, 2023
eec6ae3
WIP per profile configuration of challenges
harryttd Aug 11, 2023
eeba199
Configurable difficulty and challenges needed per profile
harryttd Aug 11, 2023
9c2f400
Update readme
harryttd Aug 11, 2023
9dbf557
Use middleware functions
harryttd Aug 14, 2023
6b470a2
Make sure existing profile is used in /verify
harryttd Aug 14, 2023
44f00c4
Enable source maps
harryttd Aug 15, 2023
cddc4d2
Get pubkey hash from the private key
harryttd Aug 15, 2023
ee8fe87
Ability to configure multiple profiles + cleanup
harryttd Aug 16, 2023
57a180f
Update README
harryttd Aug 16, 2023
62edcd7
Only validate amount if DISABLE_CHALLENGES
harryttd Aug 16, 2023
22e548c
Remove old env vars
harryttd Aug 16, 2023
731bcd1
Update readme
harryttd Aug 16, 2023
f39995e
Validate amount is greater than 0
harryttd Aug 16, 2023
89b6337
Make env.ts to handle env var type conversions
harryttd Aug 18, 2023
2029d5f
Update comments
harryttd Aug 18, 2023
872c4ea
Update package.json fields
harryttd Aug 21, 2023
97ccf7d
Make workdir /app + validate profiles.json isn't empty
harryttd Aug 23, 2023
016ef17
Don't connect to redis when challenges are disabled
harryttd Aug 23, 2023
823ce91
Return challengesDisabled and remove profile from /info res
harryttd Aug 23, 2023
5a66e59
Add section about programmatic faucet to readme
harryttd Aug 25, 2023
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
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.dockerignore
.DS_Store
.env*
.git
.github
.gitignore
.vscode
dist
Dockerfile
LICENSE.md
node_modules
npm-debug.log
README.md
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
API_PORT=3000

AUTHORIZED_HOST="*"

ENABLE_CAPTCHA=true
CAPTCHA_SECRET=6LefC8...

FAUCET_PRIVATE_KEY=edsk...

REDIS_PASSWORD=password
REDIS_URL=redis://localhost:6379

RPC_URL=http://localhost:8732

DISABLE_CHALLENGES=false

MAX_BALANCE=6000
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
.env
dist/
.env
17 changes: 11 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
FROM node:16-alpine
FROM node:18-alpine

WORKDIR /tezos-faucet-backend
USER node

COPY package.json .
WORKDIR /app

RUN chown node:node /app

COPY --chown=node:node package.json ./
COPY --chown=node:node package-lock.json ./

RUN npm install

COPY . .
COPY --chown=node:node . ./

RUN ./node_modules/typescript/bin/tsc -p ./tsconfig.json
RUN ./node_modules/typescript/bin/tsc

CMD ["node", "./dist/api.js"]
CMD ["node", "--enable-source-maps", "./dist/src/api.js"]
158 changes: 86 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,73 @@
# Tezos testnet faucet API
# Tezos Faucet Backend

## Overview

Backend for Tezos faucet front app: https://github.com/oxheadalpha/tezos-faucet.
The Tezos Faucet Backend (frontend code [here](https://github.com/oxheadalpha/tezos-faucet)) provides a reliable and secure way to distribute Tez to users. Through the implementation of a Proof of Work (PoW) mechanism, combined with CAPTCHA, we ensure users expend computational resources, thereby preventing bots and malicious actors from spamming and draining the faucet.

## Prerequisite
Here's a general flow of how it works:

Create a ReCaptcha project (shared with faucet front app).
1. **Requesting Tez**: A user initiates the process by making a request for tez. The backend responds by sending a challenge to the user.
2. **Solving Challenges**: The user must solve the challenge by finding a correct solution. The complexity of the challenge can vary, and the user doesn't know in advance how many challenges they'll need to solve.
3. **Verification & Receiving Tez**: Once the user submits a solution, the backend verifies it. If the solution is correct but there are more challenges to be solved, the user will be sent another challenge. This repeats until all challenges are solved correctly. Only then is the requested Tez granted to the user.

## Prerequisites

## Local run for development
- **Node.js** v18
- **Captcha** (Optional): Create a Google [ReCaptcha](https://www.google.com/recaptcha/about/) project. The public site key will be shared with the frontend. Activate domain verification in ReCAPTCHA parameters to allow only communication from the frontend faucet app.
- **Redis** (Optional): If `DISABLE_CHALLENGES` is not set to `false`, set up a Redis server to store PoW challenge data. It's recommended to use a single-instance Redis setup for the challenge data, as this ensures atomicity and helps in avoiding potential exploits. Given that the challenge data isn't persistent or long-term essential, a single instance suffices and is easier to maintain.

Compile Typescript sources to `/dist` directory:
## Config

Set environment variables or add them to a `.env` file. See [.env.example](.env.example). The [src/env.ts](src/env.ts) file handles necessary type conversions from environment variable strings.

Mandatory:

- `FAUCET_PRIVATE_KEY`: Faucet's private key to sign transactions
- `CAPTCHA_SECRET`: faucet ReCAPTCHA secret key (mandatory if `ENABLE_CAPTCHA=true`)
- `RPC_URL`: Tezos node RPC URL to connect to

### Profile Configuration

Profiles are configured in the [profiles.json](./profiles.json) file. Each profile is an object with the following properties:

- `amount`: The amount of Tez to be distributed for the specified profile. Default for `USER`: `1`, for `BAKER`: `6000`.
- `challengesNeeded`: The number of challenges needed if no CAPTCHA is provided. Default for both `USER` and `BAKER`: `6`.
- `challengesNeededWithCaptcha`: The number of challenges needed if a valid CAPTCHA is provided. Default for both `USER` and `BAKER`: `5`.
- `difficulty`: The difficulty level of the challenge if no CAPTCHA is provided. Default for both `USER` and `BAKER`: `5`.
- `difficultyWithCaptcha`: The difficulty level of the challenge if a valid CAPTCHA is provided. Default for both `USER` and `BAKER`: `4`.

The [src/profiles.ts](src/profiles.ts) file imports this JSON file and validates these properties. If any property is missing or invalid, an error will be thrown. If `DISABLE_CHALLENGES` is `true`, only `amount` is required.

Optional:

- `API_PORT`: API listening port (default: `3000`)
- `AUTHORIZED_HOST`: CORS origin whitelist (default `*`)
- `DISABLE_CHALLENGES`: `true` to disable challenges (default: `false`)
- `ENABLE_CAPTCHA`: `true` to enable ReCAPTCHA, `false` otherwise (default: `true`)
- `MAX_BALANCE`: maximum address balance beyond which sending of XTZ is refused (default: `6000`)

## Running the API

Install dependencies and compile Typescript sources to `/dist` directory:

```
npm install
npm run build
```

Run API with `nodemon`:
Run API:

```
npm run serve
npm run start
```

## Deploy
For developing with auto reloading:

```
npm run dev
```

## Docker

### Build

Expand All @@ -36,85 +81,54 @@ docker build . -t tezos-faucet-backend
docker run -p 3000:3000 tezos-faucet-backend
```

## Config
## API Endpoints

Set environment variables
### GET /info

Mandatory:
Returns general information about the faucet, including the faucet's address, whether captcha is enabled, the max balance allowed, and the Tez amounts granted per profile.

- `FAUCET_PRIVATE_KEY`: private key of the faucet, to sign transaction
- `FAUCET_ADDRESS`: faucet address
- `FAUCET_CAPTCHA_SECRET`: faucet ReCAPTCHA secret key (mandatory if ENABLE_CAPTCHA=true)
- `RPC_URL`: Tezos node RPC URL to connect to

Optional:

- `ENABLE_CAPTCHA`: true to enable ReCAPTCHA, false otherwise (default: true)
- `AUTHORIZED_HOST`: authorized host, for CORS (default: *).
- `API_PORT`: API listening port (default: 3000)
- `FAUCET_AMOUNT_USER`: number of XTZ to send for a regular request (default: 1)
- `FAUCET_AMOUNT_BAKER`: number of XTZ to send for a baker request (default: 6000)
- `MAX_BALANCE`: maximum user balance beyond which sending of XTZ is refused (default: 6000)

## Security

Activate domain verification in ReCAPTCHA parameters to allow only calls from the Front faucet.

## Use

### Request

Request URL:
```
POST /send
```
Example response:

Request body:
```
```json
{
captchaToken:"...",
address:"tz1...",
profile:"USER"
"faucetAddress": "tz1...",
"captchaEnabled": true,
"maxBalance": 6000,
"profiles": {
"user": {
"profile": "USER",
"amount": 1,
"currency": "tez"
},
"baker": {
"profile": "BAKER",
"amount": 6000,
"currency": "tez"
}
}
}
```

- `token`: ReCaptcha user response token
- `address`: address to send XTZ to
- `profile`: USER for a regular user who will get 1 xtz. BAKER for a baker profile, who will get 6000 xtz.

### Response
### POST /challenge

#### Success
Initiates the Tez request procedure. The user provides their address, profile type (`BAKER` or `USER`), and captcha token (optional).

Return code: HTTP 200
If a challenge already exists for the user's address in Redis it will be returned in the response. Otherwise the endpoint generates a new challenge and stores it, along with associated data in Redis.

Response body:
```
{
"status": "SUCCESS",
"txHash":"..."
}
```
The response contains the challenge string, a challenge counter starting at 1, and the difficulty. The challenge counter indicates the current challenge in a series of Proof of Work challenges that the user must complete. Users aren't privy in advance to the exact number of PoW challenges they'll need to solve to receive the requested Tez.

- `status`: SUCCESS
- `txHash`: hash of transaction
### POST /verify

Allows users to submit solutions to the challenges. The user provides their address, nonce, solution string, and profile type.

#### Error
The endpoint verifies the solution by trying to regenerate it using the challenge string and nonce.

Return code:
If the solution is correct but the required number of challenges have not yet been satisfied, a new challenge is generated and returned in the response.

- HTTP 400: Bad request (wrong captcha token)
- HTTP 500: Server or Tezos node error
If all challenges have been completed, the user's address is granted the Tez amount for their profile type. The transaction hash is returned to indicate the transfer was successful.

## Programmatic Faucet Usage

Response body:
```
{
"status": "ERROR",
"message": "Captcha error"
}
```
For programmatic usage of the faucet, we provide a `getTez.js` script located in the `/scripts` directory of the frontend repository. Please refer to it for more details on how to use it. This script can be run from a JavaScript program or directly from a shell. It interacts with the backend to request Tez, solve the required challenges, and verify the solutions.

- `status`: ERROR
- `message`: error message
Please note that the `getTez.js` script does not use CAPTCHA. Therefore, when using the programmatic faucet, challenges can be configured to be more difficult and require more of them to be solved.
29 changes: 0 additions & 29 deletions dist/Captcha.js

This file was deleted.

57 changes: 0 additions & 57 deletions dist/Tezos.js

This file was deleted.

9 changes: 0 additions & 9 deletions dist/Types.js

This file was deleted.

Loading
Loading