|
|
@@ -21,6 +21,7 @@ use std::{
|
|
|
io::{stdin, Cursor, Read},
|
|
|
process::exit,
|
|
|
str::FromStr,
|
|
|
+ sync::Arc,
|
|
|
};
|
|
|
|
|
|
use clap::{Parser, Subcommand};
|
|
|
@@ -45,6 +46,7 @@ use darkfi_sdk::{
|
|
|
};
|
|
|
use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
|
|
|
use sled_overlay::sled;
|
|
|
+use smol::Executor;
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
#[command(about = cli_desc!())]
|
|
|
@@ -102,174 +104,179 @@ async fn read_block() -> Result<BlockInfo> {
|
|
|
Ok(block)
|
|
|
}
|
|
|
|
|
|
-#[async_std::main]
|
|
|
-async fn main() -> Result<()> {
|
|
|
- // Parse arguments
|
|
|
- let args = Args::parse();
|
|
|
-
|
|
|
- // Execute a subcommand
|
|
|
- match args.command {
|
|
|
- Subcmd::Display => {
|
|
|
- let genesis_block = read_block().await;
|
|
|
- // TODO: display in more details
|
|
|
- println!("{genesis_block:?}");
|
|
|
- }
|
|
|
-
|
|
|
- Subcmd::Generate { txs_folder, genesis_timestamp } => {
|
|
|
- // Grab genesis transactions from folder
|
|
|
- let txs_folder = expand_path(&txs_folder).unwrap();
|
|
|
- let mut genesis_txs: Vec<Transaction> = vec![];
|
|
|
- for file in read_dir(txs_folder)? {
|
|
|
- let file = file?;
|
|
|
- let bytes = base64::decode(read_to_string(file.path())?.trim()).unwrap();
|
|
|
- let tx = deserialize_async(&bytes).await?;
|
|
|
- genesis_txs.push(tx);
|
|
|
+fn main() -> Result<()> {
|
|
|
+ // Initialize an executor
|
|
|
+ let executor = Arc::new(Executor::new());
|
|
|
+ smol::block_on(executor.run(async {
|
|
|
+ // Parse arguments
|
|
|
+ let args = Args::parse();
|
|
|
+
|
|
|
+ // Execute a subcommand
|
|
|
+ match args.command {
|
|
|
+ Subcmd::Display => {
|
|
|
+ let genesis_block = read_block().await;
|
|
|
+ // TODO: display in more details
|
|
|
+ println!("{genesis_block:?}");
|
|
|
}
|
|
|
|
|
|
- // Generate the genesis block
|
|
|
- let mut genesis_block = BlockInfo::default();
|
|
|
-
|
|
|
- // Update timestamp if one was provided
|
|
|
- if let Some(timestamp) = genesis_timestamp {
|
|
|
- genesis_block.header.timestamp = Timestamp::from_u64(timestamp);
|
|
|
- }
|
|
|
+ Subcmd::Generate { txs_folder, genesis_timestamp } => {
|
|
|
+ // Grab genesis transactions from folder
|
|
|
+ let txs_folder = expand_path(&txs_folder).unwrap();
|
|
|
+ let mut genesis_txs: Vec<Transaction> = vec![];
|
|
|
+ for file in read_dir(txs_folder)? {
|
|
|
+ let file = file?;
|
|
|
+ let bytes = base64::decode(read_to_string(file.path())?.trim()).unwrap();
|
|
|
+ let tx = deserialize_async(&bytes).await?;
|
|
|
+ genesis_txs.push(tx);
|
|
|
+ }
|
|
|
|
|
|
- // Retrieve genesis producer transaction
|
|
|
- let producer_tx = genesis_block.txs.pop().unwrap();
|
|
|
+ // Generate the genesis block
|
|
|
+ let mut genesis_block = BlockInfo::default();
|
|
|
|
|
|
- // Append genesis transactions
|
|
|
- if !genesis_txs.is_empty() {
|
|
|
- genesis_block.append_txs(genesis_txs);
|
|
|
- }
|
|
|
- genesis_block.append_txs(vec![producer_tx]);
|
|
|
+ // Update timestamp if one was provided
|
|
|
+ if let Some(timestamp) = genesis_timestamp {
|
|
|
+ genesis_block.header.timestamp = Timestamp::from_u64(timestamp);
|
|
|
+ }
|
|
|
|
|
|
- // Write generated genesis block to stdin
|
|
|
- let encoded = base64::encode(&serialize_async(&genesis_block).await);
|
|
|
- println!("{encoded}");
|
|
|
- }
|
|
|
+ // Retrieve genesis producer transaction
|
|
|
+ let producer_tx = genesis_block.txs.pop().unwrap();
|
|
|
|
|
|
- Subcmd::Verify => {
|
|
|
- let genesis_block = read_block().await?;
|
|
|
- let hash = genesis_block.hash();
|
|
|
+ // Append genesis transactions
|
|
|
+ if !genesis_txs.is_empty() {
|
|
|
+ genesis_block.append_txs(genesis_txs);
|
|
|
+ }
|
|
|
+ genesis_block.append_txs(vec![producer_tx]);
|
|
|
|
|
|
- println!("Verifying genesis block: {hash}");
|
|
|
+ // Write generated genesis block to stdin
|
|
|
+ let encoded = base64::encode(&serialize_async(&genesis_block).await);
|
|
|
+ println!("{encoded}");
|
|
|
+ }
|
|
|
|
|
|
- // Initialize a temporary sled database
|
|
|
- let sled_db = sled::Config::new().temporary(true).open()?;
|
|
|
- let (_, vks) = vks::get_cached_pks_and_vks()?;
|
|
|
- vks::inject(&sled_db, &vks)?;
|
|
|
+ Subcmd::Verify => {
|
|
|
+ let genesis_block = read_block().await?;
|
|
|
+ let hash = genesis_block.hash();
|
|
|
|
|
|
- // Create an overlay over whole blockchain
|
|
|
- let blockchain = Blockchain::new(&sled_db)?;
|
|
|
- let overlay = BlockchainOverlay::new(&blockchain)?;
|
|
|
- deploy_native_contracts(&overlay, 0).await?;
|
|
|
+ println!("Verifying genesis block: {hash}");
|
|
|
|
|
|
- verify_genesis_block(&overlay, &genesis_block, 0).await?;
|
|
|
+ // Initialize a temporary sled database
|
|
|
+ let sled_db = sled::Config::new().temporary(true).open()?;
|
|
|
+ let (_, vks) = vks::get_cached_pks_and_vks()?;
|
|
|
+ vks::inject(&sled_db, &vks)?;
|
|
|
|
|
|
- println!("Genesis block {hash} verified successfully!");
|
|
|
- }
|
|
|
+ // Create an overlay over whole blockchain
|
|
|
+ let blockchain = Blockchain::new(&sled_db)?;
|
|
|
+ let overlay = BlockchainOverlay::new(&blockchain)?;
|
|
|
+ deploy_native_contracts(&overlay, 0).await?;
|
|
|
|
|
|
- Subcmd::GenerateTx { amounts, recipient, spend_hook, user_data } => {
|
|
|
- let mut buf = String::new();
|
|
|
- stdin().read_to_string(&mut buf)?;
|
|
|
- let signature_secret = SecretKey::from_str(buf.trim())?;
|
|
|
+ verify_genesis_block(&overlay, &genesis_block, 0).await?;
|
|
|
|
|
|
- let mut coin_amounts = vec![];
|
|
|
- for amount in amounts {
|
|
|
- if let Err(e) = f64::from_str(&amount) {
|
|
|
- eprintln!("Invalid amount: {e:?}");
|
|
|
- exit(2);
|
|
|
- }
|
|
|
- coin_amounts.push(decode_base10(&amount, 8, true)?);
|
|
|
+ println!("Genesis block {hash} verified successfully!");
|
|
|
}
|
|
|
|
|
|
- let recipient = match recipient {
|
|
|
- Some(r) => match PublicKey::from_str(&r) {
|
|
|
- Ok(r) => Some(r),
|
|
|
- Err(e) => {
|
|
|
- eprintln!("Invalid recipient: {e:?}");
|
|
|
- exit(2);
|
|
|
- }
|
|
|
- },
|
|
|
- None => None,
|
|
|
- };
|
|
|
-
|
|
|
- let spend_hook = match spend_hook {
|
|
|
- Some(s) => match FuncId::from_str(&s) {
|
|
|
- Ok(s) => Some(s),
|
|
|
- Err(e) => {
|
|
|
- eprintln!("Invalid spend hook: {e:?}");
|
|
|
+ Subcmd::GenerateTx { amounts, recipient, spend_hook, user_data } => {
|
|
|
+ let mut buf = String::new();
|
|
|
+ stdin().read_to_string(&mut buf)?;
|
|
|
+ let signature_secret = SecretKey::from_str(buf.trim())?;
|
|
|
+
|
|
|
+ let mut coin_amounts = vec![];
|
|
|
+ for amount in amounts {
|
|
|
+ if let Err(e) = f64::from_str(&amount) {
|
|
|
+ eprintln!("Invalid amount: {e:?}");
|
|
|
exit(2);
|
|
|
}
|
|
|
- },
|
|
|
- None => None,
|
|
|
- };
|
|
|
-
|
|
|
- let user_data = match user_data {
|
|
|
- Some(u) => {
|
|
|
- let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
|
|
|
- Ok(b) => b,
|
|
|
+ coin_amounts.push(decode_base10(&amount, 8, true)?);
|
|
|
+ }
|
|
|
+
|
|
|
+ let recipient = match recipient {
|
|
|
+ Some(r) => match PublicKey::from_str(&r) {
|
|
|
+ Ok(r) => Some(r),
|
|
|
Err(e) => {
|
|
|
- eprintln!("Invalid user data: {e:?}");
|
|
|
+ eprintln!("Invalid recipient: {e:?}");
|
|
|
exit(2);
|
|
|
}
|
|
|
- };
|
|
|
+ },
|
|
|
+ None => None,
|
|
|
+ };
|
|
|
|
|
|
- match pallas::Base::from_repr(bytes).into() {
|
|
|
- Some(v) => Some(v),
|
|
|
- None => {
|
|
|
- eprintln!("Invalid user data");
|
|
|
+ let spend_hook = match spend_hook {
|
|
|
+ Some(s) => match FuncId::from_str(&s) {
|
|
|
+ Ok(s) => Some(s),
|
|
|
+ Err(e) => {
|
|
|
+ eprintln!("Invalid spend hook: {e:?}");
|
|
|
exit(2);
|
|
|
}
|
|
|
+ },
|
|
|
+ None => None,
|
|
|
+ };
|
|
|
+
|
|
|
+ let user_data = match user_data {
|
|
|
+ Some(u) => {
|
|
|
+ let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
|
|
|
+ Ok(b) => b,
|
|
|
+ Err(e) => {
|
|
|
+ eprintln!("Invalid user data: {e:?}");
|
|
|
+ exit(2);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ match pallas::Base::from_repr(bytes).into() {
|
|
|
+ Some(v) => Some(v),
|
|
|
+ None => {
|
|
|
+ eprintln!("Invalid user data");
|
|
|
+ exit(2);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ None => None,
|
|
|
+ };
|
|
|
+
|
|
|
+ // Grab mint proving keys and zkbin
|
|
|
+ let (pks, _) = vks::get_cached_pks_and_vks()?;
|
|
|
+ let mut mint = None;
|
|
|
+ for (bincode, namespace, pk) in pks {
|
|
|
+ if namespace.as_str() != MONEY_CONTRACT_ZKAS_MINT_NS_V1 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ let mut reader = Cursor::new(pk);
|
|
|
+ let zkbin = ZkBinary::decode(&bincode)?;
|
|
|
+ let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
|
|
|
+ let proving_key = ProvingKey::read(&mut reader, circuit)?;
|
|
|
+ mint = Some((proving_key, zkbin));
|
|
|
}
|
|
|
- None => None,
|
|
|
- };
|
|
|
-
|
|
|
- // Grab mint proving keys and zkbin
|
|
|
- let (pks, _) = vks::get_cached_pks_and_vks()?;
|
|
|
- let mut mint = None;
|
|
|
- for (bincode, namespace, pk) in pks {
|
|
|
- if namespace.as_str() != MONEY_CONTRACT_ZKAS_MINT_NS_V1 {
|
|
|
- continue
|
|
|
- }
|
|
|
- let mut reader = Cursor::new(pk);
|
|
|
- let zkbin = ZkBinary::decode(&bincode)?;
|
|
|
- let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
|
|
|
- let proving_key = ProvingKey::read(&mut reader, circuit)?;
|
|
|
- mint = Some((proving_key, zkbin));
|
|
|
+ let Some((mint_pk, mint_zkbin)) = mint else {
|
|
|
+ eprintln!("Mint proving keys not found.");
|
|
|
+ exit(2);
|
|
|
+ };
|
|
|
+
|
|
|
+ // Build the contract call
|
|
|
+ let builder = GenesisMintCallBuilder {
|
|
|
+ signature_public: PublicKey::from_secret(signature_secret),
|
|
|
+ amounts: coin_amounts,
|
|
|
+ recipient,
|
|
|
+ spend_hook,
|
|
|
+ user_data,
|
|
|
+ mint_zkbin,
|
|
|
+ mint_pk,
|
|
|
+ };
|
|
|
+
|
|
|
+ let debris = builder.build()?;
|
|
|
+
|
|
|
+ // Encode and build the transaction
|
|
|
+ let mut data = vec![MoneyFunction::GenesisMintV1 as u8];
|
|
|
+ debris.params.encode_async(&mut data).await?;
|
|
|
+ let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
|
|
+ let mut tx_builder = TransactionBuilder::new(
|
|
|
+ ContractCallLeaf { call, proofs: debris.proofs },
|
|
|
+ vec![],
|
|
|
+ )?;
|
|
|
+ let mut tx = tx_builder.build()?;
|
|
|
+ let sigs = tx.create_sigs(&[signature_secret])?;
|
|
|
+ tx.signatures = vec![sigs];
|
|
|
+
|
|
|
+ println!("{}", base64::encode(&serialize_async(&tx).await));
|
|
|
}
|
|
|
- let Some((mint_pk, mint_zkbin)) = mint else {
|
|
|
- eprintln!("Mint proving keys not found.");
|
|
|
- exit(2);
|
|
|
- };
|
|
|
-
|
|
|
- // Build the contract call
|
|
|
- let builder = GenesisMintCallBuilder {
|
|
|
- signature_public: PublicKey::from_secret(signature_secret),
|
|
|
- amounts: coin_amounts,
|
|
|
- recipient,
|
|
|
- spend_hook,
|
|
|
- user_data,
|
|
|
- mint_zkbin,
|
|
|
- mint_pk,
|
|
|
- };
|
|
|
-
|
|
|
- let debris = builder.build()?;
|
|
|
-
|
|
|
- // Encode and build the transaction
|
|
|
- let mut data = vec![MoneyFunction::GenesisMintV1 as u8];
|
|
|
- debris.params.encode_async(&mut data).await?;
|
|
|
- let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
|
|
- let mut tx_builder =
|
|
|
- TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
|
|
|
- let mut tx = tx_builder.build()?;
|
|
|
- let sigs = tx.create_sigs(&[signature_secret])?;
|
|
|
- tx.signatures = vec![sigs];
|
|
|
-
|
|
|
- println!("{}", base64::encode(&serialize_async(&tx).await));
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- Ok(())
|
|
|
+ Ok(())
|
|
|
+ }))
|
|
|
}
|