diff --git a/Cargo.toml b/Cargo.toml index c7a2507..bb43ecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,5 @@ halo2_gadgets = { version = "0.3.0",features = ["unstable-sha256-gadget"] } ecies = "0.2.6" halo2_proofs = "0.3.0" plotters = { version = "0.3.0", default-features = true, optional = true } -pasta_curves = "0.5" \ No newline at end of file +pasta_curves = "0.5" +orchard = "0.6.0" \ No newline at end of file diff --git a/src/deposit.rs b/src/deposit.rs new file mode 100644 index 0000000..fe6d332 --- /dev/null +++ b/src/deposit.rs @@ -0,0 +1,57 @@ +use crate::{wallets::Wallets}; +use rand::rngs::OsRng; +use orchard::{ + builder::Builder, + bundle::{Authorized, Flags}, + circuit::{ProvingKey, VerifyingKey}, + keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, + note::ExtractedNoteCommitment, + note_encryption::OrchardDomain, + tree::{MerkleHashOrchard, MerklePath}, + value::NoteValue, + Bundle, +}; + + +pub fn deposit(address: String, value: u64) -> Bundle { + let wallets = Wallets::new(); + let wallet = wallets.get_wallet(&address).unwrap(); + + let mut rng = OsRng; + let pk = ProvingKey::build(); + + let sk = wallet.sk(); + let fvk = FullViewingKey::from(&sk); + let recipient = fvk.address_at(0u32, Scope::External); + + // Create a shielding bundle. + let shielding_bundle: Bundle<_, i64> = { + // Use the empty tree. + let anchor = MerkleHashOrchard::empty_root(32.into()).into(); + + let mut builder = Builder::new(Flags::from_parts(false, true), anchor); + assert_eq!( + builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + Ok(()) + ); + let unauthorized = builder.build(&mut rng).unwrap(); + let sighash = unauthorized.commitment().into(); + let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); + proven.apply_signatures(rng, sighash, &[]).unwrap() + }; + shielding_bundle +} + +pub fn verify_bundle(bundle: &Bundle) { + let vk = VerifyingKey::build(); + assert!(matches!(bundle.verify_proof(&vk), Ok(()))); + let sighash: [u8; 32] = bundle.commitment().into(); + let bvk = bundle.binding_validating_key(); + for action in bundle.actions() { + assert_eq!(action.rk().verify(&sighash, action.authorization()), Ok(())); + } + assert_eq!( + bvk.verify(&sighash, bundle.authorization().binding_signature()), + Ok(()) + ); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index db390e2..477d64f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ mod transaction_input; mod transaction_output; mod wallet; mod wallets; +mod deposit; +mod transfer; fn main() { let mut c = cli::Cli { diff --git a/src/transfer.rs b/src/transfer.rs new file mode 100644 index 0000000..6a17e20 --- /dev/null +++ b/src/transfer.rs @@ -0,0 +1,3 @@ +pub fn transfer(address: String, value: u64) -> Bundle { + +} \ No newline at end of file diff --git a/src/wallet.rs b/src/wallet.rs index 0ec48b5..3aab807 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -3,6 +3,9 @@ use secp256k1::rand::rngs::OsRng; use secp256k1::Secp256k1; use serde::{Deserialize, Serialize}; use sha2::Sha256; +use rand::RngCore; +use rand::rngs::OsRng as randRng; +use orchard::keys; const VERSION: u8 = 0x00; pub(crate) const CHECKSUM_LENGTH: usize = 4; @@ -11,6 +14,7 @@ pub(crate) const CHECKSUM_LENGTH: usize = 4; pub struct Wallet { pub private_key: String, pub public_key: String, + pub spend_key : String, } impl Wallet { @@ -18,9 +22,15 @@ impl Wallet { let secp = Secp256k1::new(); let (private_key, public_key) = secp.generate_keypair(&mut OsRng); + let mut rng = randRng::default(); + let mut random_bytes = [0u8; 32]; + rng.fill_bytes(&mut random_bytes); + let spend_key = keys::SpendingKey::from_zip32_seed(&random_bytes, 0, 0).unwrap(); + Wallet { private_key: hex::encode(private_key.secret_bytes()), public_key: public_key.to_string(), + spend_key: hex::encode(spend_key.to_bytes()), } } @@ -32,6 +42,22 @@ impl Wallet { versioned_payload.extend_from_slice(&checksum); bs58::encode(&versioned_payload).into_string() } + + pub fn get_z_address(&self) -> String { + let spend_key = hex::decode(&self.spend_key).unwrap(); + let spend_key : Result<[u8; 32], _> = spend_key.try_into(); + let spend_key = keys::SpendingKey::from_bytes(spend_key.unwrap()).unwrap(); + let fvk: keys::FullViewingKey = (&spend_key).into(); + let addr = fvk.address_at(0u32, keys::Scope::External); + hex::encode(addr.to_raw_address_bytes()) + } + + pub fn sk(&self) -> keys::SpendingKey { + let spend_key = hex::decode(&self.spend_key).unwrap(); + let spend_key : Result<[u8; 32], _> = spend_key.try_into(); + keys::SpendingKey::from_bytes(spend_key.unwrap()).unwrap() + } + } pub fn validate_address(address: &String) -> bool { diff --git a/src/wallets.rs b/src/wallets.rs index d359790..2f85bac 100644 --- a/src/wallets.rs +++ b/src/wallets.rs @@ -12,6 +12,7 @@ const WALLET_FILE: &str = "wallets.dat"; #[derive(Serialize, Deserialize)] pub struct Wallets { wallets: HashMap, + zwallets:HashMap, } impl Wallets { @@ -22,8 +23,10 @@ impl Wallets { pub fn create_wallet(&mut self) -> String { let wallet = Wallet::new(); let address = wallet.get_address(); + let zaddr = wallet.get_z_address(); self.wallets.insert(address.clone(), wallet); + self.zwallets.insert(zaddr.clone(), wallet); address } @@ -36,6 +39,10 @@ impl Wallets { self.wallets.get(address) } + pub fn get_z_wallet(&self, address: &str) -> Option<&Wallet> { + self.zwallets.get(address) + } + fn load_from_file() -> io::Result { if Path::new(WALLET_FILE).exists() { let mut file = File::open(WALLET_FILE)?; @@ -48,6 +55,7 @@ impl Wallets { } else { Ok(Wallets { wallets: HashMap::new(), + zwallets: HashMap::new(), }) } }