main.rs 29 KB

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