parent
48f0a2ead4
commit
3d94b41c25
2
build.rs
2
build.rs
|
@ -7,7 +7,7 @@ fn main() {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
create_c_bindings();
|
// create_c_bindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
13946
src/db/data_generated.rs
13946
src/db/data_generated.rs
File diff suppressed because it is too large
Load Diff
|
@ -2,18 +2,19 @@ use blake2b_simd::Params;
|
||||||
use blake2b_simd::State;
|
use blake2b_simd::State;
|
||||||
use byteorder::WriteBytesExt;
|
use byteorder::WriteBytesExt;
|
||||||
use byteorder::LE;
|
use byteorder::LE;
|
||||||
use ff::{Field, PrimeField};
|
use ff::Field;
|
||||||
use group::GroupEncoding;
|
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use jubjub::{Fq, Fr};
|
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::sapling_bundle::SaplingBuilder;
|
||||||
use crate::ledger::builder::transparent_bundle::TransparentBuilder;
|
use crate::ledger::builder::transparent_bundle::TransparentBuilder;
|
||||||
use crate::ledger::transport::*;
|
use crate::ledger::transport::*;
|
||||||
|
|
||||||
use crate::{CompactTxStreamerClient, Destination, RawTransaction, Source, TransactionPlan};
|
use crate::{CompactTxStreamerClient, Destination, RawTransaction, Source, TransactionPlan};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rand::{rngs::OsRng, RngCore, SeedableRng};
|
use rand::{RngCore, SeedableRng};
|
||||||
use rand_chacha::ChaChaRng;
|
use rand_chacha::ChaChaRng;
|
||||||
use ripemd::{Digest, Ripemd160};
|
use ripemd::{Digest, Ripemd160};
|
||||||
use secp256k1::PublicKey;
|
use secp256k1::PublicKey;
|
||||||
|
@ -30,24 +31,9 @@ use zcash_primitives::zip32::ExtendedFullViewingKey;
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::{BlockHeight, BranchId, MainNetwork},
|
consensus::{BlockHeight, BranchId, MainNetwork},
|
||||||
constants::PROOF_GENERATION_KEY_GENERATOR,
|
transaction::{Authorized, TransactionData, TxVersion},
|
||||||
merkle_tree::IncrementalWitness,
|
|
||||||
sapling::{
|
|
||||||
note_encryption::sapling_note_encryption,
|
|
||||||
prover::TxProver,
|
|
||||||
redjubjub::Signature,
|
|
||||||
value::{NoteValue, ValueCommitment, ValueSum},
|
|
||||||
Diversifier, Node, Note, Nullifier, PaymentAddress, Rseed,
|
|
||||||
},
|
|
||||||
transaction::{
|
|
||||||
components::{
|
|
||||||
sapling::{Authorized as SapAuthorized, Bundle},
|
|
||||||
Amount, OutputDescription, SpendDescription, GROTH_PROOF_SIZE,
|
|
||||||
},
|
|
||||||
Authorized, TransactionData, TxVersion,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use zcash_proofs::{prover::LocalTxProver, sapling::SaplingProvingContext};
|
use zcash_proofs::prover::LocalTxProver;
|
||||||
|
|
||||||
mod orchard_bundle;
|
mod orchard_bundle;
|
||||||
mod sapling_bundle;
|
mod sapling_bundle;
|
||||||
|
@ -95,7 +81,8 @@ pub async fn build_broadcast_tx(
|
||||||
let mut transparent_builder = TransparentBuilder::new(network, &pubkey);
|
let mut transparent_builder = TransparentBuilder::new(network, &pubkey);
|
||||||
|
|
||||||
if transparent_builder.taddr != tx_plan.taddr {
|
if transparent_builder.taddr != tx_plan.taddr {
|
||||||
anyhow::bail!("This ledger wallet has a different address");
|
anyhow::bail!("This ledger wallet has a different address {} != {}",
|
||||||
|
transparent_builder.taddr, tx_plan.taddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute header digest
|
// Compute header digest
|
||||||
|
@ -116,10 +103,20 @@ pub async fn build_broadcast_tx(
|
||||||
// let dfvk = extsk.to_diversifiable_full_viewing_key();
|
// let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||||
|
|
||||||
let dfvk: zcash_primitives::zip32::DiversifiableFullViewingKey = ledger_get_dfvk().await?;
|
let dfvk: zcash_primitives::zip32::DiversifiableFullViewingKey = ledger_get_dfvk().await?;
|
||||||
let proofgen_key: zcash_primitives::sapling::ProofGenerationKey = ledger_get_proofgen_key().await?;
|
let proofgen_key: zcash_primitives::sapling::ProofGenerationKey =
|
||||||
|
ledger_get_proofgen_key().await?;
|
||||||
|
|
||||||
let mut sapling_builder = SaplingBuilder::new(prover, dfvk, proofgen_key);
|
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: [u8; 96] = ledger_get_o_fvk().await?.try_into().unwrap();
|
||||||
let _o_fvk =
|
let _o_fvk =
|
||||||
orchard::keys::FullViewingKey::from_bytes(&o_fvk).ok_or(anyhow!("Invalid Orchard FVK"))?;
|
orchard::keys::FullViewingKey::from_bytes(&o_fvk).ok_or(anyhow!("Invalid Orchard FVK"))?;
|
||||||
|
@ -158,9 +155,13 @@ pub async fn build_broadcast_tx(
|
||||||
let alpha = Fr::random(&mut alpha_rng);
|
let alpha = Fr::random(&mut alpha_rng);
|
||||||
println!("ALPHA {}", hex::encode(&alpha.to_bytes()));
|
println!("ALPHA {}", hex::encode(&alpha.to_bytes()));
|
||||||
|
|
||||||
sapling_builder.add_spend(alpha, diversifier, rseed, witness, sp.amount).await?;
|
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)?;
|
||||||
}
|
}
|
||||||
Source::Orchard { .. } => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ledger_set_stage(2).await?;
|
ledger_set_stage(2).await?;
|
||||||
|
@ -172,25 +173,40 @@ pub async fn build_broadcast_tx(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
transparent_builder.set_merkle_proof().await?;
|
||||||
ledger_set_stage(3).await?;
|
ledger_set_stage(3).await?;
|
||||||
|
|
||||||
for output in tx_plan.outputs.iter() {
|
for output in tx_plan.outputs.iter() {
|
||||||
if let Destination::Sapling(raw_address) = output.destination {
|
match output.destination {
|
||||||
let mut rseed = [0u8; 32];
|
Destination::Sapling(raw_address) => {
|
||||||
rseed_rng.fill_bytes(&mut rseed);
|
let mut rseed = [0u8; 32];
|
||||||
sapling_builder.add_output(rseed, raw_address, &output.memo, output.amount).await?;
|
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?;
|
ledger_set_stage(4).await?;
|
||||||
|
|
||||||
transparent_builder.set_merkle_proof().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_set_stage(5).await?;
|
||||||
|
ledger_confirm_fee().await?;
|
||||||
|
|
||||||
transparent_builder.sign().await?;
|
transparent_builder.sign().await?;
|
||||||
|
sapling_builder.sign().await?;
|
||||||
|
orchard_builder.sign().await?;
|
||||||
|
|
||||||
let transparent_bundle = transparent_builder.build();
|
let transparent_bundle = transparent_builder.build();
|
||||||
let sapling_bundle = sapling_builder.build(tx_plan.net_chg[0]).await?;
|
let sapling_bundle = sapling_builder.build().await?;
|
||||||
|
let orchard_bundle = orchard_builder.build()?;
|
||||||
|
|
||||||
let authed_tx: TransactionData<Authorized> = TransactionData {
|
let authed_tx: TransactionData<Authorized> = TransactionData {
|
||||||
version: TxVersion::Zip225,
|
version: TxVersion::Zip225,
|
||||||
|
@ -200,7 +216,7 @@ pub async fn build_broadcast_tx(
|
||||||
transparent_bundle,
|
transparent_bundle,
|
||||||
sprout_bundle: None,
|
sprout_bundle: None,
|
||||||
sapling_bundle,
|
sapling_bundle,
|
||||||
orchard_bundle: None,
|
orchard_bundle,
|
||||||
};
|
};
|
||||||
|
|
||||||
let tx = authed_tx.freeze().unwrap();
|
let tx = authed_tx.freeze().unwrap();
|
||||||
|
@ -216,7 +232,7 @@ pub async fn build_broadcast_tx(
|
||||||
}))
|
}))
|
||||||
.await?
|
.await?
|
||||||
.into_inner();
|
.into_inner();
|
||||||
log::info!("{}", response.error_message);
|
println!("{}", response.error_message);
|
||||||
|
|
||||||
Ok(response.error_message)
|
Ok(response.error_message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ use orchard::{
|
||||||
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
|
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
|
||||||
Action, Address, Anchor, Bundle, Note, Proof,
|
Action, Address, Anchor, Bundle, Note, Proof,
|
||||||
};
|
};
|
||||||
use rand::{RngCore, SeedableRng};
|
|
||||||
|
use rand::{rngs::OsRng, RngCore, SeedableRng};
|
||||||
use rand_chacha::ChaCha20Rng;
|
use rand_chacha::ChaCha20Rng;
|
||||||
use ripemd::Digest;
|
use ripemd::Digest;
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ use hex_literal::hex;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::{BlockHeight, BranchId},
|
consensus::{BlockHeight, BranchId},
|
||||||
|
memo::MemoBytes,
|
||||||
transaction::{
|
transaction::{
|
||||||
components::Amount, sighash::SignableInput, sighash_v5, txid::TxIdDigester,
|
components::Amount, sighash::SignableInput, sighash_v5, txid::TxIdDigester,
|
||||||
Authorized as TxAuthorized, Transaction, TransactionData, TxVersion, Unauthorized,
|
Authorized as TxAuthorized, Transaction, TransactionData, TxVersion, Unauthorized,
|
||||||
|
@ -43,6 +45,8 @@ use zcash_primitives::{
|
||||||
|
|
||||||
use crate::{Destination, Source};
|
use crate::{Destination, Source};
|
||||||
|
|
||||||
|
use super::create_hasher;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NoAuth;
|
pub struct NoAuth;
|
||||||
|
|
||||||
|
@ -50,6 +54,279 @@ impl Authorization for NoAuth {
|
||||||
type SpendAuth = ();
|
type SpendAuth = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct OrchardBuilder {
|
||||||
|
orchard_fvk: FullViewingKey,
|
||||||
|
anchor: Anchor,
|
||||||
|
|
||||||
|
spends: Vec<OrchardSpend>,
|
||||||
|
outputs: Vec<OrchardOutput>,
|
||||||
|
padded_inouts: Vec<(OrchardSpend, OrchardOutput)>,
|
||||||
|
|
||||||
|
actions: Vec<Action<SigningMetadata>>,
|
||||||
|
auth_actions: Vec<Action<Signature<SpendAuth>>>,
|
||||||
|
|
||||||
|
net_value: ValueSum,
|
||||||
|
net_rcv: ValueCommitTrapdoor,
|
||||||
|
proof: Proof,
|
||||||
|
|
||||||
|
sig_hash: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OrchardBuilder {
|
||||||
|
pub fn new(orchard_fvk: &FullViewingKey, anchor: Anchor) -> Self {
|
||||||
|
OrchardBuilder {
|
||||||
|
orchard_fvk: orchard_fvk.clone(),
|
||||||
|
anchor,
|
||||||
|
|
||||||
|
spends: vec![],
|
||||||
|
outputs: vec![],
|
||||||
|
padded_inouts: vec![],
|
||||||
|
|
||||||
|
actions: vec![],
|
||||||
|
auth_actions: vec![],
|
||||||
|
|
||||||
|
proof: Proof::new(vec![]),
|
||||||
|
net_value: ValueSum::default(),
|
||||||
|
net_rcv: ValueCommitTrapdoor::zero(),
|
||||||
|
|
||||||
|
sig_hash: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_spend(
|
||||||
|
&mut self,
|
||||||
|
diversifier: [u8; 11],
|
||||||
|
rseed: [u8; 32],
|
||||||
|
rho: [u8; 32],
|
||||||
|
witness: &[u8],
|
||||||
|
amount: u64,
|
||||||
|
) -> Result<()> {
|
||||||
|
let diversifier = Diversifier::from_bytes(diversifier);
|
||||||
|
let address = self.orchard_fvk.address(diversifier, Scope::External);
|
||||||
|
let rho = Nullifier::from_bytes(&rho).unwrap();
|
||||||
|
let rseed = RandomSeed::from_bytes(rseed, &rho).unwrap();
|
||||||
|
let note = Note::from_parts(address, NoteValue::from_raw(amount), rho, rseed).unwrap();
|
||||||
|
let merkle_path = decode_orchard_merkle_path(0, &witness).unwrap();
|
||||||
|
self.spends.push(OrchardSpend {
|
||||||
|
ask: None,
|
||||||
|
fvk: self.orchard_fvk.clone(),
|
||||||
|
note,
|
||||||
|
merkle_path,
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_output(&mut self, address: [u8; 43], amount: u64, memo: &MemoBytes) -> Result<()> {
|
||||||
|
let address = Address::from_raw_address_bytes(&address).unwrap();
|
||||||
|
let output = OrchardOutput {
|
||||||
|
recipient: address,
|
||||||
|
amount: NoteValue::from_raw(amount),
|
||||||
|
memo: memo.as_array().clone(),
|
||||||
|
};
|
||||||
|
self.outputs.push(output);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prepare<R: RngCore>(
|
||||||
|
&mut self,
|
||||||
|
netchg: i64,
|
||||||
|
pk: &ProvingKey,
|
||||||
|
mut alpha_rng: R,
|
||||||
|
mut rseed_rng: R,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut orchard_memos_hasher = create_hasher(b"ZTxIdOrcActMHash");
|
||||||
|
let mut orchard_nc_hasher = create_hasher(b"ZTxIdOrcActNHash");
|
||||||
|
|
||||||
|
let num_actions = self.spends.len().max(self.outputs.len());
|
||||||
|
let mut circuits = vec![];
|
||||||
|
let mut instances = vec![];
|
||||||
|
|
||||||
|
for i in 0..num_actions {
|
||||||
|
// pad with dummy spends/outputs
|
||||||
|
let spend = if i < self.spends.len() {
|
||||||
|
self.spends[i].clone()
|
||||||
|
} else {
|
||||||
|
OrchardSpend::dummy(&mut OsRng)
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = if i < self.outputs.len() {
|
||||||
|
self.outputs[i].clone()
|
||||||
|
} else {
|
||||||
|
OrchardOutput::dummy(&mut OsRng)
|
||||||
|
};
|
||||||
|
self.padded_inouts.push((spend.clone(), output.clone()));
|
||||||
|
|
||||||
|
let rcv = ValueCommitTrapdoor::random(&mut OsRng);
|
||||||
|
self.net_rcv = self.net_rcv.clone() + &rcv;
|
||||||
|
let alpha = pasta_curves::Fq::random(&mut alpha_rng);
|
||||||
|
let ak: SpendValidatingKey = spend.fvk.clone().into();
|
||||||
|
let rk = ak.randomize(&alpha);
|
||||||
|
|
||||||
|
let rho = spend.note.nullifier(&spend.fvk);
|
||||||
|
let mut rseed = [0u8; 32];
|
||||||
|
rseed_rng.fill_bytes(&mut rseed);
|
||||||
|
let rseed = RandomSeed::from_bytes(rseed, &rho).unwrap();
|
||||||
|
|
||||||
|
let v_net: ValueSum = spend.note.value() - output.amount;
|
||||||
|
self.net_value = (self.net_value + v_net).unwrap();
|
||||||
|
let cv_net = ValueCommitment::derive(v_net, rcv.clone());
|
||||||
|
|
||||||
|
let spend_info = SpendInfo::new(
|
||||||
|
spend.fvk.clone(),
|
||||||
|
spend.note.clone(),
|
||||||
|
spend.merkle_path.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let output_note = Note::from_parts(
|
||||||
|
output.recipient.clone(),
|
||||||
|
output.amount.clone(),
|
||||||
|
rho.clone(),
|
||||||
|
rseed,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let cmx: ExtractedNoteCommitment = output_note.commitment().into();
|
||||||
|
|
||||||
|
let encryptor = OrchardNoteEncryption::new(
|
||||||
|
Some(self.orchard_fvk.to_ovk(Scope::External)),
|
||||||
|
output_note.clone(),
|
||||||
|
output.recipient.clone(),
|
||||||
|
output.memo.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let epk = encryptor.epk().to_bytes().0;
|
||||||
|
let enc = encryptor.encrypt_note_plaintext();
|
||||||
|
let out = encryptor.encrypt_outgoing_plaintext(&cv_net.clone(), &cmx, &mut OsRng);
|
||||||
|
let encrypted_note = TransmittedNoteCiphertext {
|
||||||
|
epk_bytes: epk.clone(),
|
||||||
|
enc_ciphertext: enc.clone(),
|
||||||
|
out_ciphertext: out.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let rk_bytes: [u8; 32] = rk.clone().0.into();
|
||||||
|
orchard_memos_hasher.update(&enc[52..564]);
|
||||||
|
orchard_nc_hasher.update(&cv_net.to_bytes());
|
||||||
|
orchard_nc_hasher.update(&rk_bytes);
|
||||||
|
orchard_nc_hasher.update(&enc[564..]);
|
||||||
|
orchard_nc_hasher.update(&out);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"d/pkd {}",
|
||||||
|
hex::encode(&output.recipient.to_raw_address_bytes())
|
||||||
|
);
|
||||||
|
println!("rho {}", hex::encode(&rho.to_bytes()));
|
||||||
|
println!(
|
||||||
|
"amount {}",
|
||||||
|
hex::encode(&output.amount.inner().to_le_bytes())
|
||||||
|
);
|
||||||
|
println!("rseed {}", hex::encode(&rseed.as_bytes()));
|
||||||
|
println!("cmx {}", hex::encode(&cmx.to_bytes()));
|
||||||
|
|
||||||
|
let action: Action<SigningMetadata> = Action::from_parts(
|
||||||
|
rho.clone(),
|
||||||
|
rk.clone(),
|
||||||
|
cmx.clone(),
|
||||||
|
encrypted_note,
|
||||||
|
cv_net.clone(),
|
||||||
|
SigningMetadata {
|
||||||
|
dummy_ask: None,
|
||||||
|
parts: SigningParts { ak, alpha },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.actions.push(action);
|
||||||
|
|
||||||
|
let circuit =
|
||||||
|
Circuit::from_action_context(spend_info, output_note, alpha, rcv.clone()).unwrap();
|
||||||
|
circuits.push(circuit);
|
||||||
|
let instance = Instance::from_parts(self.anchor, cv_net, rho.clone(), rk, cmx, true, true);
|
||||||
|
instances.push(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.proof = Proof::create(&pk, &circuits, &instances, &mut OsRng).unwrap();
|
||||||
|
|
||||||
|
for (a, (_, ref o)) in self.actions.iter().zip(self.padded_inouts.iter()) {
|
||||||
|
let nf = a.nullifier().to_bytes();
|
||||||
|
let epk = a.encrypted_note().epk_bytes;
|
||||||
|
ledger_add_o_action(
|
||||||
|
&nf,
|
||||||
|
o.amount.inner(),
|
||||||
|
&epk,
|
||||||
|
&o.recipient.to_raw_address_bytes(),
|
||||||
|
&a.encrypted_note().enc_ciphertext[0..52],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ledger_set_orchard_merkle_proof(
|
||||||
|
&self.anchor.to_bytes(),
|
||||||
|
orchard_memos_hasher.finalize().as_bytes(),
|
||||||
|
orchard_nc_hasher.finalize().as_bytes(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ledger_set_net_orchard(-netchg).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn sign(&mut self) -> Result<()> {
|
||||||
|
self.sig_hash = ledger_get_sighash().await?;
|
||||||
|
|
||||||
|
for (a, (ref s, _)) in self.actions.iter().zip(self.padded_inouts.iter()) {
|
||||||
|
println!("alpha {:?}", a.authorization().parts.alpha);
|
||||||
|
|
||||||
|
let signature =
|
||||||
|
match s.ask {
|
||||||
|
Some(ref ask) => { // dummy spend (we have a dummy key)
|
||||||
|
println!("DUMMY SPEND");
|
||||||
|
let rsk = ask.randomize(&a.authorization().parts.alpha);
|
||||||
|
rsk.sign(&mut OsRng, &self.sig_hash)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let sig_bytes: [u8; 64] = ledger_sign_orchard().await.unwrap().try_into().unwrap();
|
||||||
|
let signature: Signature<SpendAuth> = sig_bytes.into();
|
||||||
|
signature
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let auth_action = Action::from_parts(
|
||||||
|
a.nullifier().clone(),
|
||||||
|
a.rk().clone(),
|
||||||
|
a.cmx().clone(),
|
||||||
|
a.encrypted_note().clone(),
|
||||||
|
a.cv_net().clone(),
|
||||||
|
signature,
|
||||||
|
);
|
||||||
|
self.auth_actions.push(auth_action);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Result<Option<Bundle<Authorized, Amount>>> {
|
||||||
|
if self.auth_actions.is_empty() { return Ok(None); }
|
||||||
|
let auth_actions = NonEmpty::from_slice(&self.auth_actions).unwrap();
|
||||||
|
|
||||||
|
let nv = i64::try_from(self.net_value).unwrap();
|
||||||
|
let amount = Amount::from_i64(nv).unwrap();
|
||||||
|
let bsk = self.net_rcv.into_bsk();
|
||||||
|
|
||||||
|
let flags = Flags::from_parts(true, true);
|
||||||
|
let binding_signature = bsk.sign(&mut OsRng, &self.sig_hash);
|
||||||
|
|
||||||
|
let bundle: Bundle<Authorized, Amount> = Bundle::from_parts(
|
||||||
|
auth_actions,
|
||||||
|
flags,
|
||||||
|
amount,
|
||||||
|
self.anchor.clone(),
|
||||||
|
Authorized::from_parts(self.proof, binding_signature),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Some(bundle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn build_orchard() -> Result<()> {
|
pub async fn build_orchard() -> Result<()> {
|
||||||
dotenv::dotenv()?;
|
dotenv::dotenv()?;
|
||||||
let mut prng = ChaCha20Rng::from_seed([0; 32]);
|
let mut prng = ChaCha20Rng::from_seed([0; 32]);
|
||||||
|
@ -102,6 +379,7 @@ pub async fn build_orchard() -> Result<()> {
|
||||||
Note::from_parts(address, NoteValue::from_raw(s.amount), rho, rseed).unwrap();
|
Note::from_parts(address, NoteValue::from_raw(s.amount), rho, rseed).unwrap();
|
||||||
let merkle_path = decode_orchard_merkle_path(0, &witness).unwrap();
|
let merkle_path = decode_orchard_merkle_path(0, &witness).unwrap();
|
||||||
Some(OrchardSpend {
|
Some(OrchardSpend {
|
||||||
|
ask: None,
|
||||||
fvk: orchard_fvk.clone(),
|
fvk: orchard_fvk.clone(),
|
||||||
note,
|
note,
|
||||||
merkle_path,
|
merkle_path,
|
||||||
|
@ -143,9 +421,8 @@ pub async fn build_orchard() -> Result<()> {
|
||||||
let mut actions = vec![];
|
let mut actions = vec![];
|
||||||
let mut circuits = vec![];
|
let mut circuits = vec![];
|
||||||
let mut instances = vec![];
|
let mut instances = vec![];
|
||||||
let mut net_value = ValueSum::default();
|
|
||||||
let mut sum_rcv = ValueCommitTrapdoor::zero();
|
let mut sum_rcv = ValueCommitTrapdoor::zero();
|
||||||
|
let mut net_value = ValueSum::default();
|
||||||
let mut padded_outputs = vec![];
|
let mut padded_outputs = vec![];
|
||||||
for i in 0..num_actions {
|
for i in 0..num_actions {
|
||||||
// pad with dummy spends/outputs
|
// pad with dummy spends/outputs
|
||||||
|
@ -165,7 +442,7 @@ pub async fn build_orchard() -> Result<()> {
|
||||||
let rcv = ValueCommitTrapdoor::random(&mut prng);
|
let rcv = ValueCommitTrapdoor::random(&mut prng);
|
||||||
sum_rcv = sum_rcv + &rcv;
|
sum_rcv = sum_rcv + &rcv;
|
||||||
let alpha = pasta_curves::Fq::random(&mut alpha_rng);
|
let alpha = pasta_curves::Fq::random(&mut alpha_rng);
|
||||||
let ak: SpendValidatingKey = orchard_fvk.clone().into();
|
let ak: SpendValidatingKey = spend.fvk.clone().into();
|
||||||
let rk = ak.randomize(&alpha);
|
let rk = ak.randomize(&alpha);
|
||||||
|
|
||||||
let rho = spend.note.nullifier(&orchard_fvk);
|
let rho = spend.note.nullifier(&orchard_fvk);
|
||||||
|
@ -380,6 +657,7 @@ pub async fn build_orchard() -> Result<()> {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct OrchardSpend {
|
struct OrchardSpend {
|
||||||
|
ask: Option<SpendAuthorizingKey>,
|
||||||
fvk: FullViewingKey,
|
fvk: FullViewingKey,
|
||||||
note: Note,
|
note: Note,
|
||||||
merkle_path: MerklePath,
|
merkle_path: MerklePath,
|
||||||
|
@ -387,9 +665,11 @@ struct OrchardSpend {
|
||||||
|
|
||||||
impl OrchardSpend {
|
impl OrchardSpend {
|
||||||
pub fn dummy<R: RngCore>(rng: &mut R) -> Self {
|
pub fn dummy<R: RngCore>(rng: &mut R) -> Self {
|
||||||
let (_, fvk, dummy_note) = Note::dummy(rng, None);
|
let (sk, fvk, dummy_note) = Note::dummy(rng, None);
|
||||||
|
let ask = SpendAuthorizingKey::from(&sk);
|
||||||
let dummy_path = MerklePath::dummy(rng);
|
let dummy_path = MerklePath::dummy(rng);
|
||||||
OrchardSpend {
|
OrchardSpend {
|
||||||
|
ask: Some(ask),
|
||||||
fvk,
|
fvk,
|
||||||
note: dummy_note,
|
note: dummy_note,
|
||||||
merkle_path: dummy_path,
|
merkle_path: dummy_path,
|
||||||
|
|
|
@ -1,38 +1,22 @@
|
||||||
use blake2b_simd::Params;
|
|
||||||
use blake2b_simd::State;
|
use blake2b_simd::State;
|
||||||
use byteorder::WriteBytesExt;
|
|
||||||
use byteorder::LE;
|
use ff::PrimeField;
|
||||||
use ff::{Field, PrimeField};
|
|
||||||
use group::GroupEncoding;
|
use group::GroupEncoding;
|
||||||
use hex_literal::hex;
|
|
||||||
use jubjub::{Fq, Fr};
|
use jubjub::{Fq, Fr};
|
||||||
use zcash_primitives::memo::MemoBytes;
|
use zcash_primitives::memo::MemoBytes;
|
||||||
use zcash_primitives::sapling::ProofGenerationKey;
|
use zcash_primitives::sapling::ProofGenerationKey;
|
||||||
use zcash_primitives::zip32::DiversifiableFullViewingKey;
|
use zcash_primitives::zip32::DiversifiableFullViewingKey;
|
||||||
|
|
||||||
use crate::ledger::builder::transparent_bundle::TransparentBuilder;
|
|
||||||
use crate::ledger::transport::*;
|
use crate::ledger::transport::*;
|
||||||
|
|
||||||
use crate::{CompactTxStreamerClient, Destination, RawTransaction, Source, TransactionPlan};
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rand::{rngs::OsRng, RngCore, SeedableRng};
|
use rand::rngs::OsRng;
|
||||||
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 ripemd::Digest;
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::{BlockHeight, BranchId, MainNetwork},
|
consensus::MainNetwork,
|
||||||
constants::PROOF_GENERATION_KEY_GENERATOR,
|
|
||||||
merkle_tree::IncrementalWitness,
|
merkle_tree::IncrementalWitness,
|
||||||
sapling::{
|
sapling::{
|
||||||
note_encryption::sapling_note_encryption,
|
note_encryption::sapling_note_encryption,
|
||||||
|
@ -41,12 +25,9 @@ use zcash_primitives::{
|
||||||
value::{NoteValue, ValueCommitment, ValueSum},
|
value::{NoteValue, ValueCommitment, ValueSum},
|
||||||
Diversifier, Node, Note, Nullifier, PaymentAddress, Rseed,
|
Diversifier, Node, Note, Nullifier, PaymentAddress, Rseed,
|
||||||
},
|
},
|
||||||
transaction::{
|
transaction::components::{
|
||||||
components::{
|
sapling::{Authorized as SapAuthorized, Bundle},
|
||||||
sapling::{Authorized as SapAuthorized, Bundle},
|
Amount, OutputDescription, SpendDescription, GROTH_PROOF_SIZE,
|
||||||
Amount, OutputDescription, SpendDescription, GROTH_PROOF_SIZE,
|
|
||||||
},
|
|
||||||
Authorized, TransactionData, TxVersion,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use zcash_proofs::{prover::LocalTxProver, sapling::SaplingProvingContext};
|
use zcash_proofs::{prover::LocalTxProver, sapling::SaplingProvingContext};
|
||||||
|
@ -81,8 +62,12 @@ pub struct SaplingBuilder<'a> {
|
||||||
signatures: Vec<Signature>,
|
signatures: Vec<Signature>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> SaplingBuilder<'a> {
|
impl<'a> SaplingBuilder<'a> {
|
||||||
pub fn new(prover: &'a LocalTxProver, dfvk: DiversifiableFullViewingKey, proofgen_key: ProofGenerationKey) -> Self {
|
pub fn new(
|
||||||
|
prover: &'a LocalTxProver,
|
||||||
|
dfvk: DiversifiableFullViewingKey,
|
||||||
|
proofgen_key: ProofGenerationKey,
|
||||||
|
) -> Self {
|
||||||
let spends_compact_hasher = create_hasher(b"ZTxIdSSpendCHash");
|
let spends_compact_hasher = create_hasher(b"ZTxIdSSpendCHash");
|
||||||
let spends_non_compact_hasher = create_hasher(b"ZTxIdSSpendNHash");
|
let spends_non_compact_hasher = create_hasher(b"ZTxIdSSpendNHash");
|
||||||
|
|
||||||
|
@ -109,9 +94,18 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_spend(&mut self, alpha: Fr, diversifier: [u8; 11], rseed: [u8; 32], witness: &[u8], amount: u64) -> Result<()> {
|
pub async fn add_spend(
|
||||||
|
&mut self,
|
||||||
|
alpha: Fr,
|
||||||
|
diversifier: [u8; 11],
|
||||||
|
rseed: [u8; 32],
|
||||||
|
witness: &[u8],
|
||||||
|
amount: u64,
|
||||||
|
) -> Result<()> {
|
||||||
let diversifier = Diversifier(diversifier);
|
let diversifier = Diversifier(diversifier);
|
||||||
let z_address = self.dfvk.fvk
|
let z_address = self
|
||||||
|
.dfvk
|
||||||
|
.fvk
|
||||||
.vk
|
.vk
|
||||||
.to_payment_address(diversifier)
|
.to_payment_address(diversifier)
|
||||||
.ok_or(anyhow!("Invalid diversifier"))?;
|
.ok_or(anyhow!("Invalid diversifier"))?;
|
||||||
|
@ -126,7 +120,8 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
let nf_key = self.proofgen_key.to_viewing_key().nk;
|
let nf_key = self.proofgen_key.to_viewing_key().nk;
|
||||||
let nullifier = note.nf(&nf_key, merkle_path.position);
|
let nullifier = note.nf(&nf_key, merkle_path.position);
|
||||||
|
|
||||||
let (zkproof, cv, rk) = self.prover
|
let (zkproof, cv, rk) = self
|
||||||
|
.prover
|
||||||
.spend_proof(
|
.spend_proof(
|
||||||
&mut self.sapling_context,
|
&mut self.sapling_context,
|
||||||
self.proofgen_key.clone(),
|
self.proofgen_key.clone(),
|
||||||
|
@ -139,7 +134,8 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
OsRng,
|
OsRng,
|
||||||
)
|
)
|
||||||
.map_err(|_| anyhow!("Error generating spend"))?;
|
.map_err(|_| anyhow!("Error generating spend"))?;
|
||||||
self.value_balance = (self.value_balance + note.value()).ok_or(anyhow!("Invalid amount"))?;
|
self.value_balance =
|
||||||
|
(self.value_balance + note.value()).ok_or(anyhow!("Invalid amount"))?;
|
||||||
|
|
||||||
self.spends_compact_hasher.update(nullifier.as_ref());
|
self.spends_compact_hasher.update(nullifier.as_ref());
|
||||||
self.spends_non_compact_hasher.update(&cv.to_bytes());
|
self.spends_non_compact_hasher.update(&cv.to_bytes());
|
||||||
|
@ -156,7 +152,13 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_output(&mut self, rseed: [u8; 32], raw_address: [u8; 43], memo: &MemoBytes, amount: u64) -> Result<()> {
|
pub async fn add_output(
|
||||||
|
&mut self,
|
||||||
|
rseed: [u8; 32],
|
||||||
|
raw_address: [u8; 43],
|
||||||
|
memo: &MemoBytes,
|
||||||
|
amount: u64,
|
||||||
|
) -> Result<()> {
|
||||||
let recipient = PaymentAddress::from_bytes(&raw_address).unwrap();
|
let recipient = PaymentAddress::from_bytes(&raw_address).unwrap();
|
||||||
let rseed = Rseed::AfterZip212(rseed);
|
let rseed = Rseed::AfterZip212(rseed);
|
||||||
|
|
||||||
|
@ -201,8 +203,10 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
let memo = &enc_ciphertext[52..564];
|
let memo = &enc_ciphertext[52..564];
|
||||||
self.output_memos_hasher.update(memo);
|
self.output_memos_hasher.update(memo);
|
||||||
|
|
||||||
self.output_non_compact_hasher.update(&cv.as_inner().to_bytes());
|
self.output_non_compact_hasher
|
||||||
self.output_non_compact_hasher.update(&enc_ciphertext[564..]);
|
.update(&cv.as_inner().to_bytes());
|
||||||
|
self.output_non_compact_hasher
|
||||||
|
.update(&enc_ciphertext[564..]);
|
||||||
self.output_non_compact_hasher.update(&out_ciphertext);
|
self.output_non_compact_hasher.update(&out_ciphertext);
|
||||||
|
|
||||||
let ephemeral_key = epk.to_bytes();
|
let ephemeral_key = epk.to_bytes();
|
||||||
|
@ -217,6 +221,36 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_merkle_proof(&mut self, net_chg: i64) -> Result<()> {
|
||||||
|
let spends_compact_digest = self.spends_compact_hasher.finalize();
|
||||||
|
log::info!("C SPENDS {}", hex::encode(spends_compact_digest));
|
||||||
|
let spends_non_compact_digest = self.spends_non_compact_hasher.finalize();
|
||||||
|
log::info!("NC SPENDS {}", hex::encode(spends_non_compact_digest));
|
||||||
|
|
||||||
|
let mut spends_hasher = create_hasher(b"ZTxIdSSpendsHash");
|
||||||
|
if !self.shielded_spends.is_empty() {
|
||||||
|
spends_hasher.update(spends_compact_digest.as_bytes());
|
||||||
|
spends_hasher.update(spends_non_compact_digest.as_bytes());
|
||||||
|
}
|
||||||
|
let spends_digest = spends_hasher.finalize();
|
||||||
|
log::info!("SPENDS {}", hex::encode(spends_digest));
|
||||||
|
|
||||||
|
let memos_digest = self.output_memos_hasher.finalize();
|
||||||
|
log::info!("MEMOS {}", hex::encode(memos_digest));
|
||||||
|
let outputs_nc_digest = self.output_non_compact_hasher.finalize();
|
||||||
|
log::info!("NC OUTPUTS {}", hex::encode(outputs_nc_digest));
|
||||||
|
|
||||||
|
ledger_set_sapling_merkle_proof(
|
||||||
|
spends_digest.as_bytes(),
|
||||||
|
memos_digest.as_bytes(),
|
||||||
|
outputs_nc_digest.as_bytes(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
ledger_set_net_sapling(-net_chg).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn sign(&mut self) -> Result<()> {
|
pub async fn sign(&mut self) -> Result<()> {
|
||||||
for _sp in self.shielded_spends.iter() {
|
for _sp in self.shielded_spends.iter() {
|
||||||
let signature = ledger_sign_sapling().await?;
|
let signature = ledger_sign_sapling().await?;
|
||||||
|
@ -234,29 +268,14 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(self, net_chg: i64) -> Result<Option<Bundle<SapAuthorized>>> {
|
pub async fn build(self) -> Result<Option<Bundle<SapAuthorized>>> {
|
||||||
let spends_compact_digest = self.spends_compact_hasher.finalize();
|
|
||||||
log::info!("C SPENDS {}", hex::encode(spends_compact_digest));
|
|
||||||
let spends_non_compact_digest = self.spends_non_compact_hasher.finalize();
|
|
||||||
log::info!("NC SPENDS {}", hex::encode(spends_non_compact_digest));
|
|
||||||
|
|
||||||
let mut spends_hasher = create_hasher(b"ZTxIdSSpendsHash");
|
|
||||||
if !self.shielded_spends.is_empty() {
|
|
||||||
spends_hasher.update(spends_compact_digest.as_bytes());
|
|
||||||
spends_hasher.update(spends_non_compact_digest.as_bytes());
|
|
||||||
}
|
|
||||||
let spends_digest = spends_hasher.finalize();
|
|
||||||
log::info!("SPENDS {}", hex::encode(spends_digest));
|
|
||||||
|
|
||||||
let memos_digest = self.output_memos_hasher.finalize();
|
|
||||||
log::info!("MEMOS {}", hex::encode(memos_digest));
|
|
||||||
let outputs_nc_digest = self.output_non_compact_hasher.finalize();
|
|
||||||
log::info!("NC OUTPUTS {}", hex::encode(outputs_nc_digest));
|
|
||||||
|
|
||||||
let has_sapling = !self.shielded_spends.is_empty() || !self.shielded_outputs.is_empty();
|
let has_sapling = !self.shielded_spends.is_empty() || !self.shielded_outputs.is_empty();
|
||||||
if !has_sapling { return Ok(None); }
|
if !has_sapling {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
let shielded_spends: Vec<_> = self.shielded_spends
|
let shielded_spends: Vec<_> = self
|
||||||
|
.shielded_spends
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.signatures.into_iter())
|
.zip(self.signatures.into_iter())
|
||||||
.map(|(sp, spend_auth_sig)| SpendDescription::<SapAuthorized> {
|
.map(|(sp, spend_auth_sig)| SpendDescription::<SapAuthorized> {
|
||||||
|
@ -274,18 +293,11 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
|
|
||||||
let sighash = ledger_get_sighash().await?;
|
let sighash = ledger_get_sighash().await?;
|
||||||
log::info!("TXID {}", hex::encode(&sighash));
|
log::info!("TXID {}", hex::encode(&sighash));
|
||||||
let binding_sig = self.sapling_context
|
let binding_sig = self
|
||||||
|
.sapling_context
|
||||||
.binding_sig(value, &sighash.try_into().unwrap())
|
.binding_sig(value, &sighash.try_into().unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
ledger_set_sapling_merkle_proof(
|
|
||||||
spends_digest.as_bytes(),
|
|
||||||
memos_digest.as_bytes(),
|
|
||||||
outputs_nc_digest.as_bytes(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
ledger_set_net_sapling(-net_chg).await?;
|
|
||||||
let sapling_bundle = Bundle::<_>::from_parts(
|
let sapling_bundle = Bundle::<_>::from_parts(
|
||||||
shielded_spends,
|
shielded_spends,
|
||||||
self.shielded_outputs,
|
self.shielded_outputs,
|
||||||
|
@ -295,5 +307,4 @@ impl <'a> SaplingBuilder<'a> {
|
||||||
|
|
||||||
Ok(Some(sapling_bundle))
|
Ok(Some(sapling_bundle))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -155,7 +155,7 @@ pub async fn ledger_set_orchard_merkle_proof(
|
||||||
|
|
||||||
pub async fn ledger_add_t_input(amount: u64) -> Result<()> {
|
pub async fn ledger_add_t_input(amount: u64) -> Result<()> {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E015000008"))?;
|
bb.write_all(&hex!("E015010008"))?;
|
||||||
bb.write_u64::<LE>(amount)?;
|
bb.write_u64::<LE>(amount)?;
|
||||||
apdu(&bb).await?;
|
apdu(&bb).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -222,7 +222,7 @@ pub async fn ledger_set_net_orchard(net: i64) -> Result<()> {
|
||||||
|
|
||||||
pub async fn ledger_confirm_fee() -> Result<()> {
|
pub async fn ledger_confirm_fee() -> Result<()> {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E01B000000"))?;
|
bb.write_all(&hex!("E01B010000"))?;
|
||||||
apdu(&bb).await?;
|
apdu(&bb).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ fn main() -> Result<()> {
|
||||||
let mut started = false;
|
let mut started = false;
|
||||||
let mut key = String::new();
|
let mut key = String::new();
|
||||||
let mut next_line_is_key = true;
|
let mut next_line_is_key = true;
|
||||||
for (i, line) in reader.lines().enumerate() {
|
for (_i, line) in reader.lines().enumerate() {
|
||||||
let ln = line?.trim_start().to_string();
|
let ln = line?.trim_start().to_string();
|
||||||
if ln == "HEADER=END" {
|
if ln == "HEADER=END" {
|
||||||
started = true;
|
started = true;
|
||||||
|
|
Loading…
Reference in New Issue