faucetd: Implement skeleton.

This commit is contained in:
parazyd
2022-04-16 15:09:38 +02:00
parent b02933dcb9
commit df13dc12f4
6 changed files with 248 additions and 0 deletions

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@
/darkfid2
/dnetview
/drk
/faucetd
/gatewayd
/ircd
/tau-cli

26
Cargo.lock generated
View File

@@ -2315,6 +2315,32 @@ dependencies = [
"instant",
]
[[package]]
name = "faucetd"
version = "0.3.0"
dependencies = [
"async-channel",
"async-executor",
"async-std",
"async-trait",
"chrono",
"ctrlc-async",
"darkfi",
"easy-parallel",
"futures-lite",
"lazy-init",
"log",
"rand 0.8.5",
"serde",
"serde_derive",
"serde_json",
"simplelog",
"sled",
"structopt",
"structopt-toml",
"url",
]
[[package]]
name = "feature-probe"
version = "0.1.1"

View File

@@ -18,6 +18,7 @@ members = [
"bin/darkfid",
"bin/darkfid2",
"bin/drk",
"bin/faucetd",
"bin/gatewayd",
"bin/ircd",
"bin/dnetview",

33
bin/faucetd/Cargo.toml Normal file
View File

@@ -0,0 +1,33 @@
[package]
name = "faucetd"
version = "0.3.0"
homepage = "https://dark.fi"
description = "DarkFi faucet daemon"
authors = ["darkfi <dev@dark.fi>"]
repository = "https://github.com/darkrenaissance/darkfi"
license = "AGPL-3.0-only"
edition = "2021"
[dependencies]
async-channel = "1.6.1"
async-executor = "1.4.1"
async-std = "1.11.0"
async-trait = "0.1.53"
chrono = "0.4.19"
ctrlc-async = {version = "3.2.2", default-features = false, features = ["async-std", "termination"]}
darkfi = {path = "../../", features = ["blockchain2", "wallet", "rpc", "net"]}
easy-parallel = "3.2.0"
futures-lite = "1.12.0"
lazy-init = "0.5.0"
log = "0.4.16"
rand = "0.8.5"
serde_json = "1.0.79"
simplelog = "0.12.0-alpha1"
sled = "0.34.7"
url = "2.2.2"
# Argument parsing
serde = "1.0.136"
serde_derive = "1.0.136"
structopt = "0.3.26"
structopt-toml = "0.5.0"

View File

@@ -0,0 +1,17 @@
## faucetd configuration file
##
## Please make sure you go through all the settings so you can configure
## your daemon properly.
# Path to the client database
#database_path = "~/.config/darkfi/darkfid_client.db"
# Path to the wallet database
#wallet_path = "~/.config/darkfi/darkfid_wallet.db"
# Wallet password
wallet_pass = "changeme"
# JSON-RPC listening url
#rpc_listen = "tcp://127.0.0.1:5397"
#rpc_listen = "tls://127.0.0.1:5397"

170
bin/faucetd/src/main.rs Normal file
View File

@@ -0,0 +1,170 @@
use async_executor::Executor;
use async_std::sync::Arc;
use async_trait::async_trait;
use easy_parallel::Parallel;
use futures_lite::future;
use log::info;
use serde_derive::Deserialize;
use serde_json::{json, Value};
use simplelog::{ColorChoice, TermLogger, TerminalMode};
use structopt::StructOpt;
use structopt_toml::StructOptToml;
use url::Url;
use darkfi::{
cli_desc,
rpc::{
jsonrpc,
jsonrpc::{
ErrorCode::{InvalidParams, MethodNotFound},
JsonRequest, JsonResult,
},
rpcserver2::{listen_and_serve, RequestHandler},
},
util::{
cli::{log_config, spawn_config},
path::get_config_path,
},
Result,
};
const CONFIG_FILE: &str = "faucetd_config.toml";
const CONFIG_FILE_CONTENTS: &str = include_str!("../faucetd_config.toml");
#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
#[serde(default)]
#[structopt(name = "faucetd", about = cli_desc!())]
struct Args {
#[structopt(short, long)]
/// Configuration file to use
config: Option<String>,
#[structopt(long, default_value = "testnet")]
/// Chain to use (testnet, mainnet)
chain: String,
#[structopt(long, default_value = "tcp://127.0.0.1:5381")]
/// JSON-RPC listen URL
rpc_listen: Url,
#[structopt(long, default_value = "3600")]
/// Airdrop timeout limit in seconds
airdrop_timeout: u64,
#[structopt(long, default_value = "10")]
/// Airdrop amount limit
airdrop_limit: u64,
#[structopt(short, parse(from_occurrences))]
/// Increase verbosity (-vvv supported)
verbose: u8,
}
pub struct Faucetd {
airdrop_timeout: u64,
airdrop_limit: u64,
}
#[async_trait]
impl RequestHandler for Faucetd {
async fn handle_request(&self, req: JsonRequest) -> JsonResult {
if !req.params.is_array() {
return jsonrpc::error(InvalidParams, None, req.id).into()
}
let params = req.params.as_array().unwrap();
match req.method.as_str() {
Some("ping") => return self.pong(req.id, params).await,
Some("airdrop") => return self.airdrop(req.id, params).await,
Some(_) | None => return jsonrpc::error(MethodNotFound, None, req.id).into(),
}
}
}
impl Faucetd {
pub async fn new(timeout: u64, limit: u64) -> Result<Self> {
Ok(Self { airdrop_timeout: timeout, airdrop_limit: limit })
}
// RPCAPI:
// Returns a `pong` to the `ping` request.
// --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 1}
// <-- {"jsonrpc": "2.0", "result": "pong", "id": 1}
async fn pong(&self, id: Value, _params: &[Value]) -> JsonResult {
jsonrpc::response(json!("pong"), id).into()
}
// Processes an airdrop request and airdrops requested amount to address.
/// Returns transaction ID upon success.
// --> {"jsonrpc": "2.0", "method": "airdrop", "params": ["1DarkFi...", 10], "id": 1}
// <-- {"jsonrpc": "2.0", "result": "txID", "id": 1}
async fn airdrop(&self, id: Value, params: &[Value]) -> JsonResult {
if params.len() != 2 || !params[0].is_string() || !params[1].is_u64() {
return jsonrpc::error(InvalidParams, None, id).into()
}
todo!()
// Check map for timeout
// Update timeout if successful
// Make transaction
// Broadcast
// Return txid
}
}
async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
// We use this handler to block this function after detaching all
// tasks, and to catch a shutdown signal, where we can clean up and
// exit gracefully.
let (signal, shutdown) = async_channel::bounded::<()>(1);
ctrlc_async::set_async_handler(async move {
signal.send(()).await.unwrap();
})
.unwrap();
// Initialize program state
let faucetd = Faucetd::new(args.airdrop_timeout, args.airdrop_limit).await?;
let faucetd = Arc::new(faucetd);
// JSON-RPC server
info!("Starting JSON-RPC server");
ex.spawn(listen_and_serve(args.rpc_listen, faucetd)).detach();
// TODO: Task to periodically scan map and drop timeouts. We don't
// want to keep a log of airdrops.
// Wait for SIGINT
shutdown.recv().await?;
print!("\r");
info!("Caught termination signal, cleaning up and exiting...");
Ok(())
}
fn main() -> Result<()> {
let args = Args::from_args_with_toml("").unwrap();
let cfg_path = get_config_path(args.config, CONFIG_FILE)?;
spawn_config(&cfg_path, CONFIG_FILE_CONTENTS.as_bytes())?;
let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?).unwrap();
let (lvl, conf) = log_config(args.verbose.into())?;
TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
// https://docs.rs/smol/latest/smol/struct.Executor.html#examples
let ex = Arc::new(Executor::new());
let (signal, shutdown) = async_channel::unbounded::<()>();
let (_, result) = Parallel::new()
// Run four executor threads
.each(0..4, |_| future::block_on(ex.run(shutdown.recv())))
// Run the main future on the current thread.
.finish(|| {
future::block_on(async {
realmain(args, ex.clone()).await?;
drop(signal);
Ok::<(), darkfi::Error>(())
})
});
result
}