zcash-sync/src/taddr.rs

164 lines
5.7 KiB
Rust
Raw Normal View History

2021-07-16 01:42:29 -07:00
use crate::chain::send_transaction;
2021-08-16 06:07:04 -07:00
use crate::{
connect_lightwalletd, get_branch, get_latest_height, AddressList, CompactTxStreamerClient,
DbAdapter, GetAddressUtxosArg, NETWORK,
};
2021-07-16 01:42:29 -07:00
use anyhow::Context;
use bip39::{Language, Mnemonic, Seed};
use ripemd160::{Digest, Ripemd160};
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
use sha2::Sha256;
use std::str::FromStr;
use tiny_hderive::bip32::ExtendedPrivKey;
2021-07-09 06:33:05 -07:00
use tonic::transport::Channel;
use tonic::Request;
2021-07-16 01:42:29 -07:00
use zcash_client_backend::encoding::{
decode_extended_full_viewing_key, decode_payment_address, encode_transparent_address,
};
2021-08-23 05:42:13 -07:00
use zcash_primitives::consensus::{BlockHeight, Parameters, Network};
2021-07-16 01:42:29 -07:00
use zcash_primitives::legacy::{Script, TransparentAddress};
use zcash_primitives::transaction::builder::Builder;
2021-07-09 06:33:05 -07:00
use zcash_primitives::transaction::components::amount::DEFAULT_FEE;
use zcash_primitives::transaction::components::{Amount, OutPoint, TxOut};
use zcash_proofs::prover::LocalTxProver;
2021-08-23 05:42:13 -07:00
use rand::rngs::OsRng;
2021-07-09 06:33:05 -07:00
2021-07-16 01:42:29 -07:00
pub async fn get_taddr_balance(
client: &mut CompactTxStreamerClient<Channel>,
address: &str,
) -> anyhow::Result<u64> {
2021-07-09 06:33:05 -07:00
let req = AddressList {
addresses: vec![address.to_string()],
};
2021-07-16 01:42:29 -07:00
let rep = client
.get_taddress_balance(Request::new(req))
.await?
.into_inner();
2021-07-09 06:33:05 -07:00
Ok(rep.value_zat as u64)
}
2021-07-16 01:42:29 -07:00
pub async fn shield_taddr(
db: &DbAdapter,
account: u32,
prover: &LocalTxProver,
ld_url: &str,
) -> anyhow::Result<String> {
2021-07-09 06:33:05 -07:00
let mut client = connect_lightwalletd(ld_url).await?;
let last_height = get_latest_height(&mut client).await?;
2021-08-23 05:42:13 -07:00
let mut builder = Builder::new(NETWORK, BlockHeight::from_u32(last_height));
add_shield_taddr(&mut builder, db, account, ld_url, DEFAULT_FEE).await?;
let consensus_branch_id = get_branch(last_height);
let (tx, _) = builder.build(consensus_branch_id, prover)?;
let mut raw_tx: Vec<u8> = vec![];
tx.write(&mut raw_tx)?;
let tx_id = send_transaction(&mut client, &raw_tx, last_height).await?;
log::info!("Tx ID = {}", tx_id);
Ok(tx_id)
}
pub async fn add_shield_taddr(builder: &mut Builder<'_, Network, OsRng>,
db: &DbAdapter,
account: u32,
ld_url: &str,
fee: Amount) -> anyhow::Result<()> {
let mut client = connect_lightwalletd(ld_url).await?;
2021-07-09 06:33:05 -07:00
let ivk = db.get_ivk(account)?;
2021-07-16 01:42:29 -07:00
let fvk =
decode_extended_full_viewing_key(NETWORK.hrp_sapling_extended_full_viewing_key(), &ivk)?
.unwrap();
2021-07-09 06:33:05 -07:00
let z_address = db.get_address(account)?;
let pa = decode_payment_address(NETWORK.hrp_sapling_payment_address(), &z_address)?.unwrap();
let t_address = db.get_taddr(account)?;
2021-07-16 01:42:29 -07:00
if t_address.is_none() {
anyhow::bail!("No transparent address");
}
2021-07-09 06:33:05 -07:00
let t_address = t_address.unwrap();
let amount = Amount::from_u64(get_taddr_balance(&mut client, &t_address).await?).unwrap();
2021-08-23 05:42:13 -07:00
if amount < fee {
2021-07-16 01:42:29 -07:00
anyhow::bail!("Not enough balance");
}
2021-08-23 05:42:13 -07:00
let amount = amount - fee;
2021-07-09 06:33:05 -07:00
2021-10-11 02:13:36 -07:00
add_utxos(builder, &mut client, db, account, &t_address).await?;
let ovk = fvk.fvk.ovk;
builder.add_sapling_output(Some(ovk), pa, amount, None)?;
Ok(())
}
pub async fn add_utxos(builder: &mut Builder<'_, Network, OsRng>, client: &mut CompactTxStreamerClient<Channel>, db: &DbAdapter, account: u32, t_address: &str) -> anyhow::Result<()> {
2021-07-09 06:33:05 -07:00
let sk = db.get_tsk(account)?;
2021-07-16 01:42:29 -07:00
let seckey = secp256k1::SecretKey::from_str(&sk).context("Cannot parse secret key")?;
2021-07-09 06:33:05 -07:00
let req = GetAddressUtxosArg {
addresses: vec![t_address.to_string()],
start_height: 0,
max_entries: 0,
};
2021-07-16 01:42:29 -07:00
let utxo_rep = client
.get_address_utxos(Request::new(req))
.await?
.into_inner();
2021-07-09 06:33:05 -07:00
for utxo in utxo_rep.address_utxos.iter() {
let mut tx_hash = [0u8; 32];
tx_hash.copy_from_slice(&utxo.txid);
let op = OutPoint::new(tx_hash, utxo.index as u32);
let script = Script(utxo.script.clone());
let txout = TxOut {
value: Amount::from_i64(utxo.value_zat).unwrap(),
script_pubkey: script,
};
builder.add_transparent_input(seckey, op, txout)?;
}
2021-08-23 05:42:13 -07:00
Ok(())
2021-07-09 06:33:05 -07:00
}
pub fn derive_tkeys(phrase: &str, path: &str) -> anyhow::Result<(String, String)> {
let mnemonic = Mnemonic::from_phrase(&phrase, Language::English)?;
let seed = Seed::new(&mnemonic, "");
let secp = Secp256k1::<All>::new();
let ext = ExtendedPrivKey::derive(&seed.as_bytes(), path).unwrap();
let secret_key = SecretKey::from_slice(&ext.secret()).unwrap();
let pub_key = PublicKey::from_secret_key(&secp, &secret_key);
let pub_key = pub_key.serialize();
let pub_key = Ripemd160::digest(&Sha256::digest(&pub_key));
let address = TransparentAddress::PublicKey(pub_key.into());
2021-07-16 01:42:29 -07:00
let address = encode_transparent_address(
&NETWORK.b58_pubkey_address_prefix(),
&NETWORK.b58_script_address_prefix(),
&address,
);
2021-07-09 06:33:05 -07:00
let sk = secret_key.to_string();
Ok((sk, address))
}
#[cfg(test)]
mod tests {
use crate::db::DEFAULT_DB_PATH;
2021-07-16 01:42:29 -07:00
use crate::taddr::{derive_tkeys, shield_taddr};
use crate::{DbAdapter, LWD_URL};
2021-07-09 06:33:05 -07:00
use zcash_proofs::prover::LocalTxProver;
#[tokio::test]
async fn test_shield_addr() {
let prover = LocalTxProver::with_default_location().unwrap();
let db = DbAdapter::new(DEFAULT_DB_PATH).unwrap();
let txid = shield_taddr(&db, 1, &prover, LWD_URL).await.unwrap();
println!("{}", txid);
}
#[test]
fn test_derive() {
let seed = dotenv::var("SEED").unwrap();
for i in 0..10 {
let (_sk, addr) = derive_tkeys(&seed, &format!("m/44'/133'/0'/0/{}", i)).unwrap();
println!("{}", addr);
}
}
}