Skip to content

Commit

Permalink
Migrate faucet to new framework
Browse files Browse the repository at this point in the history
  • Loading branch information
CapCap authored and aptos-bot committed Mar 8, 2022
1 parent 51bbe05 commit 63dda79
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 441 deletions.
43 changes: 20 additions & 23 deletions crates/aptos-faucet/README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
# Faucet

Faucet is a service for creating and funding accounts on Diem Network by treasury compliance account.
Faucet is a service for creating and funding accounts on the Aptos Network.
It is meant to be used for devnets and testnets.

It is created for Testnet usage only.

## Mint API

Mint API can create and fund your account. It will fund your account if it is already created onchain.
The Mint API can create and fund your account.

* Base URL: `http://faucet.testnet.aptoslabs.com/`
* Path: `/mint`
* Method: POST

URL Query Params:

| param name | type | required? | description |
|------------------------|--------|-----------|----------------------------------------------------------------------|
| `amount` | int | Y | amount of coins to mint |
| `auth_key` | string | Y | your account authentication key |
| `currency_code` | string | Y | the currency code, e.g. XDX |
| `return_txns` | bool | N | returns the transactions for creating / funding the account |
| `is_designated_dealer` | bool | N | creates a designated dealer account instead of a parent VASP account |
| `vasp_domain` | string | N | domain for VASP to add or remove for parent VASP, is_designated_dealer must be set to false |
| `is_remove_domain` | bool | N | add or remove the above VASP domain to parent VASP account |
| param name | type | required? | description |
|------------------------|--------|-----------|-------------------------------------------------------------|
| `amount` | int | Y | Amount of coins to mint. This is not always enabled. |
| `pub_key` | string | Y | Your account public key (ed25519) |
| `return_txns` | bool | N | Returns the transactions for creating / funding the account |

Notes:
* By default, the account created is a parent VASP account.
* Type bool means you set value to a string "true" or "false"
* For existing accounts as defined by the auth_key, the service submits 1 transfer funds transaction.
* For new accounts as defined by the auth_key, the service first issues a transaction for creating the account and another for transferring funds.
* All funds transferred come from the account 000000000000000000000000000000dd.
* Clients should retry their request if the requests or the transactions execution failed. One reason for failure is that, under load, the service may issue transactions with duplicate sequence numbers. Only one of those transactions will be executed, the rest will fail.
* For existing accounts as defined by the pub_key, the service submits 1 transfer funds transaction.
* For new accounts as defined by the pub_key, the service first issues a transaction for creating the account and another for transferring funds.
* All funds transferred come from the account 0xa550c18.
* Clients should retry their request if the requests or the transaction execution failed. One reason for failure is that, under load, the service may issue transactions with duplicate sequence numbers. Only one of those transactions will be executed, the rest will fail.

### Response

If no query param `return_txns` or it is not "true", server returns an unsigned int 64 in HTTP response body. The number is the account sequence number of the account `000000000000000000000000000000dd` on Testnet after executing the request.
Nominally, this number can be used for looking up the submitted transaction. However, under load, this number may be shared by multiple transactions.
If the query param `return_txns` is not provided, or it is not "true", the server returns an unsigned int64 in the HTTP response body,
which is the account sequence number of the account `0xa550c18` after executing your request.
Nominally, this number can be used for looking up the submitted transaction.
However, under load, this number may be shared by multiple transactions.

Set query param `return_txns`, server will response all transactions for creating and funding your account.
The respond HTTP body is hex encoded bytes of BCS encoded `Vec<aptos_types::transaction::SignedTransaction>`.
If the query param `return_txns` is set, the server will respond with the transactions for creating and funding your account.
The response HTTP body is hex encoded bytes of BCS encoded `Vec<aptos_types::transaction::SignedTransaction>`.

