Skip to content

Commit

Permalink
Faucet documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
wsdt committed Oct 8, 2023
1 parent bbdd6d0 commit 2bbedb6
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 5 deletions.
116 changes: 116 additions & 0 deletions boba_community/hc-captcha-metafaucet/DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# What needs to be deployed?
Relevant files are located in `./api`.

## Lambda functions
1. hc_getCAPTCHA.py
2. hc_sendMetaTx.js
3. hc_verifyCAPTCHA.py

### Why do we have mixed languages?
Until now we always had Python lambda functions. When implementing the last function for the `metaTx` I discovered a limitation of the `web3py` library that doesn't allow you to `estimateGas` for signed transactions / account-connected contract objects. This unfortunately makes the Python library incompatible with HybridCompute - for that reason I had to move over that single endpoint to JavaScript.

## Redis
All 3 lambda functions need to have access to this redis instance. The Redis endpoint is configured via environment variables for each lambda function.

# Purpose of the faucet
The faucet itself has 2 main purposes:

1. **Testnet faucet**: To provide testnet funds (native asset & token) for developers to test their applications.

2. **Mainnet faucet**: To provide mainnet funds (only native asset, but support for token available) which helps to onboard new users onto BOBA.

# Purpose of the endpoints
- `hc_getCAPTCHA.py`: Returns an *UUID* and *ImageBase64* string to show a captcha to the user on the frontend (e.g. Gateway).
- `hc_sendMetaTx.js`: Returns a nonce for the user to sign and triggers a smart contract function on behalf of the user when it receives a valid signature.
- `hc_verifyCAPTCHA.py`: Called from the smart contract directly to verify if the captcha values provided are valid.

In short, `getCAPTCHA` and `hc_sendMetaTx` are called from the Gateway or other frontends in future.

While `verifyCAPTCHA` is being called by the Faucet Smart Contract via HybridCompute.

# Return values
All endpoints return errors in the following way:
```json
{
"result": {"error": "arbitrary error"}
}
```

## hc_getCAPTCHA.py
```json
{
"result": {"uuid": "your uuid", "imageBase64": "base64 string img"}
}
```

## hc_sendMetaTx.js
There is one exception for this endpoint returning a message instead of an error when the transaction failed on-chain.

In all other cases the endpoint returns the error property as the other endpoints do.

```json
{"result": {"txHash": "0x00..", "message": "Transaction has failed / Funds issued"}}
```

## hc_verifyCAPTCHA.py
Returns abi encoded payload for HybridCompute. Handled by smart contract (faucet).

# Configuration
Configuration values are set via `.env`variables.
There is a `.env-template` file you can use.

## hc_getCAPTCHA.py
- `REDIS_URL`
- `REDIS_PORT`
- `IS_LOCAL` (optional), in production: `False`

## hc_sendMetaTx.js
- `REDIS_URL`
- `REDIS_PORT`
- `PK_KEY`, private key from where to send trigger meta transactions (does not hold faucet funds)
- `RPC_URL`
- `CONTRACT_FAUCET_ADDR` (optional locally, but mandatory in production -> *security*)
- `IS_LOCAL` (optional), in production: `False`

## hc_verifyCAPTCHA.py
- `REDIS_URL`
- `REDIS_PORT`

# Dependencies
## Python lambda
For the Python lambda functions we've a `requirements.txt` that is usually automatically installed by AWS upon deployment.

## JavaScript lambda
For the JS lambda function the following 3 packages need to be available:
- `ethers`
- `redis`
- `dotenv`

# Deployment
There is a `template.yaml` for the 2 python lambda functions. We may need extend it for the JS endpoint as well as well as security relevant best practices.

## HTTPS/HTTP
To avoid mixed content issues for the Gateway we may need to have at least `hc_getCAPTCHA.py` and `hc_sendMetaTx.js` behind *https*.

`hc_verifyCAPTCHA.py` on the other hand might need to stay on `http` so that it can be called via HybridCompute (never actually tried it out, but I can imagine this being an issue).

## Networks/Chains
Endpoints can all be chain agnostic. This includes Mainnet<>Testnet networks as long as if we are fine with using the same Private-Key for sending transactions for both mainnets & testnets.

Since this PrivateKey only needs funds to send transactions (all mainnet/testnet funds are held by the smart contract) this might be acceptable.

Right now, these only need to be deployed once for all networks:
1. hc_getCAPTCHA.py
2. hc_verifyCAPTCHA.py

This lambda function needs to be deployed for all networks separately, since we don't want users to choose an arbitrary contract:
1. hc_sendMetaTx.js

Thus, please make sure to set the contract address in the `.env` file.

# Other files
- `run-local-server-*`: only used for inter-process-communication (for integration tests), don't need to be uploaded to AWS.

