194 lines
6.6 KiB
Markdown
194 lines
6.6 KiB
Markdown
# Contractless Faucet
|
|
|
|
This faucet uses PHP and JSON storage. It connects directly to a Contractless
|
|
node through the authenticated RPC protocol, creates and signs transfers
|
|
locally, and submits them without invoking Contractless CLI programs.
|
|
|
|
## Requirements
|
|
|
|
- 64-bit PHP 8.1 or newer
|
|
- Composer
|
|
- The Contractless PHP Skein and Falcon modules
|
|
- `contractless/contractless-php-rpc`
|
|
- Access to a compatible Contractless RPC node
|
|
- A dedicated, registered faucet wallet
|
|
- Read access to that wallet file
|
|
- A writable private directory for the JSON claim ledger
|
|
|
|
Install the native modules first:
|
|
|
|
```text
|
|
https://contractless.dev/contractless/Contractless-PHP-Modules
|
|
```
|
|
|
|
Install the RPC package in the Composer project containing the faucet:
|
|
|
|
```bash
|
|
composer require contractless/contractless-php-rpc:^0.2.0 -W
|
|
```
|
|
|
|
The web-server account needs permission to read Composer's `vendor` directory
|
|
and faucet wallet, and write the configured claim-ledger directory. It does
|
|
not need access to a node runtime, balance sheet, or Contractless CLI binary.
|
|
|
|
The faucet configuration, wallet file, wallet decryption key, and claim ledger
|
|
must never be downloadable through the web server.
|
|
|
|
## Configuration
|
|
|
|
For an installation at:
|
|
|
|
```text
|
|
/var/www/project/public/faucet
|
|
```
|
|
|
|
copy the included template to the private project root:
|
|
|
|
```bash
|
|
cp /var/www/project/public/faucet/.env.example /var/www/project/faucet.env
|
|
sudo chown root:www-data /var/www/project/faucet.env
|
|
sudo chmod 640 /var/www/project/faucet.env
|
|
```
|
|
|
|
Then edit `/var/www/project/faucet.env` and replace every example value with
|
|
the correct faucet settings.
|
|
|
|
The faucet automatically loads `faucet.env` from the project root two
|
|
directories above the faucet:
|
|
|
|
```text
|
|
/var/www/project/faucet.env
|
|
```
|
|
|
|
No Apache configuration, PHP-FPM pool changes, shell exports, or service
|
|
restarts are required. Reloading the faucet page uses the saved values.
|
|
|
|
The optional `FAUCET_ENV_FILE` process variable can point to a different file,
|
|
but normal installations do not need it.
|
|
|
|
`FAUCET_ADDRESS` determines the active network:
|
|
|
|
- An address ending in `.cltc` configures testnet and displays `CLTC`.
|
|
- An address ending in `.clc` configures mainnet and displays `CLC`.
|
|
|
|
The faucet address itself must be the registered canonical 40-character short
|
|
address. Faucet recipients may enter either a canonical short address or a
|
|
registered vanity address. Vanity inputs are resolved through RPC, and the
|
|
canonical owner address is used for the transfer and 24-hour claim ledger.
|
|
|
|
Point the faucet at its dedicated wallet and provide that wallet's decryption
|
|
key:
|
|
|
|
```text
|
|
CONTRACTLESS_WALLET_PATH=/var/www/project/faucet.wallet
|
|
CONTRACTLESS_WALLET_KEY=wallet-decryption-key
|
|
```
|
|
|
|
The RPC library reads the public key from the wallet, extracts the encrypted
|
|
private key from its image, verifies the image payload HMAC, decrypts the
|
|
private key, proves that the Falcon keypair matches, and verifies that the
|
|
wallet's short address is derived from that public key.
|
|
|
|
`FAUCET_ADDRESS` must match the canonical `short_address` saved in this wallet.
|
|
Keep the wallet file and decryption key outside the public document root, but
|
|
inside a path PHP is permitted to read. For the example layout, the public
|
|
document root is `/var/www/project/public`, so `/var/www/project/faucet.wallet`
|
|
is private from HTTP while remaining available to PHP. Never expose the wallet
|
|
or key through HTML, JavaScript, logs, error responses, or source control.
|
|
|
|
Before enabling the faucet, verify the wallet through the installed RPC
|
|
package:
|
|
|
|
```bash
|
|
cd /path/to/Contractless-PHP-RPC
|
|
php examples/load_wallet.php /private/path/to/faucet.wallet 'wallet decryption key'
|
|
```
|
|
|
|
Configure the node used for RPC:
|
|
|
|
```text
|
|
CONTRACTLESS_RPC_HOST=127.0.0.1
|
|
CONTRACTLESS_RPC_PORT=50050
|
|
CONTRACTLESS_RPC_TIMEOUT=10
|
|
```
|
|
|
|
Use port `50050` for the default testnet RPC or `50055` for the default mainnet
|
|
RPC unless the selected node uses a different configured port.
|
|
|
|
The faucet searches for Composer's autoloader in its own directory, its parent
|
|
directory, and the web-project root two directories above it. Set
|
|
`FAUCET_COMPOSER_AUTOLOAD` to the absolute `vendor/autoload.php` path only when
|
|
Composer is installed elsewhere.
|
|
|
|
For production, set `FAUCET_CLAIMS_FILE` and `FAUCET_LOCK_FILE` to paths
|
|
outside the public web root. The included `storage` directory is suitable for
|
|
local setup and includes an Apache access-denial rule, but server-level private
|
|
storage is preferable.
|
|
|
|
## Claim Rules
|
|
|
|
Each successful request sends 25 base testnet or mainnet coins and pays a
|
|
0.25-coin transfer fee from the faucet wallet.
|
|
|
|
Before reserving or broadcasting a claim, the faucet:
|
|
|
|
- Retrieves its confirmed base-coin balance through RPC
|
|
- Refuses the request when the faucet cannot cover the claim and fee
|
|
- Confirms that the receiving canonical wallet address is registered
|
|
- Creates and signs the transfer locally
|
|
- Submits the exact signed transaction through RPC
|
|
|
|
The faucet records a successful claim only after the node returns
|
|
`successful_broadcast: true`.
|
|
|
|
A claim is rejected when either the canonical wallet address or normalized
|
|
client IP has claimed during the preceding 24 hours. IPv4-mapped IPv6 addresses
|
|
are treated as IPv4. Native IPv6 clients are grouped by `/64`.
|
|
|
|
Claims are reserved under an exclusive file lock before broadcasting. This
|
|
prevents simultaneous requests from passing the same address or IP check.
|
|
Failed broadcasts release their reservation so the user may try again.
|
|
|
|
The faucet trusts `REMOTE_ADDR` and intentionally ignores forwarded-IP headers.
|
|
When deploying behind a reverse proxy, configure the proxy or web server to
|
|
replace `REMOTE_ADDR` only from explicitly trusted proxy addresses.
|
|
|
|
## JSON Ledger
|
|
|
|
The claim ledger is keyed directly by canonical wallet address and normalized
|
|
IP:
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"last_request_timestamp_ms": 0,
|
|
"total_claimed_atomic": 2500000000,
|
|
"total_claims": 1,
|
|
"addresses": {
|
|
"abcd000000000000000000000000000000001234.cltc": {
|
|
"claimed_timestamp": 1785000000,
|
|
"status": "completed",
|
|
"address": "abcd000000000000000000000000000000001234.cltc",
|
|
"ip": "203.0.113.10",
|
|
"txid": "transaction hash"
|
|
}
|
|
},
|
|
"ips": {
|
|
"203.0.113.10": {
|
|
"claimed_timestamp": 1785000000,
|
|
"status": "completed",
|
|
"address": "abcd000000000000000000000000000000001234.cltc",
|
|
"ip": "203.0.113.10",
|
|
"txid": "transaction hash"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Security
|
|
|
|
Run the faucet under a dedicated operating-system account with access only to
|
|
its private configuration and claim ledger. Do not run PHP or the faucet as
|
|
root. Serve the site over HTTPS, keep directory listing disabled, and keep
|
|
`faucet.env` outside the public web root.
|