Decode Example ([source](https://diem.github.io/client-sdk-python/diem/testnet.html#diem.testnet.Faucet)):

Expand All @@ -52,12 +49,12 @@ Decode Example ([source](https://diem.github.io/client-sdk-python/diem/testnet.h

```

You should retry the mint API call if the returned transactions executed failed.
You should retry the mint API call if the transaction execution fails.


## Example

```bash
curl -X POST http://faucet.testnet.aptoslabs.com/mint\?amount\=1000000\&currency_code\=XUS\&auth_key\=459c77a38803bd53f3adee52703810e3a74fd7c46952c497e75afb0a7932586d\&return_txns\=true
curl -X POST http://faucet.testnet.aptoslabs.com/mint\?amount\=1000000\&pub_key\=459c77a38803bd53f3adee52703810e3a74fd7c46952c497e75afb0a7932586d\&return_txns\=true
01000000000000000000000000000000dd05a600000000000001e001a11ceb0b010000000701000202020403061004160205181d0735600895011000000001010000020001000003020301010004010300010501060c0108000506080005030a020a020005060c05030a020a020109000b4469656d4163636f756e741257697468647261774361706162696c6974791b657874726163745f77697468647261775f6361706162696c697479087061795f66726f6d1b726573746f72655f77697468647261775f6361706162696c69747900000000000000000000000000000001010104010c0b0011000c050e050a010a020b030b0438000b051102020107000000000000000000000000000000010358555303585553000403a74fd7c46952c497e75afb0a7932586d0140420f00000000000400040040420f00000000000000000000000000035855532a610f6000000000020020056244e7bf776e471d818dc18fdf7b8833c5439ac9a96e126f8f32c7bc7c14b64026a2c45c8e4066c661dc4f36baa6ad61499999b548b9f63ad15853660c408cedec3078b7773a829ec48de8b04291cd11530734b2f91d5e42f35a4c6378cb7c09
```
128 changes: 67 additions & 61 deletions crates/aptos-faucet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
// Copyright (c) The Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! This crates provides Faucet service for creating Testnet with simplified on-chain account creation
//! and minting coins.
//! This crate provides the Faucet service for creating and funding accounts on the Aptos Network.
//!
//! THIS SERVICE SHOULD NEVER BE DEPLOYED TO MAINNET.
//!
//! ## Launch service
//!
//! Launch faucet service local and connect to Testnet:
//! Launch faucet service locally and connect to Testnet:
//!
//! ```bash
//! cargo run -p aptos-faucet -- -a 127.0.0.1 -p 8080 -c TESTNET \
//! -m <treasury-compliance-private-key-path> -s https://testnet.aptoslabs.com/v1
//! cargo run --bin aptos-faucet -- -c TESTNET -m <mint-private-key-path> -s http://localhost:8080 -p 8081
//! ```
//!
//! Check help doc for options details:
Expand All @@ -22,14 +20,18 @@
//! ```
use anyhow::{anyhow, Result};
use aptos_crypto::ed25519::Ed25519PublicKey;
use aptos_logger::info;
use aptos_rest_client::Client;
use aptos_sdk::{
transaction_builder::{Currency, TransactionFactory},
transaction_builder::{aptos_stdlib, TransactionFactory},
types::{
account_address::AccountAddress,
chain_id::ChainId,
transaction::{authenticator::AuthenticationKey, SignedTransaction},
transaction::{
authenticator::{AuthenticationKey, AuthenticationKeyPreimage},
SignedTransaction,
},
LocalAccount,
},
};
Expand All @@ -46,28 +48,28 @@ use warp::{Filter, Rejection, Reply};
pub mod mint;

pub struct Service {
treasury_compliance_account: Mutex<LocalAccount>,
designated_dealer_account: Mutex<LocalAccount>,
pub faucet_account: Mutex<LocalAccount>,
transaction_factory: TransactionFactory,
client: Client,
endpoint: String,
fixed_amount: Option<u64>,
}

impl Service {
pub fn new(
endpoint: String,
chain_id: ChainId,
treasury_compliance_account: LocalAccount,
designated_dealer_account: LocalAccount,
faucet_account: LocalAccount,
fixed_amount: Option<u64>,
) -> Self {
let client = Client::new(Url::parse(&endpoint).expect("Invalid rest endpont"));
let client = Client::new(Url::parse(&endpoint).expect("Invalid rest endpoint"));
Service {
treasury_compliance_account: Mutex::new(treasury_compliance_account),
designated_dealer_account: Mutex::new(designated_dealer_account),
faucet_account: Mutex::new(faucet_account),
transaction_factory: TransactionFactory::new(chain_id)
.with_transaction_expiration_time(30),
client,
endpoint,
fixed_amount,
}
}

Expand Down Expand Up @@ -109,12 +111,19 @@ fn accounts_routes(
}

#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct CreateAccountParams {
authentication_key: AuthenticationKey,
currency: Currency,
pub_key: Ed25519PublicKey,
}

impl CreateAccountParams {
fn pre_image(&self) -> AuthenticationKeyPreimage {
AuthenticationKeyPreimage::ed25519(&self.pub_key)
}

fn receiver(&self) -> AccountAddress {
AuthenticationKey::ed25519(&self.pub_key).derived_address()
}
}
fn create_account_route(
service: Arc<Service>,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
Expand Down Expand Up @@ -143,55 +152,42 @@ async fn create_account(
params: CreateAccountParams,
) -> Result<SignedTransaction> {
// Check to ensure the account hasn't already been created
if service
.client
.get_account(params.authentication_key.derived_address())
.await
.is_ok()
{
if service.client.get_account(params.receiver()).await.is_ok() {
return Err(anyhow!("account already exists"));
}

// get TC account's sequence number
let tc_account_address = service
.treasury_compliance_account
.lock()
.unwrap()
.address();
let tc_sequence_number = service
let faucet_account_address = service.faucet_account.lock().unwrap().address();
let faucet_sequence_number = service
.client
.get_account(tc_account_address)
.get_account(faucet_account_address)
.await
.map_err(|_| anyhow::format_err!("treasury compliance account not found"))?
.map_err(|_| anyhow::format_err!("faucet account {} not found", faucet_account_address))?
.into_inner()
.sequence_number;

let txn = {
let mut treasury_account = service.treasury_compliance_account.lock().unwrap();
if tc_sequence_number > treasury_account.sequence_number() {
*treasury_account.sequence_number_mut() = tc_sequence_number;
let mut faucet_account = service.faucet_account.lock().unwrap();
if faucet_sequence_number > faucet_account.sequence_number() {
*faucet_account.sequence_number_mut() = faucet_sequence_number;
}

let builder = service.transaction_factory.create_parent_vasp_account(
params.currency,
0, // sliding_nonce
params.authentication_key,
&format!("No. {}", treasury_account.sequence_number()),
false, // add all currencies
let builder = service.transaction_factory.payload(
aptos_stdlib::encode_create_account_script_function(
params.receiver(),
params.pre_image().into_vec(),
),
);

treasury_account.sign_with_transaction_builder(builder)
faucet_account.sign_with_transaction_builder(builder)
};

service.client.submit(&txn).await?;
Ok(txn)
}

#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct FundAccountParams {
amount: u64,
currency: Currency,
amount: Option<u64>,
}

fn fund_account_route(
Expand Down Expand Up @@ -223,35 +219,45 @@ async fn fund_account(
address: AccountAddress,
params: FundAccountParams,
) -> Result<SignedTransaction> {
if service.fixed_amount.is_some() && params.amount.is_some() {
return Err(anyhow::format_err!(
"Mint amount is fixed to {} on this faucet",
service.fixed_amount.unwrap()
));
}

if service.fixed_amount.is_none() && params.amount.is_none() {
return Err(anyhow::format_err!("Mint amount must be provided"));
}

let amount = service
.fixed_amount
.unwrap_or_else(|| params.amount.unwrap());

// Check to ensure the account has already been created
if service.client.get_account(address).await.is_err() {
return Err(anyhow!("account doesn't exist"));
}

// get DD account's sequence number
let dd_account_address = service.designated_dealer_account.lock().unwrap().address();
let dd_sequence_number = service
let faucet_account_address = service.faucet_account.lock().unwrap().address();
let faucet_sequence_number = service
.client
.get_account(dd_account_address)
.get_account(faucet_account_address)
.await
.map_err(|_| anyhow::format_err!("treasury compliance account not found"))?
.map_err(|_| anyhow::format_err!("faucet account {} not found", faucet_account_address))?
.into_inner()
.sequence_number;

let txn = {
let mut dd_account = service.designated_dealer_account.lock().unwrap();
if dd_sequence_number > dd_account.sequence_number() {
*dd_account.sequence_number_mut() = dd_sequence_number;
let mut faucet_account = service.faucet_account.lock().unwrap();
if faucet_sequence_number > faucet_account.sequence_number() {
*faucet_account.sequence_number_mut() = faucet_sequence_number;
}

dd_account.sign_with_transaction_builder(
service.transaction_factory.peer_to_peer_with_metadata(
params.currency,
address,
params.amount,
vec![],
vec![],
),
faucet_account.sign_with_transaction_builder(
service
.transaction_factory
.payload(aptos_stdlib::encode_mint_script_function(address, amount)),
)
};

Expand Down
Loading

0 comments on commit 63dda79

Please sign in to comment.