Ledger App integration
This commit is contained in:
parent
d3d8d27538
commit
f855d7ca81
22
Cargo.toml
22
Cargo.toml
|
@ -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" }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
¶ms_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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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>")]
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
22
src/pay.rs
22
src/pay.rs
|
@ -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> {
|
||||
|
|
13
src/taddr.rs
13
src/taddr.rs
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue