239 lines
8.1 KiB
Rust
239 lines
8.1 KiB
Rust
use blake2b_simd::Params;
|
|
use blake2b_simd::State;
|
|
use byteorder::WriteBytesExt;
|
|
use byteorder::LE;
|
|
use ff::Field;
|
|
use hex_literal::hex;
|
|
use jubjub::Fr;
|
|
use orchard::circuit::ProvingKey;
|
|
|
|
use crate::ledger::builder::orchard_bundle::OrchardBuilder;
|
|
use crate::ledger::builder::sapling_bundle::SaplingBuilder;
|
|
use crate::ledger::builder::transparent_bundle::TransparentBuilder;
|
|
use crate::ledger::transport::*;
|
|
|
|
use crate::{CompactTxStreamerClient, Destination, RawTransaction, Source, TransactionPlan};
|
|
use anyhow::{anyhow, Result};
|
|
use rand::{RngCore, SeedableRng};
|
|
use rand_chacha::ChaChaRng;
|
|
use ripemd::{Digest, Ripemd160};
|
|
use secp256k1::PublicKey;
|
|
use sha2::Sha256;
|
|
use tonic::{transport::Channel, Request};
|
|
use zcash_client_backend::encoding::{
|
|
encode_extended_full_viewing_key, encode_transparent_address,
|
|
};
|
|
use zcash_primitives::consensus::Network;
|
|
use zcash_primitives::consensus::Parameters;
|
|
use zcash_primitives::legacy::TransparentAddress;
|
|
|
|
use zcash_primitives::zip32::ExtendedFullViewingKey;
|
|
|
|
use zcash_primitives::{
|
|
consensus::{BlockHeight, BranchId, MainNetwork},
|
|
transaction::{Authorized, TransactionData, TxVersion},
|
|
};
|
|
use zcash_proofs::prover::LocalTxProver;
|
|
|
|
mod orchard_bundle;
|
|
mod sapling_bundle;
|
|
mod transparent_bundle;
|
|
|
|
#[allow(dead_code)]
|
|
pub async fn show_public_keys() -> Result<()> {
|
|
let network = MainNetwork;
|
|
|
|
ledger_init().await?;
|
|
let pub_key = ledger_get_pubkey().await?;
|
|
let pub_key = PublicKey::from_slice(&pub_key)?;
|
|
let pub_key = pub_key.serialize();
|
|
let pub_key = Ripemd160::digest(&Sha256::digest(&pub_key));
|
|
let address = TransparentAddress::PublicKey(pub_key.into());
|
|
let address = encode_transparent_address(
|
|
&network.b58_pubkey_address_prefix(),
|
|
&network.b58_script_address_prefix(),
|
|
&address,
|
|
);
|
|
println!("address {}", address);
|
|
let dfvk = ledger_get_dfvk().await?;
|
|
let efvk = ExtendedFullViewingKey::from_diversifiable_full_viewing_key(&dfvk);
|
|
let efvk = encode_extended_full_viewing_key(
|
|
MainNetwork.hrp_sapling_extended_full_viewing_key(),
|
|
&efvk,
|
|
);
|
|
println!("efvk {}", efvk);
|
|
Ok(())
|
|
}
|
|
|
|
pub fn create_hasher(perso: &[u8]) -> State {
|
|
let h = Params::new().hash_length(32).personal(perso).to_state();
|
|
h
|
|
}
|
|
|
|
pub async fn build_broadcast_tx(
|
|
network: &Network,
|
|
client: &mut CompactTxStreamerClient<Channel>,
|
|
tx_plan: &TransactionPlan,
|
|
prover: &LocalTxProver,
|
|
) -> Result<String> {
|
|
ledger_init().await?;
|
|
let pubkey = ledger_get_pubkey().await?;
|
|
let mut transparent_builder = TransparentBuilder::new(network, &pubkey);
|
|
|
|
if transparent_builder.taddr != tx_plan.taddr {
|
|
anyhow::bail!("This ledger wallet has a different address {} != {}",
|
|
transparent_builder.taddr, tx_plan.taddr);
|
|
}
|
|
|
|
// Compute header digest
|
|
let mut h = create_hasher(b"ZTxIdHeadersHash");
|
|
h.update(&hex!("050000800a27a726b4d0d6c200000000"));
|
|
|
|
h.write_u32::<LE>(tx_plan.expiry_height)?;
|
|
let header_digest = h.finalize();
|
|
|
|
let master_seed = ledger_init_tx(header_digest.as_bytes()).await?;
|
|
|
|
// For testing only
|
|
// let esk = "secret-extended-key-main1qwy5cttzqqqqpq8ksfmzqgz90r73yevcw6mvwuv5zuddak9zgl9epp6x308pczzez3hse753heepdk886yf7dmse5qvyl5jsuk5w4ejhtm30cpa862kq0pfu0z4zxxvyd523zeta3rr6lj0vg30mshf6wrlfucg47jv3ldspe0sv464uewwlglr0dzakssj8tdx27vq3dnerfa5z5fgf8vjutlcey3lwn4m6ncg8y4n2cgl64rd768uqg0yfvshljqt3g4x83kngv4guq06xx";
|
|
// let extsk = decode_extended_spending_key(MainNetwork.hrp_sapling_extended_spending_key(), &esk)
|
|
// .unwrap();
|
|
// let ovk = extsk.expsk.ovk;
|
|
// let proofgen_key = extsk.expsk.proof_generation_key();
|
|
// let dfvk = extsk.to_diversifiable_full_viewing_key();
|
|
|
|
let dfvk: zcash_primitives::zip32::DiversifiableFullViewingKey = ledger_get_dfvk().await?;
|
|
let proofgen_key: zcash_primitives::sapling::ProofGenerationKey =
|
|
ledger_get_proofgen_key().await?;
|
|
|
|
let mut sapling_builder = SaplingBuilder::new(prover, dfvk, proofgen_key);
|
|
|
|
let orchard_fvk: [u8; 96] = hex::decode(&tx_plan.orchard_fvk)
|
|
.unwrap()
|
|
.try_into()
|
|
.unwrap();
|
|
let orchard_fvk = orchard::keys::FullViewingKey::from_bytes(&orchard_fvk).unwrap();
|
|
let anchor = orchard::Anchor::from_bytes(tx_plan.orchard_anchor).unwrap();
|
|
|
|
let mut orchard_builder = OrchardBuilder::new(&orchard_fvk, anchor);
|
|
|
|
let o_fvk: [u8; 96] = ledger_get_o_fvk().await?.try_into().unwrap();
|
|
let _o_fvk =
|
|
orchard::keys::FullViewingKey::from_bytes(&o_fvk).ok_or(anyhow!("Invalid Orchard FVK"))?;
|
|
|
|
// Derive rseed PRNG
|
|
let mut h = Params::new()
|
|
.hash_length(32)
|
|
.personal(b"ZRSeedPRNG__Hash")
|
|
.to_state();
|
|
h.update(&master_seed);
|
|
let main_rseed = h.finalize();
|
|
let mut rseed_rng = ChaChaRng::from_seed(main_rseed.as_bytes().try_into().unwrap());
|
|
|
|
// Derive alpha PRNG
|
|
let mut h = Params::new()
|
|
.hash_length(32)
|
|
.personal(b"ZAlphaPRNG__Hash")
|
|
.to_state();
|
|
h.update(&master_seed);
|
|
let alpha = h.finalize();
|
|
let mut alpha_rng = ChaChaRng::from_seed(alpha.as_bytes().try_into().unwrap());
|
|
|
|
for sp in tx_plan.spends.iter() {
|
|
match sp.source {
|
|
Source::Transparent { txid, index } => {
|
|
transparent_builder
|
|
.add_input(txid, index, sp.amount)
|
|
.await?;
|
|
}
|
|
Source::Sapling {
|
|
diversifier,
|
|
rseed,
|
|
ref witness,
|
|
..
|
|
} => {
|
|
let alpha = Fr::random(&mut alpha_rng);
|
|
println!("ALPHA {}", hex::encode(&alpha.to_bytes()));
|
|
|
|
sapling_builder
|
|
.add_spend(alpha, diversifier, rseed, witness, sp.amount)
|
|
.await?;
|
|
}
|
|
Source::Orchard { diversifier, rseed, rho, ref witness, .. } => {
|
|
orchard_builder.add_spend(diversifier, rseed, rho, &witness, sp.amount)?;
|
|
}
|
|
}
|
|
}
|
|
ledger_set_stage(2).await?;
|
|
|
|
for output in tx_plan.outputs.iter() {
|
|
if let Destination::Transparent(raw_address) = output.destination {
|
|
transparent_builder
|
|
.add_output(raw_address, output.amount)
|
|
.await?;
|
|
}
|
|
}
|
|
transparent_builder.set_merkle_proof().await?;
|
|
ledger_set_stage(3).await?;
|
|
|
|
for output in tx_plan.outputs.iter() {
|
|
match output.destination {
|
|
Destination::Sapling(raw_address) => {
|
|
let mut rseed = [0u8; 32];
|
|
rseed_rng.fill_bytes(&mut rseed);
|
|
sapling_builder
|
|
.add_output(rseed, raw_address, &output.memo, output.amount)
|
|
.await?;
|
|
}
|
|
Destination::Orchard(raw_address) => {
|
|
orchard_builder.add_output(raw_address, output.amount, &output.memo)?;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
sapling_builder.set_merkle_proof(tx_plan.net_chg[0]).await?;
|
|
ledger_set_stage(4).await?;
|
|
|
|
let pk = ProvingKey::build();
|
|
orchard_builder.prepare(tx_plan.net_chg[1], &pk, &mut alpha_rng, &mut rseed_rng).await?;
|
|
|
|
ledger_set_stage(5).await?;
|
|
ledger_confirm_fee().await?;
|
|
|
|
transparent_builder.sign().await?;
|
|
sapling_builder.sign().await?;
|
|
orchard_builder.sign().await?;
|
|
|
|
let transparent_bundle = transparent_builder.build();
|
|
let sapling_bundle = sapling_builder.build().await?;
|
|
let orchard_bundle = orchard_builder.build()?;
|
|
|
|
let authed_tx: TransactionData<Authorized> = TransactionData {
|
|
version: TxVersion::Zip225,
|
|
consensus_branch_id: BranchId::Nu5,
|
|
lock_time: 0,
|
|
expiry_height: BlockHeight::from_u32(tx_plan.expiry_height),
|
|
transparent_bundle,
|
|
sprout_bundle: None,
|
|
sapling_bundle,
|
|
orchard_bundle,
|
|
};
|
|
|
|
let tx = authed_tx.freeze().unwrap();
|
|
let mut raw_tx = vec![];
|
|
tx.write_v5(&mut raw_tx)?;
|
|
|
|
ledger_end_tx().await?;
|
|
|
|
let response = client
|
|
.send_transaction(Request::new(RawTransaction {
|
|
data: raw_tx,
|
|
height: 0,
|
|
}))
|
|
.await?
|
|
.into_inner();
|
|
println!("{}", response.error_message);
|
|
|
|
Ok(response.error_message)
|
|
}
|