Ledger App integration

This commit is contained in:
Hanh 2023-04-19 09:55:10 +10:00
parent d3d8d27538
commit f855d7ca81
21 changed files with 774 additions and 231 deletions

View File

@ -32,6 +32,7 @@ name = "warp_api_ffi"
crate-type = ["rlib"]
[dependencies]
nonempty = "0.7"
env_logger = "0.9.0"
anyhow = "1.0.40"
thiserror = "1.0.25"
@ -75,6 +76,8 @@ chrono = "0.4.19"
lazycell = "1.3.0"
reqwest = { version = "0.11.4", features = ["json", "rustls-tls"], default-features = false }
hex-literal = "0.4"
pasta_curves = "0.5"
f4jumble = { path = "../../librustzcash/components/f4jumble" }
# Halo
orchard = "0.3.0"
@ -108,7 +111,7 @@ objc = { version = "0.2", features = [ "objc_exception" ], optional = true }
block = { version = "0.1.6", optional = true }
[features]
ledger = ["ledger-apdu", "ledger-transport-hid"]
ledger = ["ledger-apdu", "ledger-transport-hid", "dotenv"]
dart_ffi = ["allo-isolate", "once_cell", "android_logger"]
rpc = ["rocket", "dotenv"]
nodejs = ["node-bindgen"]
@ -118,38 +121,38 @@ sqlcipher = ["rusqlite/bundled-sqlcipher-vendored-openssl"]
[dependencies.zcash_params]
#git = "https://github.com/hhanh00/zcash-params.git"
#rev = "154f3544781500c27f58923ccc6c7e779eecf9df"
#rev = "2e118feceeaa31ef68fb83d1fc94a1a46db4569c"
path = "../zcash-params"
[dependencies.zcash_client_backend]
#git = "https://github.com/hhanh00/librustzcash.git"
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
path = "../../librustzcash/zcash_client_backend"
[dependencies.zcash_primitives]
#git = "https://github.com/hhanh00/librustzcash.git"
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
path = "../../librustzcash/zcash_primitives"
features = [ "transparent-inputs" ]
[dependencies.zcash_proofs]
#git = "https://github.com/hhanh00/librustzcash.git"
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
path = "../../librustzcash/zcash_proofs"
[dependencies.zcash_address]
#git = "https://github.com/hhanh00/librustzcash.git"
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
path = "../../librustzcash/components/zcash_address"
[dependencies.zcash_encoding]
#git = "https://github.com/hhanh00/librustzcash.git"
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
path = "../../librustzcash/components/zcash_encoding"
[dependencies.zcash_note_encryption]
#git = "https://github.com/hhanh00/librustzcash.git"
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
path = "../../librustzcash/components/zcash_note_encryption"
[build-dependencies]
@ -161,5 +164,4 @@ criterion = "0.3.4"
#These patch overrides must be included in your workspace root Cargo.toml
#[patch.crates-io]
#zcash_note_encryption = { git = "https://github.com/hhanh00/librustzcash.git", rev = "ad4a1c61fdaf04ac4fb884976ad175196e695264" }
#jubjub = { git = "https://github.com/hhanh00/jubjub.git", rev = "4a3edf3d242f368b5aa418ec659d01f191127cf3" }
#zcash_note_encryption = { git = "https://github.com/hhanh00/librustzcash.git", rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646" }

View File

@ -415,6 +415,10 @@ struct CResult_u8 set_property(uint8_t coin, char *name, char *value);
struct CResult_____c_char ledger_send(uint8_t coin, char *tx_plan);
struct CResult_u32 ledger_import_account(uint8_t coin, char *name);
struct CResult_bool ledger_has_account(uint8_t coin, uint32_t account);
bool has_cuda(void);
bool has_metal(void);

View File

@ -2,7 +2,7 @@ use crate::coinconfig::{init_coin, CoinConfig, MEMPOOL, MEMPOOL_RUNNER, self};
use crate::db::data_generated::fb::{ProgressT, Recipients, SendTemplate, Servers};
use crate::db::FullEncryptedBackup;
use crate::note_selection::TransactionReport;
use crate::{ChainError, TransactionPlan, Tx, build_broadcast_tx};
use crate::{ChainError, TransactionPlan, Tx};
use allo_isolate::{ffi, IntoDart};
use android_logger::Config;
use flatbuffers::FlatBufferBuilder;
@ -1219,6 +1219,7 @@ pub unsafe extern "C" fn set_property(
to_cresult(with_coin(coin, res))
}
#[cfg(feature = "ledger")]
#[no_mangle]
#[tokio::main]
pub async unsafe extern "C" fn ledger_send(coin: u8, tx_plan: *mut c_char) -> CResult<*mut c_char> {
@ -1228,12 +1229,30 @@ pub async unsafe extern "C" fn ledger_send(coin: u8, tx_plan: *mut c_char) -> CR
let c = CoinConfig::get(coin);
let mut client = c.connect_lwd().await?;
let prover = coinconfig::get_prover();
let response = build_broadcast_tx(&mut client, &tx_plan, prover).await?;
let response = crate::ledger::build_broadcast_tx(c.chain.network(), &mut client, &tx_plan, prover).await?;
Ok::<_, anyhow::Error>(response)
};
to_cresult_str(res.await)
}
#[cfg(feature = "ledger")]
#[no_mangle]
#[tokio::main]
pub async unsafe extern "C" fn ledger_import_account(coin: u8, name: *mut c_char) -> CResult<u32> {
from_c_str!(name);
let account = crate::ledger::import_account(coin, &name).await;
to_cresult(account)
}
#[cfg(feature = "ledger")]
#[no_mangle]
pub unsafe extern "C" fn ledger_has_account(coin: u8, account: u32) -> CResult<bool> {
let res = |connection: &Connection| {
crate::ledger::is_external(connection, account)
};
to_cresult(with_coin(coin, res))
}
#[no_mangle]
pub unsafe extern "C" fn has_cuda() -> bool {
crate::gpu::has_cuda()

View File

@ -42,11 +42,12 @@ pub async fn build_tx_plan_with_utxos(
}
}
let (fvk, taddr) = {
let (fvk, taddr, orchard_fvk) = {
let db = c.db()?;
let AccountData { fvk, .. } = db.get_account_info(account)?;
let taddr = db.get_taddr(account)?.unwrap_or_default();
(fvk, taddr)
let orchard_fvk = db.get_orchard(account)?.map(|o| hex::encode(&o.fvk)).unwrap_or_default();
(fvk, taddr, orchard_fvk)
};
let change_address = get_unified_address(coin, account, 7)?;
let context = TxBuilderContext::from_height(coin, checkpoint_height)?;
@ -79,6 +80,7 @@ pub async fn build_tx_plan_with_utxos(
network,
&fvk,
&taddr,
&orchard_fvk,
checkpoint_height,
expiry_height,
&context.orchard_anchor,

View File

@ -313,6 +313,12 @@ pub fn init_db(connection: &Connection, network: &Network, has_ua: bool) -> anyh
[],
)?;
connection.execute(
"CREATE TABLE IF NOT EXISTS hw_wallets(
account INTEGER PRIMARY KEY NOT NULL,
ledger BOOL NOT NULL)",
[],
)?;
}
if version != LATEST_VERSION {

View File

@ -708,3 +708,10 @@ pub fn get_available_addrs(connection: &Connection, account: u32) -> anyhow::Res
| if has_orchard { 4 } else { 0 };
Ok(res)
}
pub fn get_account_by_address(connection: &Connection, address: &str) -> Result<Option<u32>> {
let id = connection.query_row("SELECT id_account FROM accounts WHERE address = ?1", [address], |row| {
row.get::<_, u32>(0)
}).optional()?;
Ok(id)
}

