Blockchain applications / D20
View on GitHubEthereum Faucet
Overview
This example deploys a faucet service in an Ethereum PoA or PoS blockchain, allowing users to request test Ether for their accounts. Users can configure funding requests during emulator construction or request funds after the blockchain starts through HTTP APIs and client programs. The faucet transfers Ether by submitting real transactions through an Ethereum node, allowing users to observe the funding process through transaction records and account balances. You can further extend the scenario by integrating the faucet with wallets, smart contracts, or custom applications that require funded accounts. This example provides a convenient way to manage test funds and support blockchain application development and testing.
Faucet Server
Before sending a transaction to the blockchain, the user’s account needs to have some fund. In most test nets, a faucet server is provided to fund users’ accounts after receiving requests. We have implemented such a faucet server in the SEED blockchain emulator.
This example demonstrates how to use the Faucet server with either a PoA blockchain based on D00_ethereum_poa or a PoS blockchain based on D01_ethereum_pos.
Table of Content
- How to Run
- Create Faucet Server
- How Faucet Works
- Fund Accounts Using Faucet
- Fund Accounts During the Build Time
- Fund Accounts During the Run Time Using curl
- Fund Accounts During the Run Time Using FaucetUserService
- Fund Accounts During the Run Time Using Python
- Observe Funding Results
- Developer Manual
How to Run
Run the following commands from this example directory:
python faucet.py [poa|pos] [amd|arm]
cd output
docker compose up
Arguments:
poa: Use the PoA blockchain fromD00_ethereum_poa. This is the default.pos: Use the PoS blockchain fromD01_ethereum_pos.amd: Generate Docker configuration for AMD64. This is the default.arm: Generate Docker configuration for ARM64 hosts.
Examples:
python faucet.py poa amd
python faucet.py pos arm
The script loads the selected blockchain component, gets the existing
Faucet server from that blockchain, adds build-time funding requests,
and creates a FaucetUserService container to demonstrate runtime
funding.
Create Faucet Server
We first need to add a Faucet server to a blockchain. This server runs a web server, which can transfer funds from its own account to whoever requests funds.
In the PoA blockchain from D00_ethereum_poa, the Faucet server is created as follows:
faucet: FaucetServer = blockchain.createFaucetServer(
vnode='faucet',
port=80,
linked_eth_node='eth5',
balance=10000,
max_fund_amount=10)
faucet.setDisplayName('Faucet')
In the PoS blockchain from D01_ethereum_pos, the Faucet server is linked to a Geth execution-layer node:
faucet: FaucetServer = blockchain.createFaucetServer(
vnode='faucet',
port=80,
linked_eth_node='gethnode1',
balance=10000,
max_fund_amount=10)
faucet.setDisplayName('Faucet')
We can specify the following parameters:
vnode: the virtual node name of the Faucet server.port: the port number used by the Faucet web server.linked_eth_node: the Ethereum node used by the Faucet server to send transactions to the blockchain. This node must have HTTP RPC enabled.balance: the initial balance, in Ethers, of the Faucet account. This account is created during the build time and added to the genesis block.max_fund_amount: the maximal amount of fund, in Ethers, that can be transferred in each request.
Because the selected base blockchain already has a Faucet server, this example gets the existing Faucet server object:
eth = emu.getLayer('EthereumService')
blockchain = eth.getBlockchainByName(eth.getBlockchainNames()[0])
faucet_name = blockchain.getFaucetServerNames()[0]
faucet = blockchain.getFaucetServerByName(faucet_name)
How Faucet Works
The Faucet server is not an Ethereum node. It is a web service that
owns an Ethereum account. When it receives a /fundme request, it
constructs a transaction from its own account to the requested recipient
address, signs the transaction with the Faucet account’s private key,
and sends the raw transaction through the linked Ethereum node’s HTTP
RPC interface.
Therefore, the Faucet does not directly modify account balances. It funds an account by sending a normal Ethereum transaction.
Fund Accounts Using Faucet
This example demonstrates three ways to fund accounts:
- Fund known addresses during the build time.
- Fund an address during the run time using
curl. - Fund a dynamically created account during the run time using
FaucetUserService.
Fund Accounts During the Build Time
During the emulator build time, if we already know the account address, we can ask the Faucet to fund it. The actual transaction is carried out after the emulator starts.
faucet.fund('0x72943017a1fa5f255fc0f06625aec22319fcd5b3', 2)
faucet.fund('0x5449ba5c5f185e9694146d60cfe72681e2158499', 5)
These API calls generate startup commands for the Faucet container. When the emulation starts and the Faucet server becomes ready, those commands send HTTP requests to the Faucet server.
Fund Accounts During the Run Time Using curl
Very often, we do not know the account addresses during the build time, because the accounts are created during the run time. In this case, the user can send an HTTP request to the Faucet server to ask it to fund a specified account.
First find the Faucet container and its IP address:
docker ps | grep -i faucet
docker inspect <faucet-container-name>
Check whether the Faucet server is running:
curl http://<faucet-ip>:80/
The expected response is:
OK
Data in the funding request can be conveyed using either form data or JSON data.
Using form data:
curl -X POST \
-d "address=0x72943017a1fa5f255fc0f06625aec22319fcd5b3&amount=2" \
http://<faucet-ip>:80/fundme
Using JSON data:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"address":"0x72943017a1fa5f255fc0f06625aec22319fcd5b3","amount":2}' \
http://<faucet-ip>:80/fundme
The requested amount cannot exceed max_fund_amount, which is 10 in
this example.
Fund Accounts During the Run Time Using FaucetUserService
This example also creates a FaucetUserService container. It
demonstrates how a service can create an account during the emulator
run time and then request funds from the Faucet server.
faucetUserService = FaucetUserService()
faucetUserService.install('faucetUser').setDisplayName('FaucetUser')
faucet_info = blockchain.getFaucetServerInfo()
faucetUserService.setFaucetServerInfo(faucet_info[0]['name'],
faucet_info[0]['port'])
emu.addBinding(Binding('faucetUser'))
emu.addLayer(faucetUserService)
The FaucetUserService resolves the Faucet server’s IP address from
the Faucet vnode name and port number. It then generates the following
script inside the faucetUser container:
/faucet_user/fundme.py
When the faucetUser container starts, this script waits for the
Faucet server to become available. If no account address is given to
the script, it creates a new Ethereum account and sends a /fundme
request for that new account.
After the emulator starts, use the following command to observe this runtime funding process:
docker logs <faucetuser-container-name>
The logs should include the newly created account address and a success message from the Faucet server.
Fund Accounts During the Run Time Using Python
We can also write Python programs to interact with the Faucet server. A helper class called FaucetHelper.py is created to make writing such programs easier.
The helper sends a JSON request to the Faucet server’s /fundme API:
from FaucetHelper import FaucetHelper
faucet = FaucetHelper("http://<faucet-ip>:80")
faucet.wait_for_server_ready()
faucet.send_fundme_request(
"0x72943017a1fa5f255fc0f06625aec22319fcd5b3",
2)
Observe Funding Results
The best way to understand the Faucet is to observe both the web request and the blockchain transaction.
First check the faucetUser logs:
docker logs <faucetuser-container-name>
Then check the Faucet server logs:
docker logs <faucet-container-name>
The Faucet logs should show the recipient address, requested amount, transaction hash, and transaction receipt.
To verify the recipient balance using Geth HTTP RPC, find a Geth node IP address and run:
curl -s -X POST \
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["<recipient-address>","latest"],"id":1}' \
http://<geth-ip>:8545
The returned balance is in Wei and encoded as a hexadecimal number.
This example compiles the Docker output with etherViewEnabled=True.
When the selected blockchain and explorer services are running, the
generated output can also provide a web interface for observing blocks,
transactions, and account activity.
Developer Manual
The FaucetUserService section above shows how to request funds during
runtime from a service container. For implementation details about how
to build a custom service that uses the Faucet server, see the
developer manual.
Source: SEED Emulator · D20 README