# CI/CD
We may want to consider a simple pipeline to get new versions deployed or at least having a quick writeup on how to apply fixes/updates.

16 changes: 15 additions & 1 deletion boba_community/hc-captcha-metafaucet/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
# Boba Mainnet Faucet
Using ImageCaptcha

## Successful User flow

```mermaid
sequenceDiagram
User->>+getCAPTCHA: {to: 0x00..}
User->>+sendMetaTx: {to: 0x00..}
sendMetaTx->>+User: {nonce: abc}
User->>+sendMetaTx: {to: 0x00.., sig: 0x00.., uuid: 123, key: abc}
sendMetaTx->>+FaucetContract: trigger getFaucet()
FaucetContract->>+verifyCAPTCHA: Receives Captcha values
verifyCAPTCHA->>+FaucetContract: Return Captcha result (0|1)
FaucetContract->>+User: Issue funds or revert
```

### Foundry commands
Use `--no-commit --no-git` for all commands.

### Deployment
- Ensure `IS_LOCAL` is set to False, otherwise the `getCAPTCHA` endpoint will return the already solved captcha!
- Ensure `CONTRACT_FAUCET_ADDR` is set to the right mainnet faucet address, otherwise the user can provide their own address.

#### MetaTxApi
The MetaTxAPI has been rewritten to JavaScript since the Python-Web3 library doesn't have an implementation for estimating gas for signed transactions which makes it unusable to send HybridCompute transactions.

1 change: 0 additions & 1 deletion boba_community/hc-captcha-metafaucet/api/.env-template
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ REDIS_URL=localhost
REDIS_PORT=6379
RPC_URL=https://goerli.boba.network
PK_KEY=0x..
LOCAL_PK_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
CONTRACT_FAUCET_ADDR=
IS_LOCAL=False
4 changes: 2 additions & 2 deletions boba_community/hc-captcha-metafaucet/api/hc_sendMetaTx.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ exports.handler = async function hc_sendMetaTx(event, context) {
}

if (!CONTRACT_FAUCET_ADDR && !body.faucetAddr) {
return await return_payload(redisClient, { error: 'No mainnet faucet address provided' });
return await return_payload(redisClient, { error: 'No faucet address provided' });
} else if (CONTRACT_FAUCET_ADDR && body.faucetAddr) {
return await return_payload(redisClient, { error: 'Faucet address already configured' });
}
Expand All @@ -70,7 +70,7 @@ exports.handler = async function hc_sendMetaTx(event, context) {
return await return_payload(redisClient, {error: 'Please contact support (1)'})
}
const wallet = new ethers.Wallet(PK_KEY, w3);
const faucetAddr = CONTRACT_FAUCET_ADDR || body.faucetAddr;
const faucetAddr = CONTRACT_FAUCET_ADDR ?? body.faucetAddr;
const faucetContract = new ethers.Contract(faucetAddr, FAUCET_ABI, wallet);

try {
Expand Down
28 changes: 27 additions & 1 deletion boba_community/hc-captcha-metafaucet/test/mainnet-faucet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,32 @@ describe("Get gas from mainnet faucet", function () {
expect(preContractBalance).to.be.eq(postContractBalance, "Faucet should have all original funds")
});

describe('local environment tests', () => {
// these tests are only applicable to local/test environments

it('should fail when faucet address not provided at all', async () => {
await (await fetch(EndpointConfig[LocalEndpoint.sendMetaTx].url, {
body: JSON.stringify({to: deployerWallet.address}), method: 'POST', headers: {
"content-type": "application/json"
}
})).json()

const metaRequest = await (await fetch(EndpointConfig[LocalEndpoint.sendMetaTx].url, {
// faucetAddr only needs to be defined in tests
body: JSON.stringify({
to: deployerWallet.address,
uuid: "123",
key: "987",
sig: "0x0293cc0d4eb416ca95349b7e63dc9d1c9a7aab4865b5cd6d6f2c36fb1dce12d34a05039aedf0bc64931a439def451bcf313abbcc72e9172f7fd51ecca30b41dd1b",
}), method: 'POST', headers: {
"content-type": "application/json"
}
})).json()

expect(metaRequest?.result?.error).to.be.eq('No faucet address provided')
})
})

describe('meta tx', () => {
it('should not get nonce if no address provided', async () => {
const metaRequest = await (await fetch(EndpointConfig[LocalEndpoint.sendMetaTx].url, {
Expand Down Expand Up @@ -490,7 +516,7 @@ describe("Get gas from mainnet faucet", function () {
}
})).json()

expect(metaRequest?.result?.error).to.be.eq('No mainnet faucet address provided')
expect(metaRequest?.result?.error).to.be.eq('No faucet address provided')
})

it('should get nonce to sign', async () => {
Expand Down

0 comments on commit 2bbedb6

Please sign in to comment.