mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
faucetd: Implement skeleton.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@
|
||||
/darkfid2
|
||||
/dnetview
|
||||
/drk
|
||||
/faucetd
|
||||
/gatewayd
|
||||
/ircd
|
||||
/tau-cli
|
||||
|
||||
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
33
bin/faucetd/Cargo.toml
Normal 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"
|
||||
17
bin/faucetd/faucetd_config.toml
Normal file
17
bin/faucetd/faucetd_config.toml
Normal 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
170
bin/faucetd/src/main.rs
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user