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"]
|
crate-type = ["rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nonempty = "0.7"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
anyhow = "1.0.40"
|
anyhow = "1.0.40"
|
||||||
thiserror = "1.0.25"
|
thiserror = "1.0.25"
|
||||||
|
@ -75,6 +76,8 @@ chrono = "0.4.19"
|
||||||
lazycell = "1.3.0"
|
lazycell = "1.3.0"
|
||||||
reqwest = { version = "0.11.4", features = ["json", "rustls-tls"], default-features = false }
|
reqwest = { version = "0.11.4", features = ["json", "rustls-tls"], default-features = false }
|
||||||
hex-literal = "0.4"
|
hex-literal = "0.4"
|
||||||
|
pasta_curves = "0.5"
|
||||||
|
f4jumble = { path = "../../librustzcash/components/f4jumble" }
|
||||||
|
|
||||||
# Halo
|
# Halo
|
||||||
orchard = "0.3.0"
|
orchard = "0.3.0"
|
||||||
|
@ -108,7 +111,7 @@ objc = { version = "0.2", features = [ "objc_exception" ], optional = true }
|
||||||
block = { version = "0.1.6", optional = true }
|
block = { version = "0.1.6", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
ledger = ["ledger-apdu", "ledger-transport-hid"]
|
ledger = ["ledger-apdu", "ledger-transport-hid", "dotenv"]
|
||||||
dart_ffi = ["allo-isolate", "once_cell", "android_logger"]
|
dart_ffi = ["allo-isolate", "once_cell", "android_logger"]
|
||||||
rpc = ["rocket", "dotenv"]
|
rpc = ["rocket", "dotenv"]
|
||||||
nodejs = ["node-bindgen"]
|
nodejs = ["node-bindgen"]
|
||||||
|
@ -118,38 +121,38 @@ sqlcipher = ["rusqlite/bundled-sqlcipher-vendored-openssl"]
|
||||||
|
|
||||||
[dependencies.zcash_params]
|
[dependencies.zcash_params]
|
||||||
#git = "https://github.com/hhanh00/zcash-params.git"
|
#git = "https://github.com/hhanh00/zcash-params.git"
|
||||||
#rev = "154f3544781500c27f58923ccc6c7e779eecf9df"
|
#rev = "2e118feceeaa31ef68fb83d1fc94a1a46db4569c"
|
||||||
path = "../zcash-params"
|
path = "../zcash-params"
|
||||||
|
|
||||||
[dependencies.zcash_client_backend]
|
[dependencies.zcash_client_backend]
|
||||||
#git = "https://github.com/hhanh00/librustzcash.git"
|
#git = "https://github.com/hhanh00/librustzcash.git"
|
||||||
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
|
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
|
||||||
path = "../../librustzcash/zcash_client_backend"
|
path = "../../librustzcash/zcash_client_backend"
|
||||||
|
|
||||||
[dependencies.zcash_primitives]
|
[dependencies.zcash_primitives]
|
||||||
#git = "https://github.com/hhanh00/librustzcash.git"
|
#git = "https://github.com/hhanh00/librustzcash.git"
|
||||||
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
|
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
|
||||||
path = "../../librustzcash/zcash_primitives"
|
path = "../../librustzcash/zcash_primitives"
|
||||||
features = [ "transparent-inputs" ]
|
features = [ "transparent-inputs" ]
|
||||||
|
|
||||||
[dependencies.zcash_proofs]
|
[dependencies.zcash_proofs]
|
||||||
#git = "https://github.com/hhanh00/librustzcash.git"
|
#git = "https://github.com/hhanh00/librustzcash.git"
|
||||||
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
|
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
|
||||||
path = "../../librustzcash/zcash_proofs"
|
path = "../../librustzcash/zcash_proofs"
|
||||||
|
|
||||||
[dependencies.zcash_address]
|
[dependencies.zcash_address]
|
||||||
#git = "https://github.com/hhanh00/librustzcash.git"
|
#git = "https://github.com/hhanh00/librustzcash.git"
|
||||||
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
|
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
|
||||||
path = "../../librustzcash/components/zcash_address"
|
path = "../../librustzcash/components/zcash_address"
|
||||||
|
|
||||||
[dependencies.zcash_encoding]
|
[dependencies.zcash_encoding]
|
||||||
#git = "https://github.com/hhanh00/librustzcash.git"
|
#git = "https://github.com/hhanh00/librustzcash.git"
|
||||||
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
|
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
|
||||||
path = "../../librustzcash/components/zcash_encoding"
|
path = "../../librustzcash/components/zcash_encoding"
|
||||||
|
|
||||||
[dependencies.zcash_note_encryption]
|
[dependencies.zcash_note_encryption]
|
||||||
#git = "https://github.com/hhanh00/librustzcash.git"
|
#git = "https://github.com/hhanh00/librustzcash.git"
|
||||||
#rev = "f39dd6c2af20662fd1aaa9e00172efebd3435ff4"
|
#rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646"
|
||||||
path = "../../librustzcash/components/zcash_note_encryption"
|
path = "../../librustzcash/components/zcash_note_encryption"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -161,5 +164,4 @@ criterion = "0.3.4"
|
||||||
|
|
||||||
#These patch overrides must be included in your workspace root Cargo.toml
|
#These patch overrides must be included in your workspace root Cargo.toml
|
||||||
#[patch.crates-io]
|
#[patch.crates-io]
|
||||||
#zcash_note_encryption = { git = "https://github.com/hhanh00/librustzcash.git", rev = "ad4a1c61fdaf04ac4fb884976ad175196e695264" }
|
#zcash_note_encryption = { git = "https://github.com/hhanh00/librustzcash.git", rev = "e2fe0b8d386fad99e00d6135c5caf3cc04045646" }
|
||||||
#jubjub = { git = "https://github.com/hhanh00/jubjub.git", rev = "4a3edf3d242f368b5aa418ec659d01f191127cf3" }
|
|
||||||
|
|
|
@ -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_____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_cuda(void);
|
||||||
|
|
||||||
bool has_metal(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::data_generated::fb::{ProgressT, Recipients, SendTemplate, Servers};
|
||||||
use crate::db::FullEncryptedBackup;
|
use crate::db::FullEncryptedBackup;
|
||||||
use crate::note_selection::TransactionReport;
|
use crate::note_selection::TransactionReport;
|
||||||
use crate::{ChainError, TransactionPlan, Tx, build_broadcast_tx};
|
use crate::{ChainError, TransactionPlan, Tx};
|
||||||
use allo_isolate::{ffi, IntoDart};
|
use allo_isolate::{ffi, IntoDart};
|
||||||
use android_logger::Config;
|
use android_logger::Config;
|
||||||
use flatbuffers::FlatBufferBuilder;
|
use flatbuffers::FlatBufferBuilder;
|
||||||
|
@ -1219,6 +1219,7 @@ pub unsafe extern "C" fn set_property(
|
||||||
to_cresult(with_coin(coin, res))
|
to_cresult(with_coin(coin, res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ledger")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async unsafe extern "C" fn ledger_send(coin: u8, tx_plan: *mut c_char) -> CResult<*mut c_char> {
|
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 c = CoinConfig::get(coin);
|
||||||
let mut client = c.connect_lwd().await?;
|
let mut client = c.connect_lwd().await?;
|
||||||
let prover = coinconfig::get_prover();
|
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)
|
Ok::<_, anyhow::Error>(response)
|
||||||
};
|
};
|
||||||
to_cresult_str(res.await)
|
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]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn has_cuda() -> bool {
|
pub unsafe extern "C" fn has_cuda() -> bool {
|
||||||
crate::gpu::has_cuda()
|
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 db = c.db()?;
|
||||||
let AccountData { fvk, .. } = db.get_account_info(account)?;
|
let AccountData { fvk, .. } = db.get_account_info(account)?;
|
||||||
let taddr = db.get_taddr(account)?.unwrap_or_default();
|
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 change_address = get_unified_address(coin, account, 7)?;
|
||||||
let context = TxBuilderContext::from_height(coin, checkpoint_height)?;
|
let context = TxBuilderContext::from_height(coin, checkpoint_height)?;
|
||||||
|
@ -79,6 +80,7 @@ pub async fn build_tx_plan_with_utxos(
|
||||||
network,
|
network,
|
||||||
&fvk,
|
&fvk,
|
||||||
&taddr,
|
&taddr,
|
||||||
|
&orchard_fvk,
|
||||||
checkpoint_height,
|
checkpoint_height,
|
||||||
expiry_height,
|
expiry_height,
|
||||||
&context.orchard_anchor,
|
&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 {
|
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 };
|
| if has_orchard { 4 } else { 0 };
|
||||||
Ok(res)
|
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 transport;
|
||||||
mod builder;
|
mod builder;
|
||||||
|
mod account;
|
||||||
|
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use builder::build_broadcast_tx;
|
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 blake2b_simd::Params;
|
||||||
use byteorder::WriteBytesExt;
|
use byteorder::WriteBytesExt;
|
||||||
use byteorder::LE;
|
use byteorder::LE;
|
||||||
|
@ -9,26 +5,30 @@ use ff::{PrimeField, Field};
|
||||||
use group::GroupEncoding;
|
use group::GroupEncoding;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use jubjub::{Fr, Fq};
|
use jubjub::{Fr, Fq};
|
||||||
|
|
||||||
|
|
||||||
|
use orchard::keys::Scope;
|
||||||
|
|
||||||
use rand::{rngs::OsRng, RngCore, SeedableRng};
|
use rand::{rngs::OsRng, RngCore, SeedableRng};
|
||||||
use rand_chacha::ChaChaRng;
|
use rand_chacha::ChaChaRng;
|
||||||
use ripemd::{Digest, Ripemd160};
|
use ripemd::{Digest, Ripemd160};
|
||||||
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
|
use secp256k1::PublicKey;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use tonic::{Request, transport::Channel};
|
use tonic::{Request, transport::Channel};
|
||||||
use zcash_client_backend::address::RecipientAddress;
|
use zcash_client_backend::encoding::{decode_transparent_address, encode_extended_full_viewing_key, encode_transparent_address};
|
||||||
use zcash_client_backend::encoding::decode_transparent_address;
|
use zcash_primitives::consensus::Network;
|
||||||
use zcash_primitives::consensus::Parameters;
|
use zcash_primitives::consensus::Parameters;
|
||||||
use zcash_primitives::legacy::{TransparentAddress, Script};
|
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::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::{Destination, Source, TransactionPlan, RawTransaction, CompactTxStreamerClient};
|
||||||
use crate::ledger::transport::*;
|
use crate::ledger::transport::*;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::{BlockHeight, BranchId, MainNetwork},
|
consensus::{BlockHeight, BranchId, MainNetwork},
|
||||||
merkle_tree::{Hashable, IncrementalWitness},
|
merkle_tree::IncrementalWitness,
|
||||||
sapling::{
|
sapling::{
|
||||||
note_encryption::sapling_note_encryption, value::{NoteValue, ValueCommitment, ValueSum}, Diversifier, Node, Note,
|
note_encryption::sapling_note_encryption, value::{NoteValue, ValueCommitment, ValueSum}, Diversifier, Node, Note,
|
||||||
PaymentAddress, Rseed, Nullifier, prover::TxProver, redjubjub::Signature,
|
PaymentAddress, Rseed, Nullifier, prover::TxProver, redjubjub::Signature,
|
||||||
|
@ -53,14 +53,40 @@ struct SpendDescriptionUnAuthorized {
|
||||||
zkproof: [u8; GROTH_PROOF_SIZE],
|
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?;
|
ledger_init().await?;
|
||||||
let pubkey = ledger_get_pubkey().await?;
|
let pubkey = ledger_get_pubkey().await?;
|
||||||
|
let ledger_taddr = derive_from_pubkey(network, &pubkey)?;
|
||||||
|
|
||||||
let network = MainNetwork; // TODO: Pass network as param
|
if ledger_taddr != tx_plan.taddr {
|
||||||
let secp = Secp256k1::<All>::new();
|
anyhow::bail!("This ledger wallet has a different address");
|
||||||
|
}
|
||||||
|
|
||||||
let taddr = &tx_plan.taddr;
|
let taddr = &tx_plan.taddr;
|
||||||
|
|
||||||
let taddr = decode_transparent_address(
|
let taddr = decode_transparent_address(
|
||||||
&network.b58_pubkey_address_prefix(),
|
&network.b58_pubkey_address_prefix(),
|
||||||
&network.b58_script_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 fvk = dfvk.fvk;
|
||||||
let nf_key = proofgen_key.to_viewing_key().nk;
|
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);
|
assert_eq!(PROOF_GENERATION_KEY_GENERATOR * proofgen_key.nsk, fvk.vk.nk.0);
|
||||||
|
|
||||||
// Derive rseed PRNG
|
// Derive rseed PRNG
|
||||||
|
@ -118,7 +146,6 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
|
||||||
.to_state();
|
.to_state();
|
||||||
h.update(&master_seed);
|
h.update(&master_seed);
|
||||||
let alpha = h.finalize();
|
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 alpha_rng = ChaChaRng::from_seed(alpha.as_bytes().try_into().unwrap());
|
||||||
|
|
||||||
let mut prevouts_hasher = Params::new()
|
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 });
|
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();
|
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();
|
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();
|
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();
|
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();
|
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()
|
let mut spends_hasher = Params::new()
|
||||||
.hash_length(32)
|
.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());
|
spends_hasher.update(spends_non_compact_digest.as_bytes());
|
||||||
}
|
}
|
||||||
let spends_digest = spends_hasher.finalize();
|
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()
|
let mut output_memos_hasher = Params::new()
|
||||||
.hash_length(32)
|
.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();
|
let has_transparent = !vin.is_empty() || !vout.is_empty();
|
||||||
|
|
||||||
for output in tx_plan.outputs.iter() {
|
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 note = Note::from_parts(recipient, value, rseed);
|
||||||
let rcm = note.rcm();
|
let rcm = note.rcm();
|
||||||
let cmu = note.cmu();
|
let cmu = note.cmu();
|
||||||
println!("cmu {}", hex::encode(cmu.to_bytes()));
|
log::info!("cmu {}", hex::encode(cmu.to_bytes()));
|
||||||
|
|
||||||
let encryptor =
|
let encryptor =
|
||||||
sapling_note_encryption::<_, MainNetwork>(Some(ovk.clone()), note, recipient, output.memo.clone(), &mut OsRng);
|
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 });
|
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();
|
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();
|
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(),
|
ledger_set_transparent_merkle_proof(prevouts_digest.as_bytes(),
|
||||||
pubscripts_digest.as_bytes(), sequences_digest.as_bytes()).await?;
|
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_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![];
|
let mut vins = vec![];
|
||||||
for tin in vin.iter() {
|
for tin in vin.iter() {
|
||||||
let mut txin_hasher = Params::new()
|
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(&tin.coin.script_pubkey.0);
|
||||||
txin_hasher.update(&0xFFFFFFFFu32.to_le_bytes());
|
txin_hasher.update(&0xFFFFFFFFu32.to_le_bytes());
|
||||||
let txin_hash = txin_hasher.finalize();
|
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 = ledger_sign_transparent(txin_hash.as_bytes()).await?;
|
||||||
let signature = secp256k1::ecdsa::Signature::from_der(&signature)?;
|
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,
|
script_sig,
|
||||||
sequence: 0xFFFFFFFFu32,
|
sequence: 0xFFFFFFFFu32,
|
||||||
};
|
};
|
||||||
println!("TXIN {:?}", txin);
|
|
||||||
vins.push(txin);
|
vins.push(txin);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut signatures = vec![];
|
let mut signatures = vec![];
|
||||||
for _sp in shielded_spends.iter() {
|
for _sp in shielded_spends.iter() {
|
||||||
let signature = ledger_sign_sapling().await?;
|
let signature = ledger_sign_sapling().await?;
|
||||||
println!("SIGNATURE {}", hex::encode(&signature));
|
|
||||||
let signature = Signature::read(&*signature)?;
|
let signature = Signature::read(&*signature)?;
|
||||||
// Signature verification
|
// Signature verification
|
||||||
// let rk = sp.rk();
|
// let rk = sp.rk();
|
||||||
|
@ -360,37 +492,6 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
|
||||||
signatures.push(signature);
|
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> {
|
let transparent_bundle = transparent::Bundle::<transparent::Authorized> {
|
||||||
vin: vins,
|
vin: vins,
|
||||||
vout,
|
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: i64 = value_balance.try_into().unwrap();
|
||||||
let value = Amount::from_i64(value).unwrap();
|
let value = Amount::from_i64(value).unwrap();
|
||||||
let sighash = ledger_get_sighash().await?;
|
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();
|
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(
|
let sapling_bundle = Bundle::<_>::from_parts(
|
||||||
shielded_spends, shielded_outputs, value,
|
shielded_spends, shielded_outputs, value,
|
||||||
SapAuthorized { binding_sig } );
|
SapAuthorized { binding_sig } );
|
||||||
|
@ -430,11 +528,13 @@ pub async fn build_broadcast_tx(client: &mut CompactTxStreamerClient<Channel>, t
|
||||||
let mut raw_tx = vec![];
|
let mut raw_tx = vec![];
|
||||||
tx.write_v5(&mut raw_tx)?;
|
tx.write_v5(&mut raw_tx)?;
|
||||||
|
|
||||||
|
ledger_end_tx().await?;
|
||||||
|
|
||||||
let response = client.send_transaction(Request::new(RawTransaction {
|
let response = client.send_transaction(Request::new(RawTransaction {
|
||||||
data: raw_tx,
|
data: raw_tx,
|
||||||
height: 0,
|
height: 0,
|
||||||
})).await?.into_inner();
|
})).await?.into_inner();
|
||||||
println!("{}", response.error_message);
|
log::info!("{}", response.error_message);
|
||||||
|
|
||||||
Ok(response.error_message)
|
Ok(response.error_message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use super::transport::*;
|
use super::transport::*;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
async fn unit_tests() -> Result<()> {
|
#[allow(dead_code)]
|
||||||
|
pub async fn unit_tests() -> Result<()> {
|
||||||
let hash = ledger_pedersen_hash(
|
let hash = ledger_pedersen_hash(
|
||||||
&hex::decode("B315693B486D4D3CD8E4256E8C37CA4E8EC367E4D95D5C314625DC7B44B57EA2CA18FDCFF5871906F4238FB315693B486D4D3CD8E4256E8C37CA4E8EC367E4D95D5C314625DC7B44B57EA2")?
|
&hex::decode("B315693B486D4D3CD8E4256E8C37CA4E8EC367E4D95D5C314625DC7B44B57EA2CA18FDCFF5871906F4238FB315693B486D4D3CD8E4256E8C37CA4E8EC367E4D95D5C314625DC7B44B57EA2")?
|
||||||
).await?;
|
).await?;
|
||||||
|
|
|
@ -13,9 +13,19 @@ use zcash_primitives::zip32::DiversifiableFullViewingKey;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
|
||||||
async fn apdu(data: &[u8]) -> Vec<u8> {
|
fn handle_error_code(code: u16) -> Result<()> {
|
||||||
let api = HidApi::new().unwrap();
|
match code {
|
||||||
let transport = TransportNativeHID::new(&api).unwrap();
|
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 {
|
let command = APDUCommand {
|
||||||
cla: data[0],
|
cla: data[0],
|
||||||
ins: data[1],
|
ins: data[1],
|
||||||
|
@ -23,10 +33,12 @@ async fn apdu(data: &[u8]) -> Vec<u8> {
|
||||||
p2: data[3],
|
p2: data[3],
|
||||||
data: &data[5..],
|
data: &data[5..],
|
||||||
};
|
};
|
||||||
println!("ins {}", data[1]);
|
log::info!("ins {}", data[1]);
|
||||||
let response = transport.exchange(&command).unwrap();
|
let response = transport.exchange(&command)?;
|
||||||
println!("ret {}", response.retcode());
|
let error_code = response.retcode();
|
||||||
response.data().to_vec()
|
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");
|
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()))
|
.post(&format!("http://{}:5000/apdu", TEST_SERVER_IP.unwrap()))
|
||||||
.body(format!("{{\"data\": \"{}\"}}", hex::encode(data)))
|
.body(format!("{{\"data\": \"{}\"}}", hex::encode(data)))
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
let response_body: Value = response.json().await?;
|
||||||
let response_body: Value = response.json().await.unwrap();
|
let data = response_body["data"].as_str().ok_or(anyhow!("No data field"))?;
|
||||||
let data = response_body["data"].as_str().unwrap();
|
let data = hex::decode(data)?;
|
||||||
let data = hex::decode(data).unwrap();
|
let error_code = u16::from_be_bytes(data[data.len()-2..].try_into().unwrap());
|
||||||
data[..data.len()-2].to_vec()
|
handle_error_code(error_code)?;
|
||||||
|
Ok(data[..data.len()-2].to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ledger_init() -> Result<()> {
|
pub async fn ledger_init() -> Result<()> {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.clear();
|
bb.clear();
|
||||||
bb.write_all(&hex!("E005000000"))?;
|
bb.write_all(&hex!("E005000000"))?;
|
||||||
apdu(&bb).await;
|
apdu(&bb).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -56,7 +69,7 @@ pub async fn ledger_init() -> Result<()> {
|
||||||
pub async fn ledger_get_dfvk() -> Result<DiversifiableFullViewingKey> {
|
pub async fn ledger_get_dfvk() -> Result<DiversifiableFullViewingKey> {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E006000000"))?;
|
bb.write_all(&hex!("E006000000"))?;
|
||||||
let dfvk_vec = apdu(&bb).await;
|
let dfvk_vec = apdu(&bb).await?;
|
||||||
let mut dfvk = [0; 128];
|
let mut dfvk = [0; 128];
|
||||||
dfvk.copy_from_slice(&dfvk_vec);
|
dfvk.copy_from_slice(&dfvk_vec);
|
||||||
|
|
||||||
|
@ -64,18 +77,141 @@ pub async fn ledger_get_dfvk() -> Result<DiversifiableFullViewingKey> {
|
||||||
Ok(dfvk)
|
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![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E007000000"))?;
|
bb.write_all(&hex!("E007000000"))?;
|
||||||
let address = apdu(&bb).await;
|
let pk = apdu(&bb).await?;
|
||||||
let address = String::from_utf8_lossy(&address);
|
Ok(pk)
|
||||||
Ok(address.to_string())
|
}
|
||||||
|
|
||||||
|
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> {
|
pub async fn ledger_get_proofgen_key() -> Result<ProofGenerationKey> {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E011000000"))?;
|
bb.write_all(&hex!("E021000000"))?;
|
||||||
let proofgen_key = apdu(&bb).await;
|
let proofgen_key = apdu(&bb).await?;
|
||||||
let proofgen_key = ProofGenerationKey {
|
let proofgen_key = ProofGenerationKey {
|
||||||
ak: SubgroupPoint::from_bytes(proofgen_key[0..32].try_into().unwrap()).unwrap(),
|
ak: SubgroupPoint::from_bytes(proofgen_key[0..32].try_into().unwrap()).unwrap(),
|
||||||
nsk: Fr::from_bytes(proofgen_key[32..64].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)
|
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![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E008000020"))?;
|
bb.write_all(&hex!("E022000020"))?;
|
||||||
bb.write_all(header_digest)?;
|
bb.write_all(txin_digest)?;
|
||||||
let main_seed = apdu(&bb).await;
|
let signature = apdu(&bb).await?;
|
||||||
Ok(main_seed)
|
Ok(signature)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ledger_sign_sapling() -> Result<Vec<u8>> {
|
pub async fn ledger_sign_sapling() -> Result<Vec<u8>> {
|
||||||
let mut bb: Vec<u8> = vec![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E012000000"))?;
|
bb.write_all(&hex!("E023000000"))?;
|
||||||
let signature = apdu(&bb).await;
|
let signature = apdu(&bb).await?;
|
||||||
Ok(signature)
|
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![];
|
let mut bb: Vec<u8> = vec![];
|
||||||
bb.write_all(&hex!("E014000020"))?;
|
bb.write_all(&hex!("E030000000"))?;
|
||||||
bb.write_all(txin_digest)?;
|
apdu(&bb).await?;
|
||||||
let signature = apdu(&bb).await;
|
Ok(())
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ledger_cmu(data: &[u8]) -> Result<Vec<u8>> {
|
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_all(&hex!("E0800000"))?;
|
||||||
bb.write_u8(data.len() as u8)?;
|
bb.write_u8(data.len() as u8)?;
|
||||||
bb.write_all(data)?;
|
bb.write_all(data)?;
|
||||||
let cmu = apdu(&bb).await;
|
let cmu = apdu(&bb).await?;
|
||||||
Ok(cmu)
|
Ok(cmu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +255,7 @@ pub async fn ledger_jubjub_hash(data: &[u8]) -> Result<Vec<u8>> {
|
||||||
bb.write_all(&hex!("E0810000"))?;
|
bb.write_all(&hex!("E0810000"))?;
|
||||||
bb.write_u8(data.len() as u8)?;
|
bb.write_u8(data.len() as u8)?;
|
||||||
bb.write_all(data)?;
|
bb.write_all(data)?;
|
||||||
let cmu = apdu(&bb).await;
|
let cmu = apdu(&bb).await?;
|
||||||
Ok(cmu)
|
Ok(cmu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +264,15 @@ pub async fn ledger_pedersen_hash(data: &[u8]) -> Result<Vec<u8>> {
|
||||||
bb.write_all(&hex!("E0820000"))?;
|
bb.write_all(&hex!("E0820000"))?;
|
||||||
bb.write_u8(data.len() as u8)?;
|
bb.write_u8(data.len() as u8)?;
|
||||||
bb.write_all(data)?;
|
bb.write_all(data)?;
|
||||||
let cmu = apdu(&bb).await;
|
let cmu = apdu(&bb).await?;
|
||||||
Ok(cmu)
|
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;
|
pub mod api;
|
||||||
|
|
||||||
#[cfg(feature = "ledger")]
|
#[cfg(feature = "ledger")]
|
||||||
mod ledger;
|
pub mod ledger;
|
||||||
|
|
||||||
pub use crate::chain::{connect_lightwalletd, get_best_server, ChainError};
|
pub use crate::chain::{connect_lightwalletd, get_best_server, ChainError};
|
||||||
pub use crate::coinconfig::{
|
pub use crate::coinconfig::{
|
||||||
|
@ -121,7 +121,8 @@ pub use note_selection::{
|
||||||
build_tx, build_tx_plan, fetch_utxos, get_secret_keys, TransactionBuilderConfig,
|
build_tx, build_tx_plan, fetch_utxos, get_secret_keys, TransactionBuilderConfig,
|
||||||
TransactionBuilderError, TransactionPlan, TxBuilderContext, MAX_ATTEMPTS,
|
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")]
|
#[cfg(feature = "nodejs")]
|
||||||
pub mod nodejs;
|
pub mod nodejs;
|
||||||
|
@ -134,7 +135,4 @@ pub fn init_test() {
|
||||||
set_coin_lwd_url(0, "http://127.0.0.1:9067");
|
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;
|
pub use taddr::derive_from_secretkey;
|
||||||
|
|
|
@ -1,20 +1,39 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Read,
|
io::{Read, Write},
|
||||||
path::Path,
|
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 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 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;
|
use zcash_proofs::prover::LocalTxProver;
|
||||||
|
|
||||||
#[tokio::main]
|
use group::ff::Field;
|
||||||
async fn main() -> Result<()> {
|
use nonempty::NonEmpty;
|
||||||
let network: &Network = &MainNetwork;
|
|
||||||
|
|
||||||
|
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 params_dir = Path::new(&std::env::var("HOME").unwrap()).join(".zcash-params");
|
||||||
let prover = LocalTxProver::new(
|
let prover = LocalTxProver::new(
|
||||||
¶ms_dir.join("sapling-spend.params"),
|
¶ms_dir.join("sapling-spend.params"),
|
||||||
|
@ -27,7 +46,243 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let mut client = connect_lightwalletd("https://lwdv3.zecwallet.co").await?;
|
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(())
|
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::note::Nullifier;
|
||||||
use orchard::value::NoteValue;
|
use orchard::value::NoteValue;
|
||||||
use orchard::{Address, Anchor, Bundle};
|
use orchard::{Address, Anchor, Bundle};
|
||||||
use rand::{CryptoRng, RngCore, SeedableRng};
|
use rand::{CryptoRng, RngCore};
|
||||||
use rand_chacha::ChaChaRng;
|
|
||||||
use ripemd::{Digest, Ripemd160};
|
use ripemd::{Digest, Ripemd160};
|
||||||
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
|
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
|
@ -100,7 +99,6 @@ pub fn build_tx(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut has_orchard = false;
|
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 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)
|
let anchor: Anchor = orchard::tree::MerkleHashOrchard::from_bytes(&plan.orchard_anchor)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -290,6 +290,7 @@ pub fn build_tx_plan<F: FeeCalculator>(
|
||||||
network: &Network,
|
network: &Network,
|
||||||
fvk: &str,
|
fvk: &str,
|
||||||
taddr: &str,
|
taddr: &str,
|
||||||
|
orchard_fvk: &str,
|
||||||
anchor_height: u32,
|
anchor_height: u32,
|
||||||
expiry_height: u32,
|
expiry_height: u32,
|
||||||
orchard_anchor: &Option<Hash>,
|
orchard_anchor: &Option<Hash>,
|
||||||
|
@ -323,6 +324,7 @@ pub fn build_tx_plan<F: FeeCalculator>(
|
||||||
let tx_plan = TransactionPlan {
|
let tx_plan = TransactionPlan {
|
||||||
fvk: fvk.to_string(),
|
fvk: fvk.to_string(),
|
||||||
taddr: taddr.to_string(),
|
taddr: taddr.to_string(),
|
||||||
|
orchard_fvk: orchard_fvk.to_string(),
|
||||||
anchor_height,
|
anchor_height,
|
||||||
expiry_height,
|
expiry_height,
|
||||||
orchard_anchor: orchard_anchor.unwrap_or(Hash::default()),
|
orchard_anchor: orchard_anchor.unwrap_or(Hash::default()),
|
||||||
|
|
|
@ -157,8 +157,9 @@ pub struct RecipientShort {
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
pub struct TransactionPlan {
|
pub struct TransactionPlan {
|
||||||
pub fvk: String,
|
|
||||||
pub taddr: String,
|
pub taddr: String,
|
||||||
|
pub fvk: String,
|
||||||
|
pub orchard_fvk: String,
|
||||||
pub anchor_height: u32,
|
pub anchor_height: u32,
|
||||||
pub expiry_height: u32,
|
pub expiry_height: u32,
|
||||||
#[serde(with = "SerHex::<Strict>")]
|
#[serde(with = "SerHex::<Strict>")]
|
||||||
|
|
|
@ -12,7 +12,7 @@ mod note;
|
||||||
|
|
||||||
pub use hash::{OrchardHasher, ORCHARD_ROOTS};
|
pub use hash::{OrchardHasher, ORCHARD_ROOTS};
|
||||||
pub use key::{derive_orchard_keys, OrchardKeyBytes};
|
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 {
|
pub fn get_proving_key() -> &'static ProvingKey {
|
||||||
if !PROVING_KEY.filled() {
|
if !PROVING_KEY.filled() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::chain::Nf;
|
use crate::chain::Nf;
|
||||||
use crate::db::ReceivedNote;
|
use crate::db::ReceivedNote;
|
||||||
use crate::sync::{
|
use crate::sync::{
|
||||||
CompactOutputBytes, DecryptedNote, Node, OutputPosition, TrialDecrypter, ViewKey,
|
CompactOutputBytes, DecryptedNote, Node, OutputPosition, TrialDecrypter, ViewKey, Witness,
|
||||||
};
|
};
|
||||||
use crate::CompactTx;
|
use crate::CompactTx;
|
||||||
use orchard::keys::PreparedIncomingViewingKey;
|
use orchard::keys::PreparedIncomingViewingKey;
|
||||||
|
@ -110,3 +110,17 @@ impl<N: Parameters> TrialDecrypter<N, OrchardDomain, OrchardViewKey, DecryptedOr
|
||||||
vtx.actions.iter().map(|co| co.into()).collect()
|
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,
|
height: latest_height as u64,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(anyhow!("Broadcasting is disabled"));
|
let rep = client
|
||||||
|
.send_transaction(Request::new(raw_tx))
|
||||||
// let rep = client
|
.await?
|
||||||
// .send_transaction(Request::new(raw_tx))
|
.into_inner();
|
||||||
// .await?
|
let code = rep.error_code;
|
||||||
// .into_inner();
|
if code == 0 {
|
||||||
// let code = rep.error_code;
|
Ok(rep.error_message)
|
||||||
// if code == 0 {
|
} else {
|
||||||
// Ok(rep.error_message)
|
Err(anyhow::anyhow!(rep.error_message))
|
||||||
// } else {
|
}
|
||||||
// Err(anyhow::anyhow!(rep.error_message))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tx_summary(tx: &Tx) -> anyhow::Result<TxSummary> {
|
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))
|
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(
|
pub async fn sweep_tkey(
|
||||||
last_height: u32,
|
last_height: u32,
|
||||||
sk: &str,
|
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(
|
pub fn get_unified_address(
|
||||||
network: &Network,
|
network: &Network,
|
||||||
db: &DbAdapter,
|
db: &DbAdapter,
|
||||||
|
@ -59,8 +62,8 @@ pub fn get_unified_address(
|
||||||
}
|
}
|
||||||
if !tpe.sapling && !tpe.orchard {
|
if !tpe.sapling && !tpe.orchard {
|
||||||
// UA cannot be t-only
|
// UA cannot be t-only
|
||||||
tpe.sapling = true;
|
let address = db.get_taddr(account)?.ok_or(anyhow!("No taddr"))?;
|
||||||
tpe.orchard = true;
|
return Ok(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
let address = match (tpe.transparent, tpe.sapling, tpe.orchard) {
|
let address = match (tpe.transparent, tpe.sapling, tpe.orchard) {
|
||||||
|
|
Loading…
Reference in New Issue