diff --git a/script/research/gg/.gitignore b/script/research/gg/.gitignore index af4873523..a3c9ee1d3 100644 --- a/script/research/gg/.gitignore +++ b/script/research/gg/.gitignore @@ -1,4 +1,5 @@ /target Cargo.lock rustfmt.toml +gg /genesis_txs diff --git a/script/research/gg/Cargo.toml b/script/research/gg/Cargo.toml index c3ad37c8d..34076632f 100644 --- a/script/research/gg/Cargo.toml +++ b/script/research/gg/Cargo.toml @@ -21,10 +21,10 @@ darkfi-sdk = {path = "../../../src/sdk"} darkfi-serial = "0.4.2" # Misc -async-std = {version = "1.13.0", features = ["attributes"]} bs58 = "0.5.1" clap = {version = "4.4.11", features = ["derive"]} sled-overlay = "0.1.6" +smol = "2.0.2" [patch.crates-io] halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v4"} diff --git a/script/research/gg/Makefile b/script/research/gg/Makefile new file mode 100644 index 000000000..7635c6669 --- /dev/null +++ b/script/research/gg/Makefile @@ -0,0 +1,37 @@ +.POSIX: + +# Install prefix +PREFIX = $(HOME)/.cargo + +# Cargo binary +CARGO = cargo +nightly + +# Compile target +RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2) +# Uncomment when doing musl static builds +#RUSTFLAGS = -C target-feature=+crt-static -C link-self-contained=yes + +SRC = \ + Cargo.toml \ + $(shell find src -type f -name '*.rs') \ + +BIN = $(shell grep '^name = ' Cargo.toml | cut -d' ' -f3 | tr -d '"') + +all: $(BIN) + +$(BIN): $(SRC) + RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package $@ + cp -f target/$(RUST_TARGET)/release/$@ $@ + +fmt: + $(CARGO) fmt --all + +clippy: + RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clippy --target=$(RUST_TARGET) \ + --release --all-features --workspace --tests + +clean: + RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clean --target=$(RUST_TARGET) --release --package $(BIN) + rm -f $(BIN) + +.PHONY: all fmt clippy clean diff --git a/script/research/gg/src/main.rs b/script/research/gg/src/main.rs index 58f56e5b1..072108cdc 100644 --- a/script/research/gg/src/main.rs +++ b/script/research/gg/src/main.rs @@ -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 { Ok(block) } -#[async_std::main] -async fn main() -> Result<()> { - // Parse arguments - let args = Args::parse(); +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:?}"); - } - - Subcmd::Generate { txs_folder, genesis_timestamp } => { - // Grab genesis transactions from folder - let txs_folder = expand_path(&txs_folder).unwrap(); - let mut genesis_txs: Vec = 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); + // 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); - } - - // Retrieve genesis producer transaction - let producer_tx = genesis_block.txs.pop().unwrap(); - - // Append genesis transactions - if !genesis_txs.is_empty() { - genesis_block.append_txs(genesis_txs); - } - genesis_block.append_txs(vec![producer_tx]); - - // Write generated genesis block to stdin - let encoded = base64::encode(&serialize_async(&genesis_block).await); - println!("{encoded}"); - } - - Subcmd::Verify => { - let genesis_block = read_block().await?; - let hash = genesis_block.hash(); - - println!("Verifying genesis block: {hash}"); - - // 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)?; - - // Create an overlay over whole blockchain - let blockchain = Blockchain::new(&sled_db)?; - let overlay = BlockchainOverlay::new(&blockchain)?; - deploy_native_contracts(&overlay, 0).await?; - - verify_genesis_block(&overlay, &genesis_block, 0).await?; - - println!("Genesis block {hash} verified successfully!"); - } - - 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); + Subcmd::Generate { txs_folder, genesis_timestamp } => { + // Grab genesis transactions from folder + let txs_folder = expand_path(&txs_folder).unwrap(); + let mut genesis_txs: Vec = 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); } - coin_amounts.push(decode_base10(&amount, 8, true)?); + + // 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); + } + + // Retrieve genesis producer transaction + let producer_tx = genesis_block.txs.pop().unwrap(); + + // Append genesis transactions + if !genesis_txs.is_empty() { + genesis_block.append_txs(genesis_txs); + } + genesis_block.append_txs(vec![producer_tx]); + + // Write generated genesis block to stdin + let encoded = base64::encode(&serialize_async(&genesis_block).await); + println!("{encoded}"); } - let recipient = match recipient { - Some(r) => match PublicKey::from_str(&r) { - Ok(r) => Some(r), - Err(e) => { - eprintln!("Invalid recipient: {e:?}"); + Subcmd::Verify => { + let genesis_block = read_block().await?; + let hash = genesis_block.hash(); + + println!("Verifying genesis block: {hash}"); + + // 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)?; + + // Create an overlay over whole blockchain + let blockchain = Blockchain::new(&sled_db)?; + let overlay = BlockchainOverlay::new(&blockchain)?; + deploy_native_contracts(&overlay, 0).await?; + + verify_genesis_block(&overlay, &genesis_block, 0).await?; + + println!("Genesis block {hash} verified successfully!"); + } + + 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, - }; + coin_amounts.push(decode_base10(&amount, 8, true)?); + } - 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, + 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, - }; + 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 + // 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 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(()) + })) }