main.rs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{collections::HashMap, path::Path, str::FromStr, sync::Arc};
  19. use async_trait::async_trait;
  20. use chrono::Utc;
  21. use darkfi_money_contract::{
  22. client::{
  23. transfer_v1::TransferCallBuilder, MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_PUBLIC,
  24. MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE, MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
  25. },
  26. MoneyFunction::TransferV1 as MoneyTransfer,
  27. MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
  28. };
  29. use darkfi_sdk::{
  30. crypto::{
  31. contract_id::MONEY_CONTRACT_ID, mimc_vdf, pasta_prelude::Field, Keypair, MerkleNode,
  32. MerkleTree, PublicKey, DARK_TOKEN_ID,
  33. },
  34. num_bigint::BigUint,
  35. num_traits::Num,
  36. pasta::{group::ff::PrimeField, pallas},
  37. tx::ContractCall,
  38. };
  39. use darkfi_serial::{deserialize, serialize, Encodable};
  40. use log::{debug, error, info};
  41. use rand::rngs::OsRng;
  42. use smol::{
  43. lock::{Mutex, RwLock},
  44. stream::StreamExt,
  45. };
  46. use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
  47. use tinyjson::JsonValue;
  48. use url::Url;
  49. use darkfi::{
  50. async_daemonize,
  51. blockchain::contract_store::SMART_CONTRACT_ZKAS_DB_NAME,
  52. cli_desc,
  53. consensus::{
  54. constants::{
  55. MAINNET_BOOTSTRAP_TIMESTAMP, MAINNET_GENESIS_HASH_BYTES, MAINNET_GENESIS_TIMESTAMP,
  56. MAINNET_INITIAL_DISTRIBUTION, TESTNET_BOOTSTRAP_TIMESTAMP, TESTNET_GENESIS_HASH_BYTES,
  57. TESTNET_GENESIS_TIMESTAMP, TESTNET_INITIAL_DISTRIBUTION,
  58. },
  59. proto::{ProtocolSync, ProtocolTx},
  60. task::block_sync_task,
  61. ValidatorState, ValidatorStatePtr,
  62. },
  63. net,
  64. net::P2pPtr,
  65. rpc::{
  66. jsonrpc::{
  67. ErrorCode::{InternalError, InvalidParams, MethodNotFound},
  68. JsonError, JsonRequest, JsonResponse, JsonResult,
  69. },
  70. server::{listen_and_serve, RequestHandler},
  71. },
  72. system::{sleep, StoppableTask},
  73. tx::Transaction,
  74. util::{parse::decode_base10, path::expand_path},
  75. wallet::{WalletDb, WalletPtr},
  76. zk::{proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses},
  77. zkas::ZkBinary,
  78. Error, Result,
  79. };
  80. mod error;
  81. use error::{server_error, RpcError};
  82. const CONFIG_FILE: &str = "faucetd_config.toml";
  83. const CONFIG_FILE_CONTENTS: &str = include_str!("../faucetd_config.toml");
  84. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  85. #[serde(default)]
  86. #[structopt(name = "faucetd", about = cli_desc!())]
  87. struct Args {
  88. #[structopt(short, long)]
  89. /// Configuration file to use
  90. config: Option<String>,
  91. #[structopt(long, default_value = "testnet")]
  92. /// Chain to use (testnet, mainnet)
  93. chain: String,
  94. #[structopt(long, default_value = "~/.config/darkfi/faucetd_wallet.db")]
  95. /// Path to wallet database
  96. wallet_path: String,
  97. #[structopt(long, default_value = "changeme")]
  98. /// Password for the wallet database
  99. wallet_pass: String,
  100. #[structopt(long, default_value = "~/.config/darkfi/faucetd_blockchain")]
  101. /// Path to blockchain database
  102. database: String,
  103. #[structopt(long, default_value = "tcp://127.0.0.1:9340")]
  104. /// JSON-RPC listen URL
  105. rpc_listen: Url,
  106. #[structopt(long)]
  107. /// P2P accept addresses for the syncing protocol
  108. sync_p2p_accept: Vec<Url>,
  109. #[structopt(long)]
  110. /// P2P external addresses for the syncing protocol
  111. sync_p2p_external: Vec<Url>,
  112. #[structopt(long, default_value = "8")]
  113. /// Connection slots for the syncing protocol
  114. sync_slots: usize,
  115. #[structopt(long)]
  116. /// Connect to seed for the syncing protocol (repeatable flag)
  117. sync_p2p_seed: Vec<Url>,
  118. #[structopt(long)]
  119. /// Connect to peer for the syncing protocol (repeatable flag)
  120. sync_p2p_peer: Vec<Url>,
  121. #[structopt(long)]
  122. /// Prefered transports of outbound connections for the syncing protocol (repeatable flag)
  123. sync_p2p_transports: Vec<String>,
  124. #[structopt(long)]
  125. /// Enable localnet hosts
  126. localnet: bool,
  127. #[structopt(long)]
  128. /// Whitelisted cashier address (repeatable flag)
  129. cashier_pub: Vec<String>,
  130. #[structopt(long)]
  131. /// Whitelisted faucet address (repeatable flag)
  132. faucet_pub: Vec<String>,
  133. #[structopt(long, default_value = "600")]
  134. /// Airdrop timeout limit in seconds
  135. airdrop_timeout: i64,
  136. #[structopt(long, default_value = "10")]
  137. /// Airdrop amount limit
  138. airdrop_limit: String, // We convert this to u64 with decode_base10
  139. #[structopt(short, long)]
  140. /// Set log file to ouput into
  141. log: Option<String>,
  142. #[structopt(short, parse(from_occurrences))]
  143. /// Increase verbosity (-vvv supported)
  144. verbose: u8,
  145. }
  146. type ProvingKeyMap = Arc<RwLock<HashMap<[u8; 32], Vec<(String, ProvingKey, ZkBinary)>>>>;
  147. type AirdropMap = Arc<Mutex<HashMap<[u8; 32], i64>>>;
  148. type ChallengeMap = Arc<Mutex<HashMap<[u8; 32], (BigUint, u64)>>>;
  149. pub struct Faucetd {
  150. synced: Mutex<bool>, // AtomicBool is weird in Arc
  151. sync_p2p: P2pPtr,
  152. validator_state: ValidatorStatePtr,
  153. keypair: Keypair,
  154. _wallet: WalletPtr,
  155. merkle_tree: MerkleTree,
  156. airdrop_timeout: i64,
  157. airdrop_limit: u64,
  158. airdrop_map: AirdropMap,
  159. challenge_map: ChallengeMap,
  160. proving_keys: ProvingKeyMap,
  161. }
  162. #[async_trait]
  163. impl RequestHandler for Faucetd {
  164. async fn handle_request(&self, req: JsonRequest) -> JsonResult {
  165. match req.method.as_str() {
  166. "challenge" => return self.challenge(req.id, req.params).await,
  167. "airdrop" => return self.airdrop(req.id, req.params).await,
  168. _ => return JsonError::new(MethodNotFound, None, req.id).into(),
  169. }
  170. }
  171. }
  172. impl Faucetd {
  173. pub async fn new(
  174. validator_state: ValidatorStatePtr,
  175. sync_p2p: P2pPtr,
  176. wallet: WalletPtr,
  177. timeout: i64,
  178. limit: u64,
  179. ) -> Result<Self> {
  180. // Here we initialize the wallet for the money contract.
  181. let merkle_tree = Self::initialize_wallet(wallet.clone()).await?;
  182. // This is kinda bad, but whatever. The hashmaps hold proving keys for
  183. // the money contract. We keep it under RwLock in case we want to add
  184. // other proving keys to it later.
  185. let proving_keys = Arc::new(RwLock::new(HashMap::new()));
  186. // For now we'll create the keys for the money contract
  187. let cid = *MONEY_CONTRACT_ID;
  188. // Do a lookup for the money contract's zkas database and fetch the circuits.
  189. let blockchain = { validator_state.read().await.blockchain.clone() };
  190. let db_handle =
  191. blockchain.contracts.lookup(&blockchain.sled_db, &cid, SMART_CONTRACT_ZKAS_DB_NAME)?;
  192. let Some(mint_zkbytes) = db_handle.get(serialize(&MONEY_CONTRACT_ZKAS_MINT_NS_V1))? else {
  193. error!(target: "faucetd", "{} zkas bincode not found in sled database", MONEY_CONTRACT_ZKAS_MINT_NS_V1);
  194. return Err(Error::ZkasBincodeNotFound)
  195. };
  196. let Some(burn_zkbytes) = db_handle.get(serialize(&MONEY_CONTRACT_ZKAS_BURN_NS_V1))? else {
  197. error!(target: "faucetd", "{} zkas bincode not found in sled database", MONEY_CONTRACT_ZKAS_BURN_NS_V1);
  198. return Err(Error::ZkasBincodeNotFound)
  199. };
  200. let (mint_zkbin, _): (Vec<u8>, Vec<u8>) = deserialize(&mint_zkbytes)?;
  201. let (burn_zkbin, _): (Vec<u8>, Vec<u8>) = deserialize(&burn_zkbytes)?;
  202. let mint_zkbin = ZkBinary::decode(&mint_zkbin)?;
  203. let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin)?, &mint_zkbin);
  204. let burn_zkbin = ZkBinary::decode(&burn_zkbin)?;
  205. let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin)?, &burn_zkbin);
  206. info!(target: "faucetd", "Creating mint circuit proving key");
  207. let mint_provingkey = ProvingKey::build(mint_zkbin.k, &mint_circuit);
  208. info!(target: "faucetd", "Creating burn circuit proving key");
  209. let burn_provingkey = ProvingKey::build(burn_zkbin.k, &burn_circuit);
  210. {
  211. let provingkeys = vec![
  212. (MONEY_CONTRACT_ZKAS_MINT_NS_V1.to_string(), mint_provingkey, mint_zkbin),
  213. (MONEY_CONTRACT_ZKAS_BURN_NS_V1.to_string(), burn_provingkey, burn_zkbin),
  214. ];
  215. let mut proving_keys_w = proving_keys.write().await;
  216. proving_keys_w.insert(cid.inner().to_repr(), provingkeys);
  217. }
  218. // Get or create an initial keypair for signing transactions
  219. let keypair = Self::initialize_keypair(wallet.clone()).await?;
  220. info!(target: "faucetd", "Faucet pubkey: {}", keypair.public);
  221. let faucetd = Self {
  222. synced: Mutex::new(false),
  223. sync_p2p,
  224. validator_state,
  225. keypair,
  226. _wallet: wallet,
  227. merkle_tree,
  228. airdrop_timeout: timeout,
  229. airdrop_limit: limit,
  230. airdrop_map: Arc::new(Mutex::new(HashMap::new())),
  231. challenge_map: Arc::new(Mutex::new(HashMap::new())),
  232. proving_keys,
  233. };
  234. Ok(faucetd)
  235. }
  236. async fn initialize_wallet(wallet: WalletPtr) -> Result<MerkleTree> {
  237. // Perform wallet initialization for the money contract
  238. let wallet_schema = include_str!("../../../src/contract/money/wallet.sql");
  239. // Get a wallet connection
  240. info!(target: "faucetd", "Acquiring wallet connection");
  241. let conn = wallet.conn.lock().await;
  242. info!(target: "faucetd", "Initializing wallet schema");
  243. conn.execute(wallet_schema, rusqlite::params![])?;
  244. let query = format!("SELECT * FROM {}", MONEY_TREE_COL_TREE);
  245. let merkle_tree = conn.query_row(&query, [], |row| {
  246. let tree_bytes: Vec<u8> = row.get(MONEY_TREE_COL_TREE)?;
  247. Ok(deserialize(&tree_bytes).unwrap())
  248. });
  249. let merkle_tree = match merkle_tree {
  250. Ok(v) => {
  251. info!(target: "faucetd", "Merkle tree already exists");
  252. v
  253. }
  254. Err(_) => {
  255. let mut tree = MerkleTree::new(100);
  256. tree.append(MerkleNode::from(pallas::Base::ZERO));
  257. let tree_bytes = serialize(&tree);
  258. let query = format!(
  259. "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1)",
  260. MONEY_TREE_TABLE, MONEY_TREE_TABLE, MONEY_TREE_COL_TREE
  261. );
  262. conn.execute(&query, rusqlite::params![tree_bytes])?;
  263. info!(target: "faucetd", "Successfully initialized Merkle tree");
  264. tree
  265. }
  266. };
  267. Ok(merkle_tree)
  268. }
  269. async fn initialize_keypair(wallet: WalletPtr) -> Result<Keypair> {
  270. let conn = wallet.conn.lock().await;
  271. let query = format!(
  272. "SELECT {}, {} FROM {};",
  273. MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE
  274. );
  275. let keypair = conn.query_row(&query, [], |row| {
  276. let public_bytes: Vec<u8> = row.get("public")?;
  277. let secret_bytes: Vec<u8> = row.get("secret")?;
  278. let public = deserialize(&public_bytes).unwrap();
  279. let secret = deserialize(&secret_bytes).unwrap();
  280. Ok(Keypair { public, secret })
  281. });
  282. let keypair = match keypair {
  283. Ok(k) => k,
  284. Err(_) => {
  285. let keypair = Keypair::random(&mut OsRng);
  286. let is_default = 0;
  287. let public_bytes = serialize(&keypair.public);
  288. let secret_bytes = serialize(&keypair.secret);
  289. let query = format!(
  290. "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3)",
  291. MONEY_KEYS_TABLE,
  292. MONEY_KEYS_COL_IS_DEFAULT,
  293. MONEY_KEYS_COL_PUBLIC,
  294. MONEY_KEYS_COL_SECRET
  295. );
  296. conn.execute(&query, rusqlite::params![is_default, public_bytes, secret_bytes])?;
  297. info!(target: "faucetd", "Wrote keypair to wallet");
  298. keypair
  299. }
  300. };
  301. Ok(keypair)
  302. }
  303. // RPCAPI:
  304. // Request a VDF challenge in order to become eligible for an airdrop. It is then
  305. // necessary to execute the VDF with the challenge as input and pass it to the
  306. // `airdrop` call, which the faucet will then verify.
  307. //
  308. // **Params:**
  309. // * `array[0]`: base58 encoded address string of the recipient
  310. //
  311. // **Returns:**
  312. // * `array[0]`: hex-encoded challenge string
  313. // * `array[1]`: n steps (`u64`) needed for VDF evaluation
  314. //
  315. // --> {"jsonrpc": "2.0", "method": "challenge", "params": ["1DarkFi..."], "id": 1}
  316. // <-- {"jsonrpc": "2.0", "result": ["0x123...", 10000], "id": 1}
  317. async fn challenge(&self, id: u16, params: JsonValue) -> JsonResult {
  318. const N_STEPS: u64 = 2_000_000;
  319. let params = params.get::<Vec<JsonValue>>().unwrap();
  320. if params.len() != 1 || !params[0].is_string() {
  321. return JsonError::new(InvalidParams, None, id).into()
  322. }
  323. if !(*self.synced.lock().await) {
  324. error!(target: "faucetd", "challenge(): Blockchain is not yet synced");
  325. return JsonError::new(InternalError, None, id).into()
  326. }
  327. let pubkey = params[0].get::<String>().unwrap();
  328. let pubkey = match PublicKey::from_str(pubkey) {
  329. Ok(v) => v,
  330. Err(e) => {
  331. error!(target: "faucetd", "challenge(): Failed parsing PublicKey from String: {}", e);
  332. return server_error(RpcError::ParseError, id)
  333. }
  334. };
  335. let map = self.challenge_map.lock().await;
  336. if map.contains_key(&pubkey.to_bytes()) {
  337. return server_error(RpcError::RateLimitReached, id)
  338. }
  339. drop(map);
  340. // Create a random challenge
  341. let mut hasher = blake3::Hasher::new();
  342. hasher.update(&pubkey.to_bytes());
  343. hasher.update(&pallas::Base::random(&mut OsRng).to_repr());
  344. let h = hasher.finalize();
  345. let c = BigUint::from_str_radix(&h.to_hex(), 16).unwrap();
  346. // Add/Update this airdrop into the hashmap
  347. let mut map = self.challenge_map.lock().await;
  348. map.insert(pubkey.to_bytes(), (c.clone(), N_STEPS));
  349. drop(map);
  350. let chall = JsonValue::from(c.to_str_radix(16));
  351. let steps = JsonValue::from(N_STEPS as f64);
  352. JsonResponse::new(JsonValue::Array(vec![chall, steps]), id).into()
  353. }
  354. // RPCAPI:
  355. // Processes a native token airdrop request and airdrops requested amount to address.
  356. // Returns the transaction ID upon success.
  357. //
  358. // **Params:**
  359. // * `array[0]`: base58 encoded address string of the recipient
  360. // * `array[1]`: Amount to airdrop in form of f64
  361. // * `array[2]`: VDF evaluation witness as hex-encoded BigUint string
  362. //
  363. // **Returns:**
  364. // * hex-encoded transaction ID string
  365. //
  366. // --> {"jsonrpc": "2.0", "method": "airdrop", "params": ["1DarkFi...", 1.42, "0x123..."], "id": 1}
  367. // <-- {"jsonrpc": "2.0", "result": "txID", "id": 1}
  368. async fn airdrop(&self, id: u16, params: JsonValue) -> JsonResult {
  369. let params = params.get::<Vec<JsonValue>>().unwrap();
  370. if params.len() != 3 ||
  371. !params[0].is_string() ||
  372. !params[1].is_number() ||
  373. !params[2].is_string()
  374. {
  375. return JsonError::new(InvalidParams, None, id).into()
  376. }
  377. if !(*self.synced.lock().await) {
  378. error!(target: "faucetd", "airdrop(): Blockchain is not yet synced");
  379. return JsonError::new(InternalError, None, id).into()
  380. }
  381. // Decode public key
  382. let pubkey = params[0].get::<String>().unwrap();
  383. let pubkey = match PublicKey::from_str(pubkey) {
  384. Ok(v) => v,
  385. Err(e) => {
  386. error!(target: "faucetd", "airdrop(): Failed parsing PublicKey from String: {}", e);
  387. return server_error(RpcError::ParseError, id)
  388. }
  389. };
  390. // Decode requested airdrop amount
  391. let amount = params[1].get::<f64>().unwrap().to_string();
  392. let amount = match decode_base10(&amount, 8, true) {
  393. Ok(v) => v,
  394. Err(_) => {
  395. error!(target: "faucetd", "airdrop(): Failed parsing amount from string");
  396. return server_error(RpcError::ParseError, id)
  397. }
  398. };
  399. if amount > self.airdrop_limit {
  400. return server_error(RpcError::AmountExceedsLimit, id)
  401. }
  402. // Decode VDF witness
  403. let witness = params[2].get::<String>().unwrap();
  404. let Ok(witness) = BigUint::from_str_radix(witness, 16) else {
  405. error!(target: "faucetd", "airdrop(): Failed parsing VDF witness from string");
  406. return server_error(RpcError::ParseError, id)
  407. };
  408. // Check if there as a previous airdrop and the timeout has passed.
  409. let now = Utc::now().timestamp();
  410. let map = self.airdrop_map.lock().await;
  411. if let Some(last_airdrop) = map.get(&pubkey.to_bytes()) {
  412. if now - last_airdrop <= self.airdrop_timeout {
  413. error!(target: "faucetd", "airdrop(): Time limit reached for {}", pubkey);
  414. return server_error(RpcError::TimeLimitReached, id)
  415. }
  416. };
  417. drop(map);
  418. // Check if a VDF challenge exists
  419. let map = self.challenge_map.lock().await;
  420. let Some((challenge, n_steps)) = map.get(&pubkey.to_bytes()).cloned() else {
  421. error!(target: "faucetd", "airdrop(): No VDF challenge found for {}", pubkey);
  422. return server_error(RpcError::NoVdfChallenge, id)
  423. };
  424. drop(map);
  425. // Verify the VDF
  426. info!(target: "faucetd", "airdrop(): Verifying VDF for {}...", pubkey);
  427. if !mimc_vdf::verify(&challenge, n_steps, &witness) {
  428. error!(target: "faucetd", "airdrop(): VDF verification failed for {}", pubkey);
  429. return server_error(RpcError::VdfVerifyFailed, id)
  430. }
  431. // Remove the challenge from the map at this point. Latter stuff might
  432. // fail, but we want clients to be able to request things again.
  433. let mut mut_map = self.challenge_map.lock().await;
  434. mut_map.remove(&pubkey.to_bytes());
  435. drop(mut_map);
  436. let cid = *MONEY_CONTRACT_ID;
  437. let (mint_zkbin, mint_pk, burn_zkbin, burn_pk) = {
  438. let proving_keys_r = self.proving_keys.read().await;
  439. let Some(arr) = proving_keys_r.get(&cid.to_bytes()) else {
  440. error!(target: "faucetd", "Contract ID {} not found in proving keys hashmap", cid);
  441. return server_error(RpcError::InternalError, id)
  442. };
  443. let Some(mint_data) = arr.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_MINT_NS_V1) else {
  444. error!(target: "faucetd", "{} proof data not found in vector", MONEY_CONTRACT_ZKAS_MINT_NS_V1);
  445. return server_error(RpcError::InternalError, id)
  446. };
  447. let Some(burn_data) = arr.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_BURN_NS_V1) else {
  448. error!(target: "faucetd", "{} proof data not found in vector", MONEY_CONTRACT_ZKAS_BURN_NS_V1);
  449. return server_error(RpcError::InternalError, id)
  450. };
  451. (mint_data.2.clone(), mint_data.1.clone(), burn_data.2.clone(), burn_data.1.clone())
  452. };
  453. // Create money contract transfer params and proofs
  454. let builder = TransferCallBuilder {
  455. keypair: self.keypair,
  456. recipient: pubkey,
  457. value: amount,
  458. token_id: *DARK_TOKEN_ID,
  459. rcpt_spend_hook: pallas::Base::zero(),
  460. rcpt_user_data: pallas::Base::zero(),
  461. rcpt_user_data_blind: pallas::Base::random(&mut OsRng),
  462. change_spend_hook: pallas::Base::zero(),
  463. change_user_data: pallas::Base::zero(),
  464. change_user_data_blind: pallas::Base::random(&mut OsRng),
  465. coins: vec![],
  466. tree: self.merkle_tree.clone(),
  467. mint_zkbin,
  468. mint_pk,
  469. burn_zkbin,
  470. burn_pk,
  471. clear_input: true,
  472. };
  473. let debris = match builder.build() {
  474. Ok(v) => v,
  475. Err(e) => {
  476. error!(target: "faucetd", "Failed to build transfer tx params: {}", e);
  477. return server_error(RpcError::InternalError, id)
  478. }
  479. };
  480. // Build transaction
  481. let mut data = vec![MoneyTransfer as u8];
  482. debris.params.encode(&mut data).unwrap();
  483. let calls = vec![ContractCall { contract_id: cid, data }];
  484. let proofs = vec![debris.proofs];
  485. let mut tx = Transaction { calls, proofs, signatures: vec![] };
  486. let sigs = tx.create_sigs(&mut OsRng, &debris.signature_secrets).unwrap();
  487. tx.signatures = vec![sigs];
  488. // Safety check to see if the transaction is actually valid.
  489. let lock = self.validator_state.read().await;
  490. let current_slot = lock.consensus.time_keeper.current_slot();
  491. if let Err(e) = lock.verify_transactions(&[tx.clone()], current_slot, false).await {
  492. error!(target: "faucetd", "airdrop(): Failed to verify transaction before broadcasting: {}", e);
  493. return JsonError::new(InternalError, None, id).into()
  494. }
  495. // Broadcast transaction to the network.
  496. self.sync_p2p.broadcast(&tx).await;
  497. // Add/Update this airdrop into the hashmap
  498. let mut map = self.airdrop_map.lock().await;
  499. map.insert(pubkey.to_bytes(), now);
  500. drop(map);
  501. let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string();
  502. JsonResponse::new(JsonValue::String(tx_hash), id).into()
  503. }
  504. }
  505. async fn prune_airdrop_maps(
  506. rate_map: AirdropMap,
  507. challenge_map: ChallengeMap,
  508. timeout: i64,
  509. ) -> Result<()> {
  510. loop {
  511. sleep(timeout as u64).await;
  512. debug!(target: "faucetd", "Pruning airdrop maps");
  513. let now = Utc::now().timestamp();
  514. let mut prune = vec![];
  515. let im_map = rate_map.lock().await;
  516. for (k, v) in im_map.iter() {
  517. if now - *v > timeout {
  518. prune.push(*k);
  519. }
  520. }
  521. drop(im_map);
  522. let mut mut_rate_map = rate_map.lock().await;
  523. let mut mut_challenge_map = challenge_map.lock().await;
  524. for i in prune {
  525. mut_rate_map.remove(&i);
  526. mut_challenge_map.remove(&i);
  527. }
  528. drop(mut_rate_map);
  529. drop(mut_challenge_map);
  530. }
  531. }
  532. async_daemonize!(realmain);
  533. async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
  534. // Initialize or load wallet
  535. let wallet = WalletDb::new(Some(expand_path(&args.wallet_path)?), Some(&args.wallet_pass))?;
  536. // Initialize or open sled database
  537. let db_path =
  538. Path::new(expand_path(&args.database)?.to_str().unwrap()).join(args.chain.clone());
  539. let sled_db = sled::open(&db_path)?;
  540. // Initialize validator state
  541. let (bootstrap_ts, genesis_ts, genesis_data, initial_distribution) = match args.chain.as_str() {
  542. "mainnet" => (
  543. *MAINNET_BOOTSTRAP_TIMESTAMP,
  544. *MAINNET_GENESIS_TIMESTAMP,
  545. *MAINNET_GENESIS_HASH_BYTES,
  546. *MAINNET_INITIAL_DISTRIBUTION,
  547. ),
  548. "testnet" => (
  549. *TESTNET_BOOTSTRAP_TIMESTAMP,
  550. *TESTNET_GENESIS_TIMESTAMP,
  551. *TESTNET_GENESIS_HASH_BYTES,
  552. *TESTNET_INITIAL_DISTRIBUTION,
  553. ),
  554. x => {
  555. error!(target: "faucetd", "Unsupported chain `{}`", x);
  556. return Err(Error::UnsupportedChain)
  557. }
  558. };
  559. // Parse faucet addresses
  560. let mut faucet_pubkeys = vec![];
  561. for i in args.cashier_pub {
  562. let pk = PublicKey::from_str(&i)?;
  563. faucet_pubkeys.push(pk);
  564. }
  565. for i in args.faucet_pub {
  566. let pk = PublicKey::from_str(&i)?;
  567. faucet_pubkeys.push(pk);
  568. }
  569. // Initialize validator state
  570. let state = ValidatorState::new(
  571. &sled_db,
  572. bootstrap_ts,
  573. genesis_ts,
  574. genesis_data,
  575. initial_distribution,
  576. wallet.clone(),
  577. faucet_pubkeys,
  578. false,
  579. false,
  580. )
  581. .await?;
  582. // P2P network. The faucet doesn't participate in consensus, so we only
  583. // build the sync protocol.
  584. let network_settings = net::Settings {
  585. inbound_addrs: args.sync_p2p_accept,
  586. outbound_connections: args.sync_slots,
  587. external_addrs: args.sync_p2p_external,
  588. peers: args.sync_p2p_peer.clone(),
  589. seeds: args.sync_p2p_seed.clone(),
  590. allowed_transports: args.sync_p2p_transports,
  591. localnet: args.localnet,
  592. ..Default::default()
  593. };
  594. let sync_p2p = net::P2p::new(network_settings, ex.clone()).await;
  595. let registry = sync_p2p.protocol_registry();
  596. info!(target: "faucetd", "Registering block sync P2P protocols...");
  597. let _state = state.clone();
  598. registry
  599. .register(net::SESSION_ALL, move |channel, p2p| {
  600. let state = _state.clone();
  601. async move { ProtocolSync::init(channel, state, p2p, false).await.unwrap() }
  602. })
  603. .await;
  604. let _state = state.clone();
  605. registry
  606. .register(net::SESSION_ALL, move |channel, p2p| {
  607. let state = _state.clone();
  608. async move { ProtocolTx::init(channel, state, p2p).await.unwrap() }
  609. })
  610. .await;
  611. let airdrop_timeout = args.airdrop_timeout;
  612. let airdrop_limit = decode_base10(&args.airdrop_limit, 8, true)?;
  613. // Initialize program state
  614. let faucetd = Faucetd::new(
  615. state.clone(),
  616. sync_p2p.clone(),
  617. wallet.clone(),
  618. airdrop_timeout,
  619. airdrop_limit,
  620. )
  621. .await?;
  622. let faucetd = Arc::new(faucetd);
  623. // Task to periodically clean up the airdrop rate/challenge hashmaps
  624. let airdrop_task = StoppableTask::new();
  625. airdrop_task.clone().start(
  626. prune_airdrop_maps(
  627. faucetd.airdrop_map.clone(),
  628. faucetd.challenge_map.clone(),
  629. airdrop_timeout,
  630. ),
  631. |res| async {
  632. match res {
  633. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  634. Err(e) => error!(target: "faucetd", "Failed starting airdrop prune task: {}", e),
  635. }
  636. },
  637. Error::DetachedTaskStopped,
  638. ex.clone(),
  639. );
  640. // JSON-RPC server
  641. info!(target: "faucetd", "Starting JSON-RPC server");
  642. let rpc_task = StoppableTask::new();
  643. rpc_task.clone().start(
  644. listen_and_serve(args.rpc_listen, faucetd.clone(), ex.clone()),
  645. |res| async {
  646. match res {
  647. Ok(()) | Err(Error::RPCServerStopped) => { /* Do nothing */ }
  648. Err(e) => error!(target: "faucetd", "Failed starting JSON-RPC server: {}", e),
  649. }
  650. },
  651. Error::RPCServerStopped,
  652. ex.clone(),
  653. );
  654. info!(target: "faucetd", "Starting sync P2P network");
  655. sync_p2p.clone().start().await?;
  656. // TODO: I think this is not needed anymore
  657. //info!("Waiting for sync P2P outbound connections");
  658. //sync_p2p.clone().wait_for_outbound(ex).await?;
  659. match block_sync_task(sync_p2p.clone(), state.clone()).await {
  660. Ok(()) => *faucetd.synced.lock().await = true,
  661. Err(e) => error!(target: "faucetd", "Failed syncing blockchain: {}", e),
  662. }
  663. // Signal handling for graceful termination.
  664. let (signals_handler, signals_task) = SignalHandler::new(ex)?;
  665. signals_handler.wait_termination(signals_task).await?;
  666. info!(target: "faucetd", "Caught termination signal, cleaning up and exiting...");
  667. info!(target: "faucetd", "Stopping JSON-RPC server...");
  668. rpc_task.stop().await;
  669. info!(target: "faucetd", "Stopping airdrop prune task...");
  670. airdrop_task.stop().await;
  671. info!(target: "faucetd", "Stopping syncing P2P network...");
  672. sync_p2p.stop().await;
  673. info!(target: "faucetd", "Flushing database...");
  674. let flushed_bytes = sled_db.flush_async().await?;
  675. info!(target: "faucetd", "Flushed {} bytes", flushed_bytes);
  676. Ok(())
  677. }