View File

@ -1,7 +1,11 @@
mod transport;
mod builder;
mod account;
// #[cfg(test)]
mod tests;
pub use builder::build_broadcast_tx;
pub use transport::*;
pub use account::{import as import_account, is_external};

51
src/ledger/account.rs Normal file
View File

@ -0,0 +1,51 @@
use anyhow::Result;
use rusqlite::{params, Connection, OptionalExtension};
use zcash_client_backend::encoding::{encode_payment_address, encode_extended_full_viewing_key};
use zcash_primitives::{consensus::Parameters, zip32::ExtendedFullViewingKey};
use crate::{db::read::get_account_by_address, taddr::derive_from_pubkey, CoinConfig};
use super::transport::*;
pub async fn import(coin: u8, name: &str) -> Result<u32> {
let c = CoinConfig::get(coin);
let network = c.chain.network();
ledger_init().await?;
let dfvk = ledger_get_dfvk().await?;
let fvk = ExtendedFullViewingKey::from_diversifiable_full_viewing_key(&dfvk);
let fvk = encode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk);
let (_, pa) = dfvk.default_address();
let address = encode_payment_address(network.hrp_sapling_payment_address(), &pa);
let mut db = c.db()?;
if let Some(account) = get_account_by_address(&db.connection, &address)? {
return Ok(account)
}
let pub_key = ledger_get_pubkey().await?;
let t_address = derive_from_pubkey(network, &pub_key)?;
println!("OFVK");
let o_fvk = ledger_get_o_fvk().await?;
println!("fvk {}", hex::encode(&o_fvk));
println!("OFVK2");
let db_transaction = db.begin_transaction()?;
db_transaction.execute("INSERT INTO accounts(name, seed, aindex, sk, ivk, address) VALUES
(?1, NULL, 0, NULL, ?2, ?3)", params![name, fvk, address])?;
let id_account = db_transaction.last_insert_rowid() as u32;
db_transaction.execute("INSERT INTO taddrs(account, sk, address) VALUES
(?1, NULL, ?2)", params![id_account, t_address])?;
db_transaction.execute("INSERT INTO orchard_addrs(account, sk, fvk) VALUES
(?1, NULL, ?2)", params![id_account, o_fvk])?;
db_transaction.execute("INSERT INTO hw_wallets(account, ledger) VALUES
(?1, 1)", [id_account])?;
db_transaction.commit()?;
Ok(id_account)
}
pub fn is_external(connection: &Connection, account: u32) -> Result<bool> {
let res = connection.query_row("SELECT ledger FROM hw_wallets WHERE account = ?1", [account], |row| {
row.get::<_, bool>(0)
}).optional()?;
Ok(res.unwrap_or(false))
}

View File

@ -1,7 +1,3 @@
use std::{
io::Read, vec,
};
use std::io::Write;
use blake2b_simd::Params;
use byteorder::WriteBytesExt;
use byteorder::LE;
@ -9,26 +5,30 @@ use ff::{PrimeField, Field};
use group::GroupEncoding;
use hex_literal::hex;
use jubjub::{Fr, Fq};
use orchard::keys::Scope;
use rand::{rngs::OsRng, RngCore, SeedableRng};
use rand_chacha::ChaChaRng;
use ripemd::{Digest, Ripemd160};
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
use secp256k1::PublicKey;
use sha2::Sha256;
use tonic::{Request, transport::Channel};
use zcash_client_backend::address::RecipientAddress;
use zcash_client_backend::encoding::decode_transparent_address;
use zcash_client_backend::encoding::{decode_transparent_address, encode_extended_full_viewing_key, encode_transparent_address};
use zcash_primitives::consensus::Network;
use zcash_primitives::consensus::Parameters;
use zcash_primitives::legacy::{TransparentAddress, Script};
use zcash_primitives::transaction::components::transparent::builder::Unauthorized;
use zcash_primitives::transaction::components::{transparent, TxIn, OutPoint, TxOut};
use zcash_primitives::transaction::txid::TxIdDigester;
use zcash_primitives::zip32::ExtendedFullViewingKey;
use crate::taddr::derive_from_pubkey;
use crate::{Destination, Source, TransactionPlan, RawTransaction, CompactTxStreamerClient};
use crate::ledger::transport::*;
use anyhow::{anyhow, Result};
use zcash_primitives::{
consensus::{BlockHeight, BranchId, MainNetwork},
merkle_tree::{Hashable, IncrementalWitness},
merkle_tree::IncrementalWitness,
sapling::{
note_encryption::sapling_note_encryption, value::{NoteValue, ValueCommitment, ValueSum}, Diversifier, Node, Note,
PaymentAddress, Rseed, Nullifier, prover::TxProver, redjubjub::Signature,
@ -53,14 +53,40 @@ struct SpendDescriptionUnAuthorized {
zkproof: [u8; GROTH_PROOF_SIZE],
}
pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, tx_plan: &TransactionPlan, prover: &LocalTxProver) -> Result<String> {
#[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 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 ledger_taddr = derive_from_pubkey(network, &pubkey)?;
let network = MainNetwork; // TODO: Pass network as param
let secp = Secp256k1::<All>::new();
if ledger_taddr != tx_plan.taddr {
anyhow::bail!("This ledger wallet has a different address");
}
let taddr = &tx_plan.taddr;
let taddr = decode_transparent_address(
&network.b58_pubkey_address_prefix(),
&network.b58_script_address_prefix(),
@ -99,7 +125,9 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
let fvk = dfvk.fvk;
let nf_key = proofgen_key.to_viewing_key().nk;
println!("NSK {}", hex::encode(proofgen_key.nsk.to_bytes()));
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"))?;
assert_eq!(PROOF_GENERATION_KEY_GENERATOR * proofgen_key.nsk, fvk.vk.nk.0);
// Derive rseed PRNG
@ -118,7 +146,6 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
.to_state();
h.update(&master_seed);
let alpha = h.finalize();
println!("ALPHA SEED {}", hex::encode(&alpha.as_bytes()));
let mut alpha_rng = ChaChaRng::from_seed(alpha.as_bytes().try_into().unwrap());
let mut prevouts_hasher = Params::new()
@ -197,22 +224,22 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
shielded_spends.push(SpendDescriptionUnAuthorized { cv, anchor, nullifier, rk, zkproof });
}
Source::Orchard { .. } => { unimplemented!() },
Source::Orchard { .. } => {}
}
}
ledger_set_stage(1).await?;
ledger_set_stage(2).await?;
let prevouts_digest = prevouts_hasher.finalize();
println!("PREVOUTS {}", hex::encode(prevouts_digest));
log::info!("PREVOUTS {}", hex::encode(prevouts_digest));
let pubscripts_digest = trscripts_hasher.finalize();
println!("PUBSCRIPTS {}", hex::encode(pubscripts_digest));
log::info!("PUBSCRIPTS {}", hex::encode(pubscripts_digest));
let sequences_digest = sequences_hasher.finalize();
println!("SEQUENCES {}", hex::encode(sequences_digest));
log::info!("SEQUENCES {}", hex::encode(sequences_digest));
let spends_compact_digest = spends_compact_hasher.finalize();
println!("C SPENDS {}", hex::encode(spends_compact_digest));
log::info!("C SPENDS {}", hex::encode(spends_compact_digest));
let spends_non_compact_digest = spends_non_compact_hasher.finalize();
println!("NC SPENDS {}", hex::encode(spends_non_compact_digest));
log::info!("NC SPENDS {}", hex::encode(spends_non_compact_digest));
let mut spends_hasher = Params::new()
.hash_length(32)
@ -223,7 +250,7 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
spends_hasher.update(spends_non_compact_digest.as_bytes());
}
let spends_digest = spends_hasher.finalize();
println!("SPENDS {}", hex::encode(spends_digest));
log::info!("SPENDS {}", hex::encode(spends_digest));
let mut output_memos_hasher = Params::new()
.hash_length(32)
@ -250,7 +277,7 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
});
}
}
ledger_set_stage(2).await?;
ledger_set_stage(3).await?;
let has_transparent = !vin.is_empty() || !vout.is_empty();
for output in tx_plan.outputs.iter() {
@ -266,7 +293,7 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
let note = Note::from_parts(recipient, value, rseed);
let rcm = note.rcm();
let cmu = note.cmu();
println!("cmu {}", hex::encode(cmu.to_bytes()));
log::info!("cmu {}", hex::encode(cmu.to_bytes()));
let encryptor =
sapling_note_encryption::<_, MainNetwork>(Some(ovk.clone()), note, recipient, output.memo.clone(), &mut OsRng);
@ -298,12 +325,12 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
shielded_outputs.push(OutputDescription { cv, cmu, ephemeral_key, enc_ciphertext, out_ciphertext, zkproof });
}
}
ledger_set_stage(3).await?;
ledger_set_stage(4).await?;
let memos_digest = output_memos_hasher.finalize();
println!("MEMOS {}", hex::encode(memos_digest));
log::info!("MEMOS {}", hex::encode(memos_digest));
let outputs_nc_digest = output_non_compact_hasher.finalize();
println!("NC OUTPUTS {}", hex::encode(outputs_nc_digest));
log::info!("NC OUTPUTS {}", hex::encode(outputs_nc_digest));
ledger_set_transparent_merkle_proof(prevouts_digest.as_bytes(),
pubscripts_digest.as_bytes(), sequences_digest.as_bytes()).await?;
@ -311,6 +338,113 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
ledger_set_net_sapling(-tx_plan.net_chg[0]).await?;
ledger_set_stage(5).await?;
let orchard_spends: Vec<_> = tx_plan.spends.iter().filter(|&s|
if let Source::Orchard { .. } = s.source { true } else { false }
).cloned().collect();
let orchard_outputs: Vec<_> = tx_plan.outputs.iter().filter(|&o|
if let Destination::Orchard(_) = o.destination { true } else { false }
).cloned().collect();
let num_orchard_spends = orchard_spends.len();
let num_orchard_outputs = orchard_outputs.len();
let num_actions = num_orchard_spends.max(num_orchard_outputs);
let orchard_address = o_fvk.address_at(0u64, Scope::External);
let mut empty_memo = [0u8; 512];
empty_memo[0] = 0xF6;
// let mut actions = vec![];
for i in 0..num_actions {
let rcv = orchard::value::ValueCommitTrapdoor::random(OsRng);
let (_sk, dummy_fvk, dummy_note) = orchard::Note::dummy(&mut OsRng, None);
let _dummy_recipient = dummy_fvk.address_at(0u64, Scope::External);
let alpha = pasta_curves::pallas::Scalar::random(&mut alpha_rng);
let (fvk, spend_note) = if i < num_orchard_spends {
let sp = &tx_plan.spends[i];
let note = match &sp.source {
Source::Orchard { rseed, rho, .. } => {
let rho = orchard::note::Nullifier::from_bytes(rho).unwrap();
let note = orchard::Note::from_parts(
orchard_address.clone(),
orchard::value::NoteValue::from_raw(sp.amount),
rho,
orchard::note::RandomSeed::from_bytes(rseed.clone(), &rho).unwrap()).unwrap();
note
}
_ => unreachable!()
};
(o_fvk.clone(), note)
}
else {
(dummy_fvk, dummy_note)
};
let nf = spend_note.nullifier(&fvk);
let mut rseed = [0; 32];
rseed_rng.fill_bytes(&mut rseed);
let (output_note, memo) = if i < num_orchard_outputs {
let output = &orchard_outputs[i];
let address = match output.destination {
Destination::Orchard(address) => address,
_ => unreachable!()
};
let rseed = orchard::note::RandomSeed::from_bytes(rseed, &nf).unwrap();
let note = orchard::Note::from_parts(
orchard::Address::from_raw_address_bytes(&address).unwrap(),
orchard::value::NoteValue::from_raw(output.amount),
nf.clone(),
rseed).unwrap();
let memo = output.memo.as_array().clone();
(note, memo)
}
else {
(dummy_note.clone(), empty_memo)
};
let _rk = fvk.ak.randomize(&alpha);
let cm = output_note.commitment();
let cmx = cm.into();
let encryptor = orchard::note_encryption::OrchardNoteEncryption::new(
Some(o_fvk.to_ovk(Scope::External)),
output_note,
output_note.recipient(),
memo.clone()
);
let v_net = orchard::value::ValueSum::default();
let cv_net = orchard::value::ValueCommitment::derive(v_net, rcv.clone());
let _encrypted_note = orchard::note::TransmittedNoteCiphertext {
epk_bytes: encryptor.epk().to_bytes().0,
enc_ciphertext: encryptor.encrypt_note_plaintext(),
out_ciphertext: encryptor.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng),
};
// compact outputs ZTxIdOrcActCHash
// nf
// cmx
// epk_bytes
// enc_ciphertext[..52]
// memo ZTxIdOrcActMHash
// enc_ciphertext[52..564]
// non compact ZTxIdOrcActNHash
// cv_net
// rk
// enc_ciphertext[564..]
// out_ciphertext
}
let mut vins = vec![];
for tin in vin.iter() {
let mut txin_hasher = Params::new()
@ -325,7 +459,7 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
txin_hasher.update(&tin.coin.script_pubkey.0);
txin_hasher.update(&0xFFFFFFFFu32.to_le_bytes());
let txin_hash = txin_hasher.finalize();
println!("TXIN {}", hex::encode(txin_hash));
log::info!("TXIN {}", hex::encode(txin_hash));
let signature = ledger_sign_transparent(txin_hash.as_bytes()).await?;
let signature = secp256k1::ecdsa::Signature::from_der(&signature)?;
@ -340,14 +474,12 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
script_sig,
sequence: 0xFFFFFFFFu32,
};
println!("TXIN {:?}", txin);
vins.push(txin);
}
let mut signatures = vec![];
for _sp in shielded_spends.iter() {
let signature = ledger_sign_sapling().await?;
println!("SIGNATURE {}", hex::encode(&signature));
let signature = Signature::read(&*signature)?;
// Signature verification
// let rk = sp.rk();
@ -360,37 +492,6 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
signatures.push(signature);
}
// let sk = SecretKey::from_slice(&hex::decode("a2a6cef015bbce367e916d83152094d6492c422beb037edcbbd50593aa02b183").unwrap()).unwrap();
// let mut transparent_builder = transparent::builder::TransparentBuilder::empty();
// for vin in vin.iter() {
// transparent_builder.add_input(sk.clone(), vin.utxo.clone(), vin.coin.clone()).unwrap();
// }
// for vout in vout.iter() {
// transparent_builder.add_output(&vout.recipient_address().unwrap(), vout.value).unwrap();
// }
// let transparent_bundle = transparent_builder.build();
// let transparent_bundle = {
// let consensus_branch_id =
// BranchId::for_height(&network, BlockHeight::from_u32(tx_plan.anchor_height));
// let version = TxVersion::suggested_for_branch(consensus_branch_id);
// let unauthed_tx: TransactionData<zcash_primitives::transaction::Unauthorized> =
// TransactionData::from_parts(
// version,
// consensus_branch_id,
// 0,
// BlockHeight::from_u32(tx_plan.expiry_height),
// transparent_bundle.clone(),
// None,
// None,
// None,
// );
// let txid_parts = unauthed_tx.digest(TxIdDigester);
// transparent_bundle.unwrap().apply_signatures(&unauthed_tx, &txid_parts)
// };
// println!("VIN 2 {:?}", &transparent_bundle.vin[0]);
let transparent_bundle = transparent::Bundle::<transparent::Authorized> {
vin: vins,
vout,
@ -405,12 +506,9 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
let value: i64 = value_balance.try_into().unwrap();
let value = Amount::from_i64(value).unwrap();
let sighash = ledger_get_sighash().await?;
println!("TXID {}", hex::encode(&sighash));
log::info!("TXID {}", hex::encode(&sighash));
let binding_sig = sapling_context.binding_sig(value, &sighash.try_into().unwrap()).unwrap();
println!("{} {}", has_transparent, has_sapling);
println!("{} {} {:?}", shielded_spends.len(), shielded_outputs.len(), value_balance);
let sapling_bundle = Bundle::<_>::from_parts(
shielded_spends, shielded_outputs, value,
SapAuthorized { binding_sig } );
@ -430,11 +528,13 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
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);
log::info!("{}", response.error_message);
Ok(response.error_message)
}

