diff --git a/script/research/gg/Cargo.toml b/script/research/gg/Cargo.toml index 66e5f64a9..62170214a 100644 --- a/script/research/gg/Cargo.toml +++ b/script/research/gg/Cargo.toml @@ -15,12 +15,14 @@ edition = "2021" [dependencies] # Darkfi darkfi = {path = "../../../", features = ["validator"]} +darkfi_money_contract = {path = "../../../src/contract/money", features = ["no-entrypoint", "client"]} darkfi-contract-test-harness = {path = "../../../src/contract/test-harness"} +darkfi-sdk = {path = "../../../src/sdk"} darkfi-serial = {path = "../../../src/serial"} # Misc -anyhow = "1.0.80" async-std = {version = "1.12.0", features = ["attributes"]} +bs58 = "0.5.0" clap = {version = "4.5.2", features = ["derive"]} sled = "0.34.7" diff --git a/script/research/gg/src/main.rs b/script/research/gg/src/main.rs index 7d8331958..e1508528b 100644 --- a/script/research/gg/src/main.rs +++ b/script/research/gg/src/main.rs @@ -18,20 +18,32 @@ use std::{ fs::{read_dir, read_to_string}, - io::{stdin, Read}, + io::{stdin, Cursor, Read}, + process::exit, + str::FromStr, }; -use anyhow::Result; use clap::{Parser, Subcommand}; use darkfi::{ blockchain::{BlockInfo, Blockchain, BlockchainOverlay}, cli_desc, - tx::Transaction, - util::{encoding::base64, path::expand_path, time::Timestamp}, - validator::verification::verify_genesis_block, + tx::{ContractCallLeaf, Transaction, TransactionBuilder}, + util::{encoding::base64, parse::decode_base10, path::expand_path, time::Timestamp}, + validator::{utils::deploy_native_contracts, verification::verify_genesis_block}, + zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit}, + zkas::ZkBinary, + Result, }; use darkfi_contract_test_harness::vks; -use darkfi_serial::{deserialize, serialize}; +use darkfi_money_contract::{ + client::genesis_mint_v1::GenesisMintCallBuilder, MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1, +}; +use darkfi_sdk::{ + crypto::{contract_id::MONEY_CONTRACT_ID, FuncId, Keypair, SecretKey}, + pasta::pallas, + ContractCall, +}; +use darkfi_serial::{deserialize, serialize, AsyncEncodable}; #[derive(Parser)] #[command(about = cli_desc!())] @@ -58,11 +70,18 @@ enum Subcmd { /// Read a Darkfi genesis block from stdin and verify it Verify, + + /// Generate a Darkfi genesis transaction using the secret + /// key from stdin + GenerateTx { + /// Amount to mint for this genesis transaction + amount: String, + }, } /// Auxiliary function to read a bs58 genesis block from stdin fn read_block() -> Result { - eprintln!("Reading genesis block from stdin..."); + println!("Reading genesis block from stdin..."); let mut buf = String::new(); stdin().read_to_string(&mut buf)?; let bytes = base64::decode(buf.trim()).unwrap(); @@ -80,7 +99,6 @@ async fn main() -> Result<()> { Subcmd::Display => { let genesis_block = read_block()?; println!("{genesis_block:#?}"); - Ok(()) } Subcmd::Generate { txs_folder, genesis_timestamp } => { @@ -88,7 +106,12 @@ async fn main() -> Result<()> { let txs_folder = expand_path(&txs_folder).unwrap(); let mut genesis_txs: Vec = vec![]; for file in read_dir(txs_folder)? { - let bytes = base64::decode(read_to_string(file?.path())?.trim()).unwrap(); + let file = file?; + let Ok(bytes) = bs58::decode(&read_to_string(file.path())?.trim()).into_vec() + else { + eprintln!("Error: Failed to decode transaction: {:?}", file.path()); + exit(2); + }; let tx = deserialize(&bytes)?; genesis_txs.push(tx); } @@ -98,7 +121,7 @@ async fn main() -> Result<()> { // Update timestamp if one was provided if let Some(timestamp) = genesis_timestamp { - genesis_block.header.timestamp = Timestamp(timestamp); + genesis_block.header.timestamp = Timestamp::from_u64(timestamp); } // Retrieve genesis producer transaction @@ -113,8 +136,6 @@ async fn main() -> Result<()> { // Write generated genesis block to stdin let encoded = base64::encode(&serialize(&genesis_block)); println!("{encoded}"); - - Ok(()) } Subcmd::Verify => { @@ -131,12 +152,72 @@ async fn main() -> Result<()> { // Create an overlay over whole blockchain let blockchain = Blockchain::new(&sled_db)?; let overlay = BlockchainOverlay::new(&blockchain)?; + deploy_native_contracts(&overlay).await?; verify_genesis_block(&overlay, &genesis_block).await?; println!("Genesis block {hash} verified successfully!"); + } - Ok(()) + Subcmd::GenerateTx { amount } => { + let mut buf = String::new(); + stdin().read_to_string(&mut buf)?; + let Ok(bytes) = bs58::decode(&buf.trim()).into_vec() else { + eprintln!("Error: Failed to decode stdin buffer"); + exit(2); + }; + let secret = deserialize::(&bytes)?; + let keypair = Keypair::new(secret); + + if let Err(e) = f64::from_str(&amount) { + eprintln!("Invalid amount: {e:?}"); + exit(2); + } + let amount = decode_base10(&amount, 8, false)?; + + // 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 { + keypair, + amount, + spend_hook: FuncId::none(), + user_data: pallas::Base::ZERO, + 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(&[keypair.secret])?; + tx.signatures = vec![sigs]; + + println!("{}", bs58::encode(&serialize(&tx)).into_string()); } } + + Ok(()) }