View File

@ -1,7 +1,8 @@
use super::transport::*;
use anyhow::Result;
async fn unit_tests() -> Result<()> {
#[allow(dead_code)]
pub async fn unit_tests() -> Result<()> {
let hash = ledger_pedersen_hash(
&hex::decode("B315693B486D4D3CD8E4256E8C37CA4E8EC367E4D95D5C314625DC7B44B57EA2CA18FDCFF5871906F4238FB315693B486D4D3CD8E4256E8C37CA4E8EC367E4D95D5C314625DC7B44B57EA2")?
).await?;

View File

@ -13,9 +13,19 @@ use zcash_primitives::zip32::DiversifiableFullViewingKey;
use std::io::Write;
use hex_literal::hex;
async fn apdu(data: &[u8]) -> Vec<u8> {
let api = HidApi::new().unwrap();
let transport = TransportNativeHID::new(&api).unwrap();
fn handle_error_code(code: u16) -> Result<()> {
match code {
0x9000 => Ok(()),
0x6D02 => Err(anyhow!("Zcash Application NOT OPEN")),
0x6985 => Err(anyhow!("Tx REJECTED by User")),
0x5515 => Err(anyhow!("Ledger is LOCKED")),
_ => Err(anyhow!("Ledger device returned error code {:#06x}", code)),
}
}
async fn apdu_hid(data: &[u8]) -> Result<Vec<u8>> {
let api = HidApi::new()?;
let transport = TransportNativeHID::new(&api)?;
let command = APDUCommand {
cla: data[0],
ins: data[1],
@ -23,10 +33,12 @@ async fn apdu(data: &[u8]) -> Vec<u8> {
p2: data[3],
data: &data[5..],
};
println!("ins {}", data[1]);
let response = transport.exchange(&command).unwrap();
println!("ret {}", response.retcode());
response.data().to_vec()
log::info!("ins {}", data[1]);
let response = transport.exchange(&command)?;
let error_code = response.retcode();
log::info!("error_code {}", error_code);
handle_error_code(error_code)?;
Ok(response.data().to_vec())
}
const TEST_SERVER_IP: Option<&'static str> = option_env!("LEDGER_IP");
@ -36,19 +48,20 @@ async fn apdu_http(data: &[u8]) -> Vec<u8> {
.post(&format!("http://{}:5000/apdu", TEST_SERVER_IP.unwrap()))
.body(format!("{{\"data\": \"{}\"}}", hex::encode(data)))
.send()
.await
.unwrap();
let response_body: Value = response.json().await.unwrap();
let data = response_body["data"].as_str().unwrap();
let data = hex::decode(data).unwrap();
data[..data.len()-2].to_vec()
.await?;
let response_body: Value = response.json().await?;
let data = response_body["data"].as_str().ok_or(anyhow!("No data field"))?;
let data = hex::decode(data)?;
let error_code = u16::from_be_bytes(data[data.len()-2..].try_into().unwrap());
handle_error_code(error_code)?;
Ok(data[..data.len()-2].to_vec())
}
pub async fn ledger_init() -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.clear();
bb.write_all(&hex!("E005000000"))?;
apdu(&bb).await;
apdu(&bb).await?;
Ok(())
}
@ -56,7 +69,7 @@ pub async fn ledger_init() -> Result<()> {
pub async fn ledger_get_dfvk() -> Result<DiversifiableFullViewingKey> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E006000000"))?;
let dfvk_vec = apdu(&bb).await;
let dfvk_vec = apdu(&bb).await?;
let mut dfvk = [0; 128];
dfvk.copy_from_slice(&dfvk_vec);
@ -64,18 +77,141 @@ pub async fn ledger_get_dfvk() -> Result<DiversifiableFullViewingKey> {
Ok(dfvk)
}
pub async fn ledger_get_address() -> Result<String> {
pub async fn ledger_get_pubkey() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E007000000"))?;
let address = apdu(&bb).await;
let address = String::from_utf8_lossy(&address);
Ok(address.to_string())
let pk = apdu(&bb).await?;
Ok(pk)
}
pub async fn ledger_get_o_fvk() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E008000000"))?;
let pk = apdu(&bb).await?;
Ok(pk)
}
pub async fn ledger_init_tx(header_digest: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E010000020"))?;
bb.write_all(header_digest)?;
let main_seed = apdu(&bb).await?;
Ok(main_seed)
}
pub async fn ledger_set_stage(stage: u8) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E011"))?;
bb.write_u8(stage)?;
bb.write_all(&hex!("0000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_transparent_merkle_proof(prevouts_digest: &[u8], pubscripts_digest: &[u8], sequences_digest: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E012000060"))?;
bb.write_all(prevouts_digest)?;
bb.write_all(pubscripts_digest)?;
bb.write_all(sequences_digest)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_sapling_merkle_proof(spends_digest: &[u8], memos_digest: &[u8], outputs_nc_digest: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E013000060"))?;
bb.write_all(spends_digest)?;
bb.write_all(memos_digest)?;
bb.write_all(outputs_nc_digest)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_orchard_merkle_proof(anchor: &[u8], memos_digest: &[u8], outputs_nc_digest: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E014000060"))?;
bb.write_all(anchor)?;
bb.write_all(memos_digest)?;
bb.write_all(outputs_nc_digest)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_t_input(amount: u64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E015000008"))?;
bb.write_u64::<LE>(amount)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_t_output(amount: u64, address: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E01601001D"))?;
bb.write_u64::<LE>(amount)?;
bb.write_all(address)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_s_output(amount: u64, epk: &[u8], address: &[u8], enc_compact: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E017010087"))?;
bb.write_all(address)?;
bb.write_u64::<LE>(amount)?;
bb.write_all(epk)?;
bb.write_all(enc_compact)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_add_o_action(nf: &[u8], amount: u64, epk: &[u8], address: &[u8], enc_compact: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0180100A7"))?;
bb.write_all(nf)?;
bb.write_all(address)?;
bb.write_u64::<LE>(amount)?;
bb.write_all(epk)?;
bb.write_all(enc_compact)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_net_sapling(net: i64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E019000008"))?;
bb.write_i64::<LE>(net)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_set_net_orchard(net: i64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E01A000008"))?;
bb.write_i64::<LE>(net)?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_confirm_fee() -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E01B000000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_get_sighash() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E020000000"))?;
let sighash = apdu(&bb).await?;
Ok(sighash)
}
pub async fn ledger_get_proofgen_key() -> Result<ProofGenerationKey> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E011000000"))?;
let proofgen_key = apdu(&bb).await;
bb.write_all(&hex!("E021000000"))?;
let proofgen_key = apdu(&bb).await?;
let proofgen_key = ProofGenerationKey {
ak: SubgroupPoint::from_bytes(proofgen_key[0..32].try_into().unwrap()).unwrap(),
nsk: Fr::from_bytes(proofgen_key[32..64].try_into().unwrap()).unwrap(),
@ -83,106 +219,26 @@ pub async fn ledger_get_proofgen_key() -> Result<ProofGenerationKey> {
Ok(proofgen_key)
}
pub async fn ledger_init_tx(header_digest: &[u8]) -> Result<Vec<u8>> {
pub async fn ledger_sign_transparent(txin_digest: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E008000020"))?;
bb.write_all(header_digest)?;
let main_seed = apdu(&bb).await;
Ok(main_seed)
}
pub async fn ledger_add_t_input(amount: u64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E009000008"))?;
bb.write_u64::<LE>(amount)?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_add_t_output(amount: u64, address: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E00A00001D"))?;
bb.write_u64::<LE>(amount)?;
bb.write_all(address)?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_add_s_output(amount: u64, epk: &[u8], address: &[u8], enc_compact: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E00B000087"))?;
bb.write_all(address)?;
bb.write_u64::<LE>(amount)?;
bb.write_all(epk)?;
bb.write_all(enc_compact)?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_set_transparent_merkle_proof(prevouts_digest: &[u8], pubscripts_digest: &[u8], sequences_digest: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E00D000060"))?;
bb.write_all(prevouts_digest)?;
bb.write_all(pubscripts_digest)?;
bb.write_all(sequences_digest)?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_set_sapling_merkle_proof(spends_digest: &[u8], memos_digest: &[u8], outputs_nc_digest: &[u8]) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E00E000060"))?;
bb.write_all(spends_digest)?;
bb.write_all(memos_digest)?;
bb.write_all(outputs_nc_digest)?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_set_net_sapling(net: i64) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E00C000008"))?;
bb.write_i64::<LE>(net)?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_set_stage(stage: u8) -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E00F"))?;
bb.write_u8(stage)?;
bb.write_all(&hex!("0000"))?;
apdu(&bb).await;
Ok(())
}
pub async fn ledger_get_sighash() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E010000000"))?;
let sighash = apdu(&bb).await;
Ok(sighash)
bb.write_all(&hex!("E022000020"))?;
bb.write_all(txin_digest)?;
let signature = apdu(&bb).await?;
Ok(signature)
}
pub async fn ledger_sign_sapling() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E012000000"))?;
let signature = apdu(&bb).await;
bb.write_all(&hex!("E023000000"))?;
let signature = apdu(&bb).await?;
Ok(signature)
}
pub async fn ledger_sign_transparent(txin_digest: &[u8]) -> Result<Vec<u8>> {
pub async fn ledger_end_tx() -> Result<()> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E014000020"))?;
bb.write_all(txin_digest)?;
let signature = apdu(&bb).await;
Ok(signature)
}
pub async fn ledger_get_pubkey() -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E013000000"))?;
let pk = apdu(&bb).await;
Ok(pk)
bb.write_all(&hex!("E030000000"))?;
apdu(&bb).await?;
Ok(())
}
pub async fn ledger_cmu(data: &[u8]) -> Result<Vec<u8>> {
@ -190,7 +246,7 @@ pub async fn ledger_cmu(data: &[u8]) -> Result<Vec<u8>> {
bb.write_all(&hex!("E0800000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let cmu = apdu(&bb).await;
let cmu = apdu(&bb).await?;
Ok(cmu)
}
@ -199,7 +255,7 @@ pub async fn ledger_jubjub_hash(data: &[u8]) -> Result<Vec<u8>> {
bb.write_all(&hex!("E0810000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let cmu = apdu(&bb).await;
let cmu = apdu(&bb).await?;
Ok(cmu)
}
@ -208,6 +264,15 @@ pub async fn ledger_pedersen_hash(data: &[u8]) -> Result<Vec<u8>> {
bb.write_all(&hex!("E0820000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let cmu = apdu(&bb).await;
let cmu = apdu(&bb).await?;
Ok(cmu)
}
pub async fn ledger_test_math(data: &[u8]) -> Result<Vec<u8>> {
let mut bb: Vec<u8> = vec![];
bb.write_all(&hex!("E0FF0000"))?;
bb.write_u8(data.len() as u8)?;
bb.write_all(data)?;
let res = apdu(&bb).await?;
Ok(res)
}

View File

@ -102,7 +102,7 @@ mod zip32;
pub mod api;
#[cfg(feature = "ledger")]
mod ledger;
pub mod ledger;
pub use crate::chain::{connect_lightwalletd, get_best_server, ChainError};
pub use crate::coinconfig::{
@ -121,7 +121,8 @@ pub use note_selection::{
build_tx, build_tx_plan, fetch_utxos, get_secret_keys, TransactionBuilderConfig,
TransactionBuilderError, TransactionPlan, TxBuilderContext, MAX_ATTEMPTS,
};
pub use unified::{decode_unified_address, get_unified_address};
pub use crate::orchard::decode_merkle_path as decode_orchard_merkle_path;
pub use crate::unified::{decode_unified_address, get_unified_address};
#[cfg(feature = "nodejs")]
pub mod nodejs;
@ -134,7 +135,4 @@ pub fn init_test() {
set_coin_lwd_url(0, "http://127.0.0.1:9067");
}
#[cfg(feature = "ledger")]
pub use ledger::{build_broadcast_tx};
pub use taddr::derive_from_secretkey;

View File

@ -1,20 +1,39 @@
use std::{
fs::File,
io::Read,
io::{Read, Write},
path::Path,
};
use blake2b_simd::Params;
use byteorder::{WriteBytesExt, LE};
use group::{Group, GroupEncoding};
use halo2_proofs::{pasta::pallas::{self}};
use orchard::{keys::{Scope, SpendValidatingKey}, note::{RandomSeed, Nullifier, ExtractedNoteCommitment}, bundle::{Flags, Authorized}, tree::MerklePath, value::{ValueCommitTrapdoor, ValueCommitment, ValueSum}, circuit::{Circuit, ProvingKey}, primitives::redpallas::{Signature, SpendAuth}, Action, builder::{SpendInfo}, Proof};
use rand::{SeedableRng, RngCore};
use rand_chacha::ChaCha20Rng;
use ripemd::Digest;
use secp256k1::SecretKey;
use warp_api_ffi::{TransactionPlan, connect_lightwalletd, build_broadcast_tx, derive_from_secretkey};
use warp_api_ffi::{TransactionPlan, connect_lightwalletd, ledger::{build_broadcast_tx, ledger_init, ledger_set_stage, ledger_init_tx, ledger_add_o_action, ledger_set_orchard_merkle_proof, ledger_confirm_fee, ledger_set_net_orchard}, decode_orchard_merkle_path};
use anyhow::Result;
use zcash_client_backend::{encoding::encode_transparent_address, address::RecipientAddress};
use zcash_primitives::{legacy::TransparentAddress, consensus::{Parameters, Network, Network::MainNetwork}};
use zcash_primitives::{consensus::{Network::MainNetwork, BlockHeight, BranchId}, transaction::{TransactionData, TxVersion, txid::TxIdDigester, sighash_v5, sighash::SignableInput, components::Amount}};
use zcash_proofs::prover::LocalTxProver;
#[tokio::main]
async fn main() -> Result<()> {
let network: &Network = &MainNetwork;
use group::ff::Field;
use nonempty::NonEmpty;
use hex_literal::hex;
use warp_api_ffi::{Source, Destination};
mod orchard_bundle;
#[tokio::main]
async fn main() {
orchard_bundle::build_orchard().await.unwrap();
}
#[tokio::main]
async fn main2() -> Result<()> {
let params_dir = Path::new(&std::env::var("HOME").unwrap()).join(".zcash-params");
let prover = LocalTxProver::new(
&params_dir.join("sapling-spend.params"),
@ -27,7 +46,243 @@ async fn main() -> Result<()> {
let mut client = connect_lightwalletd("https://lwdv3.zecwallet.co").await?;
build_broadcast_tx(&mut client, &tx_plan, &prover).await?;
build_broadcast_tx(&MainNetwork, &mut client, &tx_plan, &prover).await?;
Ok(())
}
const ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [
99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, 124,
24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183,
];
async fn main3() -> Result<()> {
dotenv::dotenv()?;
let spending_key = hex::decode(dotenv::var("SPENDING_KEY").unwrap()).unwrap();
let x = hex::decode("063b1e0d8b7f64bb2a9465903b46c9019b552951afa5d735817d449d1334c510").unwrap();
let expiry_height = 2_500_000;
let mut h = Params::new()
.hash_length(32)
.personal(b"ZTxIdHeadersHash")
.to_state();
h.update(&hex!("050000800a27a726b4d0d6c200000000"));
h.write_u32::<LE>(expiry_height)?;
let header_digest = h.finalize();
// let Q: Point = Point::hash_to_curve(Q_PERSONALIZATION)(MERKLE_CRH_PERSONALIZATION.as_bytes());
// let p = <Ep as GroupEncoding>::from_bytes(&ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES).unwrap();
// println!("p {:?}", p);
// let q = Ep::identity() + p;
// println!("q {:?}", q);
// let p = p.double();
// println!("{:?}", p);
// println!("{}", hex::encode(ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES));
let sk = orchard::keys::SpendingKey::from_bytes(spending_key.try_into().unwrap()).unwrap();
let fvk = orchard::keys::FullViewingKey::from(&sk);
println!("FVK {:?}", fvk);
println!("FVK {}", hex::encode(fvk.to_bytes()));
// let ivk = fvk.to_ivk(Scope::External);
// println!("IVK {:?}", ivk);
// println!("DK {:?}", hex::encode(ivk.dk.to_bytes()));
// let rho = Nullifier::dummy(&mut OsRng);
// println!("rho {}", hex::encode(rho.to_bytes()));
let rho = Nullifier::from_bytes(&x.clone().try_into().unwrap()).unwrap();
let mut prng = ChaCha20Rng::from_seed([0; 32]);
let rcv = ValueCommitTrapdoor::random(&mut prng);
let mut alpha_rng = ChaCha20Rng::from_seed([1; 32]);
let mut rseed_rng = ChaCha20Rng::from_seed([2; 32]);
let alpha = pallas::Scalar::random(&mut alpha_rng);
let ak: SpendValidatingKey = fvk.clone().into();
let rk = ak.randomize(&alpha);
let rk_bytes: [u8; 32] = rk.clone().0.into();
let v_net = orchard::value::ValueSum::from_raw(-1000);
let cv_net = orchard::value::ValueCommitment::derive(v_net, rcv.clone());
let mut rseed = [0u8; 32];
rseed_rng.fill_bytes(&mut rseed);
println!("rseed {}", hex::encode(&rseed));
let rseed = RandomSeed::from_bytes(rseed, &rho).unwrap();
println!("esk {:?}", &rseed.esk(&rho));
println!("psi {:?}", &rseed.psi(&rho));
println!("rcm {:?}", &rseed.rcm(&rho));
let address = fvk.address_at(0u64, Scope::External);
let mut buf = vec![];
buf.write_all(&rho.to_bytes())?;
buf.write_all(&address.to_raw_address_bytes())?;
buf.write_u64::<LE>(400_000)?;
let note = orchard::Note::from_parts(address,
orchard::value::NoteValue::from_raw(400_000), rho, rseed).unwrap();
println!("note {:?}", note);
let cmx: ExtractedNoteCommitment = note.commitment().into();
println!("cmx {:?}", cmx);
// ledger_test_math(&buf).await?;
let mut memo = [0u8; 512];
memo[0] = 0xF6;
let encryptor = orchard::note_encryption::OrchardNoteEncryption::new(
None,
note.clone(),
address.clone(),
memo);
let epk = encryptor.epk().to_bytes().0;
let enc = encryptor.encrypt_note_plaintext();
let out = encryptor.encrypt_outgoing_plaintext(&cv_net.clone(), &cmx, &mut prng);
let encrypted_note = orchard::note::TransmittedNoteCiphertext {
epk_bytes: epk.clone(),
enc_ciphertext: enc.clone(),
out_ciphertext: out.clone(),
};
let merkle_path = MerklePath::dummy(&mut prng);
let anchor = merkle_path.root(cmx);
let _authorization = zcash_primitives::transaction::components::orchard::Unauthorized {};
let action =
orchard::Action::from_parts(rho.clone(), rk, cmx, encrypted_note, cv_net.clone(),
Signature::<SpendAuth>::from([0; 64]));
let _actions = NonEmpty::new(action);
let _circuit = Circuit::from_action_context_unchecked(orchard::builder::SpendInfo::new(
fvk.clone(), note.clone(), merkle_path).unwrap(), note, alpha, rcv.clone());
let mut orchard_memos_hasher = Params::new()
.hash_length(32)
.personal(b"ZTxIdOrcActMHash")
.to_state();
orchard_memos_hasher.update(&enc[52..564]);
let orchard_memos_hash = orchard_memos_hasher.finalize();
let mut orchard_nc_hasher = Params::new()
.hash_length(32)
.personal(b"ZTxIdOrcActNHash")
.to_state();
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);
let orchard_nc_hash = orchard_nc_hasher.finalize();
// let mut orchard_builder = orchard::builder::Builder::new(Flags::from_byte(3).unwrap(),
// anchor);
// orchard_builder.add_spend(fvk.clone(), note, merkle_path).unwrap();
// orchard_builder.add_recipient(None, address.clone(),
// orchard::value::NoteValue::from_raw(399_000), None).unwrap();
// let bundle: orchard::Bundle<orchard::builder::InProgress<orchard::builder::Unproven, orchard::builder::Unauthorized>, _> = orchard_builder.build(&mut prng).unwrap();
let _bsk = rcv.into_bsk();
let _pk = ProvingKey::build();
// let instance = action.to_instance(Flags::from_parts(true, true), anchor.clone());
// let bundle = orchard::Bundle::from_parts(actions,
// Flags::from_byte(3).unwrap(),
// Amount::from_i64(-1000).unwrap(),
// anchor,
// InProgress::<Unproven, OrchardUnauthorized> {
// proof: Unproven { circuits: vec![circuit] },
// sigs: OrchardUnauthorized { bsk }
// }
// );
// let proof = bundle.create_proof(&pk, &mut prng).unwrap();
// let proof = proof.authorization();
// let proof = &proof.proof;
// let bundle = orchard::Bundle::from_parts(actions,
// Flags::from_byte(3).unwrap(),
// Amount::from_i64(-1000).unwrap(),
// anchor,
// orchard::bundle::Authorized {
// proof: todo!(),
// binding_signature: todo!(),
// }
// );
let tx_data: TransactionData<zcash_primitives::transaction::Unauthorized> = TransactionData {
version: TxVersion::Zip225,
consensus_branch_id: BranchId::Nu5,
lock_time: 0,
expiry_height: BlockHeight::from_u32(expiry_height),
transparent_bundle: None,
sprout_bundle: None,
sapling_bundle: None,
orchard_bundle: None,
};
let txid_parts = tx_data.digest(TxIdDigester);
let sig_hash = sighash_v5::v5_signature_hash(&tx_data, &SignableInput::Shielded, &txid_parts);
println!("ORCHARD memos {:?}", orchard_memos_hash);
println!("ORCHARD nc {:?}", orchard_nc_hash);
println!("SIGHASH PARTS {:?}", txid_parts);
println!("SIGHASH {:?}", sig_hash);
ledger_init().await.unwrap();
ledger_init_tx(header_digest.as_bytes()).await.unwrap();
ledger_set_orchard_merkle_proof(&anchor.to_bytes(),
orchard_memos_hash.as_bytes(), orchard_nc_hash.as_bytes()).await.unwrap();
// no t-in
ledger_set_stage(2).await.unwrap();
// no t-out
ledger_set_stage(3).await.unwrap();
// no s-out
ledger_set_stage(4).await.unwrap();
ledger_add_o_action(&x, 400_000, &epk,
&address.to_raw_address_bytes(), &enc[0..52]).await.unwrap();
ledger_set_stage(5).await.unwrap();
ledger_set_net_orchard(-1000).await.unwrap();
ledger_confirm_fee().await.unwrap();
// println!("address {}", hex::encode(address.to_raw_address_bytes()));
// let esk = ExtendedSpendingKey::master(&[1; 32]);
// let (_, pa) = esk.default_address();
// let ta = TransparentAddress::PublicKey([1; 20]);
// let ua = UnifiedAddress::from_receivers(
// Some(address),
// Some(pa),
// Some(ta),
// ).unwrap();
// let ua = ua.encode(&MainNetwork);
// println!("UA {}", ua);
// let mut message = [1u8; 128];
// f4jumble::f4jumble_mut(&mut message[..]).unwrap();
// println!("f4 {}", hex::encode(message));
// println!("{}", hex::encode(address.to_raw_address_bytes()));
// let a = zcash_address::ZcashAddress::try_from_encoded("u1hlpt22xwe3sy034cdjhtnlp4l39zwa7xsj6tsxq0d2p9mv0gzsxnzkpc3wxpv6nh9s2kyxe54qnnxujxc8wqjemvlelzsdxlm7feqdjuksgpx45w3we563apmtmxhql6aa584u9569pdaq3q8h9p8gma67z5td3sckhamh99aqkgf3cg76rykn2e2pwxdjm8wdya0w39355rgvhxvpw").unwrap();
// if let zcash_address::AddressKind::Unified(r) = a.kind {
// let Address(rs) = r;
// let r = &rs[0];
// if let Receiver::Orchard(address) = r {
// println!("address {}", hex::encode(address));
// }
// }
// println!("a {:?}", a);
// ledger_init().await?;
// ledger_test_math().await?;
// let fvk = ledger_get_o_fvk().await?;
// println!("FVK {}", hex::encode(&fvk));
Ok(())
}

View File

@ -13,8 +13,7 @@ use orchard::keys::{FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey};
use orchard::note::Nullifier;
use orchard::value::NoteValue;
use orchard::{Address, Anchor, Bundle};
use rand::{CryptoRng, RngCore, SeedableRng};
use rand_chacha::ChaChaRng;
use rand::{CryptoRng, RngCore};
use ripemd::{Digest, Ripemd160};
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
use sha2::Sha256;
@ -100,7 +99,6 @@ pub fn build_tx(
};
let mut has_orchard = false;
let mut rng = ChaChaRng::from_seed([0; 32]);
let mut builder = Builder::new_with_rng(*network, BlockHeight::from_u32(plan.anchor_height), &mut rng);
let anchor: Anchor = orchard::tree::MerkleHashOrchard::from_bytes(&plan.orchard_anchor)
.unwrap()

View File

@ -290,6 +290,7 @@ pub fn build_tx_plan<F: FeeCalculator>(
network: &Network,
fvk: &str,
taddr: &str,
orchard_fvk: &str,
anchor_height: u32,
expiry_height: u32,
orchard_anchor: &Option<Hash>,
@ -323,6 +324,7 @@ pub fn build_tx_plan<F: FeeCalculator>(
let tx_plan = TransactionPlan {
fvk: fvk.to_string(),
taddr: taddr.to_string(),
orchard_fvk: orchard_fvk.to_string(),
anchor_height,
expiry_height,
orchard_anchor: orchard_anchor.unwrap_or(Hash::default()),

View File

@ -157,8 +157,9 @@ pub struct RecipientShort {
#[derive(Serialize, Deserialize, Default)]
#[serde_as]
pub struct TransactionPlan {
pub fvk: String,
pub taddr: String,
pub fvk: String,
pub orchard_fvk: String,
pub anchor_height: u32,
pub expiry_height: u32,
#[serde(with = "SerHex::<Strict>")]

View File

@ -12,7 +12,7 @@ mod note;
pub use hash::{OrchardHasher, ORCHARD_ROOTS};
pub use key::{derive_orchard_keys, OrchardKeyBytes};
pub use note::{DecryptedOrchardNote, OrchardDecrypter, OrchardViewKey};
pub use note::{DecryptedOrchardNote, OrchardDecrypter, OrchardViewKey, decode_merkle_path};
pub fn get_proving_key() -> &'static ProvingKey {
if !PROVING_KEY.filled() {

View File

@ -1,7 +1,7 @@
use crate::chain::Nf;
use crate::db::ReceivedNote;
use crate::sync::{
CompactOutputBytes, DecryptedNote, Node, OutputPosition, TrialDecrypter, ViewKey,
CompactOutputBytes, DecryptedNote, Node, OutputPosition, TrialDecrypter, ViewKey, Witness,
};
use crate::CompactTx;
use orchard::keys::PreparedIncomingViewingKey;
@ -110,3 +110,17 @@ impl<N: Parameters> TrialDecrypter<N, OrchardDomain, OrchardViewKey, DecryptedOr
vtx.actions.iter().map(|co| co.into()).collect()
}
}
pub fn decode_merkle_path(id_note: u32, witness: &[u8]) -> anyhow::Result<orchard::tree::MerklePath> {
let witness = Witness::from_bytes(id_note, witness)?;
let auth_path: Vec<_> = witness
.auth_path(32, &super::ORCHARD_ROOTS, &super::OrchardHasher::new())
.iter()
.map(|n| orchard::tree::MerkleHashOrchard::from_bytes(n).unwrap())
.collect();
let merkle_path = orchard::tree::MerklePath::from_parts(
witness.position as u32,
auth_path.try_into().unwrap(),
);
Ok(merkle_path)
}

View File

@ -423,18 +423,16 @@ pub async fn broadcast_tx(tx: &[u8]) -> anyhow::Result<String> {
height: latest_height as u64,
};
return Err(anyhow!("Broadcasting is disabled"));
// let rep = client
// .send_transaction(Request::new(raw_tx))
// .await?
// .into_inner();
// let code = rep.error_code;
// if code == 0 {
// Ok(rep.error_message)
// } else {
// Err(anyhow::anyhow!(rep.error_message))
// }
let rep = client
.send_transaction(Request::new(raw_tx))
.await?
.into_inner();
let code = rep.error_code;
if code == 0 {
Ok(rep.error_message)
} else {
Err(anyhow::anyhow!(rep.error_message))
}
}
pub fn get_tx_summary(tx: &Tx) -> anyhow::Result<TxSummary> {

View File

@ -268,6 +268,19 @@ pub fn derive_from_secretkey(
Ok((sk, address))
}
pub fn derive_from_pubkey(network: &Network, pub_key: &[u8]) -> anyhow::Result<String> {
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,
);
Ok(address)
}
pub async fn sweep_tkey(
last_height: u32,
sk: &str,

View File

@ -42,6 +42,9 @@ impl std::fmt::Display for DecodedUA {
}
}
/*
* It can also return a t-addr if there is no other selection
*/
pub fn get_unified_address(
network: &Network,
db: &DbAdapter,
@ -59,8 +62,8 @@ pub fn get_unified_address(
}
if !tpe.sapling && !tpe.orchard {
// UA cannot be t-only
tpe.sapling = true;
tpe.orchard = true;
let address = db.get_taddr(account)?.ok_or(anyhow!("No taddr"))?;
return Ok(address)
}
let address = match (tpe.transparent, tpe.sapling, tpe.orchard) {