Implement transaction creation #73

Merged
pitmutt merged 1 commit from rav001 into dev040 2024-04-30 21:56:10 +00:00
8 changed files with 682 additions and 58 deletions
Showing only changes of commit ad9e2f074d - Show all commits

View file

@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6.1.0]
### Added
- Function to create a raw transaction
- New types for transaction creation:
- `Rseed`
- `TransparentTxSpend`
- `SaplingTxSpend`
- `OrchardTxSpend`
- `OutgoingNote`
- Rust crates:
- `secp256k1`
- `jubjub`
- `rand_core`
### Changed
- `DecodedNote` type now includes a field for `rho` and one for `rseed`
## [0.6.0.0] ## [0.6.0.0]
### Added ### Added

View file

@ -318,6 +318,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "bumpalo"
version = "3.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -703,6 +709,19 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "hdwallet"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7"
dependencies = [
"lazy_static",
"rand_core",
"ring",
"secp256k1",
"thiserror",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@ -796,6 +815,15 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "js-sys"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "jubjub" name = "jubjub"
version = "0.10.0" version = "0.10.0"
@ -1313,6 +1341,30 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "ripemd"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
dependencies = [
"digest 0.10.7",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.37.20" version = "0.37.20"
@ -1336,10 +1388,13 @@ dependencies = [
"f4jumble", "f4jumble",
"haskell-ffi", "haskell-ffi",
"incrementalmerkletree", "incrementalmerkletree",
"jubjub",
"nonempty", "nonempty",
"orchard", "orchard",
"proc-macro2", "proc-macro2",
"rand_core",
"sapling-crypto", "sapling-crypto",
"secp256k1",
"zcash_address 0.2.0", "zcash_address 0.2.0",
"zcash_client_backend", "zcash_client_backend",
"zcash_note_encryption", "zcash_note_encryption",
@ -1385,6 +1440,24 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "secp256k1"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894"
dependencies = [
"secp256k1-sys",
]
[[package]]
name = "secp256k1-sys"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "secrecy" name = "secrecy"
version = "0.8.0" version = "0.8.0"
@ -1661,6 +1734,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -1673,6 +1752,70 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.32",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.32",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "which" name = "which"
version = "4.4.0" version = "4.4.0"
@ -1684,6 +1827,28 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@ -1882,6 +2047,7 @@ dependencies = [
"ff", "ff",
"fpe", "fpe",
"group", "group",
"hdwallet",
"hex", "hex",
"incrementalmerkletree", "incrementalmerkletree",
"jubjub", "jubjub",
@ -1891,7 +2057,9 @@ dependencies = [
"rand", "rand",
"rand_core", "rand_core",
"redjubjub", "redjubjub",
"ripemd",
"sapling-crypto", "sapling-crypto",
"secp256k1",
"sha2 0.10.6", "sha2 0.10.6",
"subtle", "subtle",
"tracing", "tracing",

View file

@ -13,13 +13,16 @@ borsh = "0.10"
bech32 = "0.11" bech32 = "0.11"
orchard = "0.7.1" orchard = "0.7.1"
zcash_note_encryption = "0.4.0" zcash_note_encryption = "0.4.0"
zcash_primitives = "0.14.0" zcash_primitives = { version = "0.14.0", features = ["transparent-inputs"]}
zcash_client_backend = "0.11.1" zcash_client_backend = "0.11.1"
sapling-crypto = "0.1.3" sapling-crypto = "0.1.3"
zip32 = "0.1.0" zip32 = "0.1.0"
proc-macro2 = "1.0.66" proc-macro2 = "1.0.66"
nonempty = "0.7.0" nonempty = "0.7.0"
incrementalmerkletree = "0.5.0" incrementalmerkletree = "0.5.0"
secp256k1 = "0.26.0"
jubjub = "0.10.0"
rand_core = { version = "0.6.4", features = ["getrandom"]}
[features] [features]

View file

@ -12,6 +12,8 @@ use std::{
use nonempty::NonEmpty; use nonempty::NonEmpty;
use rand_core::OsRng;
use f4jumble; use f4jumble;
use borsh::{BorshDeserialize, BorshSerialize}; use borsh::{BorshDeserialize, BorshSerialize};
@ -23,6 +25,10 @@ use haskell_ffi::{
FromHaskell, HaskellSize, ToHaskell FromHaskell, HaskellSize, ToHaskell
}; };
use secp256k1::SecretKey;
use jubjub::Fr;
use incrementalmerkletree::{ use incrementalmerkletree::{
frontier::CommitmentTree, frontier::CommitmentTree,
witness::IncrementalWitness witness::IncrementalWitness
@ -39,8 +45,13 @@ use sapling_crypto::{
Node, Node,
MerklePath, MerklePath,
PaymentAddress, PaymentAddress,
Anchor as SaplingAnchor,
value::ValueCommitment as SaplingValueCommitment, value::ValueCommitment as SaplingValueCommitment,
note::ExtractedNoteCommitment as SaplingNoteCommitment, note::{
ExtractedNoteCommitment as SaplingNoteCommitment,
Note as SaplingNote,
Rseed
},
keys::{ keys::{
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
ExpandedSpendingKey, ExpandedSpendingKey,
@ -57,6 +68,11 @@ use sapling_crypto::{
Authorized as SaplingAuthorized, Authorized as SaplingAuthorized,
Bundle as SaplingBundle Bundle as SaplingBundle
}, },
value::NoteValue as SaplingNoteValue,
circuit::{
SpendParameters,
OutputParameters
},
zip32::{ zip32::{
sapling_find_address, sapling_find_address,
DiversifierKey DiversifierKey
@ -70,18 +86,34 @@ use zcash_primitives::{
read_incremental_witness, read_incremental_witness,
write_incremental_witness write_incremental_witness
}, },
legacy::{
Script,
TransparentAddress
},
zip339::{Count, Mnemonic}, zip339::{Count, Mnemonic},
transaction::components::{ transaction::{
amount::Amount, Transaction,
transparent::{ fees::zip317::FeeRule,
Bundle as TransparentBundle, builder::{
TxIn, Builder,
TxOut, Error,
OutPoint, BuildConfig
Authorized },
components::{
amount::{
Amount,
NonNegativeAmount
},
transparent::{
Bundle as TransparentBundle,
TxIn,
TxOut,
OutPoint,
Authorized
}
} }
}, },
transaction::Transaction, memo::MemoBytes,
consensus::{ consensus::{
BranchId::Nu5, BranchId::Nu5,
MainNetwork, MainNetwork,
@ -107,6 +139,7 @@ use zcash_client_backend::keys::sapling::{
use zcash_primitives::zip32::DiversifierIndex; use zcash_primitives::zip32::DiversifierIndex;
use orchard::{ use orchard::{
Address as OrchardAddress,
Bundle as OrchardBundle, Bundle as OrchardBundle,
bundle::{ bundle::{
Authorized as OrchardAuthorized, Authorized as OrchardAuthorized,
@ -114,11 +147,18 @@ use orchard::{
}, },
Action, Action,
keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope},
note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note::{RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note_encryption::OrchardDomain, note_encryption::OrchardDomain,
primitives::redpallas::{VerificationKey, SpendAuth, Signature}, primitives::redpallas::{VerificationKey, SpendAuth, Signature},
tree::MerkleHashOrchard, tree::{
value::ValueCommitment MerklePath as OrchardMerklePath,
MerkleHashOrchard,
Anchor as OrchardAnchor
},
value::{
ValueCommitment,
NoteValue
}
}; };
use bech32::{ use bech32::{
@ -255,7 +295,9 @@ pub struct Hnote {
note: u64, note: u64,
recipient: Vec<u8>, recipient: Vec<u8>,
memo: Vec<u8>, memo: Vec<u8>,
nullifier: Vec<u8> nullifier: Vec<u8>,
rho: Vec<u8>,
rseed: Hrseed
} }
impl<RW> ToHaskell<RW> for Hnote { impl<RW> ToHaskell<RW> for Hnote {
@ -265,6 +307,27 @@ impl<RW> ToHaskell<RW> for Hnote {
} }
} }
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Hrseed {
kind: u8,
bytes: Vec<u8>
}
impl<RW> FromHaskell<RW> for Hrseed {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = Hrseed::deserialize(buf)?;
Ok(x)
}
}
impl<RW> ToHaskell<RW> for Hrseed {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
}
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
pub struct Hua { pub struct Hua {
net: u8, net: u8,
@ -363,6 +426,13 @@ pub struct HTxOut {
script: Vec<u8> script: Vec<u8>
} }
impl<RW> FromHaskell<RW> for HTxOut {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = HTxOut::deserialize(buf)?;
Ok(x)
}
}
impl<RW> ToHaskell<RW> for HTxOut { impl<RW> ToHaskell<RW> for HTxOut {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> { fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?; self.serialize(writer)?;
@ -374,6 +444,9 @@ impl HTxOut {
pub fn pack(t: &TxOut) -> HTxOut { pub fn pack(t: &TxOut) -> HTxOut {
return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() } return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() }
} }
pub fn unpack(&self) -> TxOut {
TxOut { value: NonNegativeAmount::from_nonnegative_i64(self.amt).unwrap(), script_pubkey: Script(self.script.clone())}
}
} }
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
@ -382,6 +455,13 @@ pub struct Houtpoint {
index: u32 index: u32
} }
impl<RW> FromHaskell<RW> for Houtpoint {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = Houtpoint::deserialize(buf)?;
Ok(x)
}
}
impl<RW> ToHaskell<RW> for Houtpoint { impl<RW> ToHaskell<RW> for Houtpoint {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> { fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?; self.serialize(writer)?;
@ -393,6 +473,31 @@ impl Houtpoint {
pub fn pack(o: &OutPoint) -> Houtpoint { pub fn pack(o: &OutPoint) -> Houtpoint {
return Houtpoint {hash: o.hash().to_vec() , index: o.n() } return Houtpoint {hash: o.hash().to_vec() , index: o.n() }
} }
pub fn unpack(&self) -> OutPoint {
OutPoint::new(to_array(self.hash.clone()), self.index)
}
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct HtransparentInput {
sk: Vec<u8>,
utxo: Houtpoint,
coin: HTxOut
}
impl<RW> FromHaskell<RW> for HtransparentInput {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = HtransparentInput::deserialize(buf)?;
Ok(x)
}
}
impl<RW> ToHaskell<RW> for HtransparentInput {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
Ok(())
}
} }
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
@ -447,6 +552,52 @@ impl Hspend {
} }
} }
#[derive(BorshSerialize, BorshDeserialize)]
pub struct HsaplingInput {
sk: Vec<u8>,
note: Hnote,
iw: Vec<u8>
}
impl<RW> FromHaskell<RW> for HsaplingInput {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = HsaplingInput::deserialize(buf)?;
Ok(x)
}
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct HorchardInput {
sk: Vec<u8>,
note: Hnote,
iw: Vec<u8>
}
impl<RW> FromHaskell<RW> for HorchardInput {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = HorchardInput::deserialize(buf)?;
Ok(x)
}
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Houtput {
kind: u8,
ovk: Vec<u8>,
to: Vec<u8>,
amt: u64,
memo: Vec<u8>,
chg: bool
}
impl<RW> FromHaskell<RW> for Houtput {
fn from_haskell(buf: &mut &[u8], _tag: PhantomData<RW>) -> Result<Self> {
let x = Houtput::deserialize(buf)?;
Ok(x)
}
}
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
pub struct HOBundle { pub struct HOBundle {
empty: bool, empty: bool,
@ -771,23 +922,31 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
match result { match result {
Some((n, r, m)) => { Some((n, r, m)) => {
let nullifier = n.nf(&nk, pos); let nullifier = n.nf(&nk, pos);
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec() }; let rseed = match n.rseed() {
Rseed::BeforeZip212(x) => {
Hrseed {kind: 1, bytes: x.to_bytes().to_vec()}
},
Rseed::AfterZip212(y) => {
Hrseed {kind: 2, bytes: y.to_vec()}
}
};
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec(), rho: vec![0], rseed };
marshall_to_haskell_var(&hn, out, out_len, RW); marshall_to_haskell_var(&hn, out, out_len, RW);
}, },
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
}, },
Err(_e1) => { Err(_e1) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} };
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
}, },
Err(_e) => { Err(_e) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} };
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -816,23 +975,31 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3); let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3);
match result { match result {
Some((n, r, m)) => { Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0]}; let rseed = match n.rseed() {
Rseed::BeforeZip212(x) => {
Hrseed { kind: 1, bytes: x.to_bytes().to_vec()}
},
Rseed::AfterZip212(y) => {
Hrseed { kind: 2, bytes: y.to_vec()}
}
};
let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0], rho: vec![0], rseed};
marshall_to_haskell_var(&hn, out, out_len, RW); marshall_to_haskell_var(&hn, out, out_len, RW);
} }
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
}, },
Err(_e1) => { Err(_e1) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
} }
Err(_e) => { Err(_e) => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -866,17 +1033,19 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action); let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result { match result {
Some((n, r, m)) => { Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0]}; let rho = n.rho().to_bytes().to_vec();
let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()};
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0], rho, rseed};
marshall_to_haskell_var(&hn, out, out_len, RW); marshall_to_haskell_var(&hn, out, out_len, RW);
} }
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
}, },
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -914,11 +1083,13 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action); let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result { match result {
Some((n, r, m)) => { Some((n, r, m)) => {
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec()}; let rho = n.rho().to_bytes().to_vec();
let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()};
let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec(), rho, rseed};
marshall_to_haskell_var(&hn, out, out_len, RW); marshall_to_haskell_var(&hn, out, out_len, RW);
} }
None => { None => {
let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}};
marshall_to_haskell_var(&hn0, out, out_len, RW); marshall_to_haskell_var(&hn0, out, out_len, RW);
} }
} }
@ -1454,8 +1625,193 @@ pub extern "C" fn rust_wrapper_decode_sapling_address(
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_create_transaction( pub extern "C" fn rust_wrapper_create_transaction(
sapling: *const u8, sap_wit: *const u8,
sapling_len: usize, sap_wit_len: usize,
orch_wit: *const u8,
orch_wit_len: usize,
t_input: *const u8,
t_input_len: usize,
s_input: *const u8,
s_input_len: usize,
o_input: *const u8,
o_input_len: usize,
out_list: *const u8,
out_list_len: usize,
sapspend: *const u8,
sapspend_len: usize,
sapoutput: *const u8,
sapoutput_len: usize,
net: bool,
bl_height: u32,
out: *mut u8, out: *mut u8,
out_len: &mut usize){ out_len: &mut usize){
let sap_wit_in: Vec<u8> = marshall_from_haskell_var(sap_wit, sap_wit_len, RW);
let sap_wit_reader = Cursor::new(sap_wit_in);
let sap_iw: Option<IncrementalWitness<Node, SAPLING_DEPTH>> = read_incremental_witness(sap_wit_reader).ok();
let sap_anchor = match sap_iw {
Some(s_iw) => {
Some(SaplingAnchor::from(s_iw.root()))
},
None => {
None
}
};
let orch_wit_in: Vec<u8> = marshall_from_haskell_var(orch_wit, orch_wit_len, RW);
let orch_wit_reader = Cursor::new(orch_wit_in);
let orch_iw: Option<IncrementalWitness<MerkleHashOrchard, 32>> = read_incremental_witness(orch_wit_reader).ok();
let orch_anchor = match orch_iw {
Some(o_iw) => {
Some(OrchardAnchor::from(o_iw.root()))
},
None => {
None
}
};
let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor};
let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config);
let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config);
let trans_input: Vec<HtransparentInput> = marshall_from_haskell_var(t_input, t_input_len, RW);
for t_in in trans_input {
if t_in.sk.len() > 1 {
let k = SecretKey::from_slice(&t_in.sk).unwrap();
if net {
match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) {
Ok(()) => {
continue;
},
Err(_e) => { println!("Error reading transparent input"); }
}
} else {
match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) {
Ok(()) => {
continue;
},
Err(_e) => { println!("Error reading transparent input"); }
}
}
}
}
let sap_input: Vec<HsaplingInput> = marshall_from_haskell_var(s_input, s_input_len, RW);
for s_in in sap_input {
if s_in.sk.len() > 1 {
let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk);
match sp_key {
Ok(sk) => {
let pay_addr = PaymentAddress::from_bytes(&to_array(s_in.note.recipient)).unwrap();
let rseed = if s_in.note.rseed.kind == 1 {
Rseed::BeforeZip212(Fr::from_bytes(&to_array(s_in.note.rseed.bytes)).unwrap())
} else {
Rseed::AfterZip212(to_array(s_in.note.rseed.bytes))
};
let note = SaplingNote::from_parts(pay_addr, SaplingNoteValue::from_raw(s_in.note.note), rseed);
let wit_reader = Cursor::new(s_in.iw);
let iw: IncrementalWitness<Node, SAPLING_DEPTH> = read_incremental_witness(wit_reader).unwrap();
let merkle_path = iw.path().unwrap();
if net {
let _mb = main_builder.add_sapling_spend::<String>(&sk, note, merkle_path).unwrap();
} else {
let _tb = test_builder.add_sapling_spend::<String>(&sk, note, merkle_path).unwrap();
}
},
Err(_e) => {
continue;
}
}
}
}
let orch_input: Vec<HorchardInput> = marshall_from_haskell_var(o_input, o_input_len, RW);
for o_in in orch_input {
if o_in.sk.len() > 1 {
let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap();
let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap();
let rho = Nullifier::from_bytes(&to_array(o_in.note.rho)).unwrap();
let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap();
let val = NoteValue::from_raw(o_in.note.note);
let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap();
let wit_reader = Cursor::new(o_in.iw);
let iw: IncrementalWitness<MerkleHashOrchard, 32> = read_incremental_witness(wit_reader).unwrap();
let merkle_path = OrchardMerklePath::from(iw.path().unwrap());
if net {
let _mb = main_builder.add_orchard_spend::<String>(&sp_key, note, merkle_path).unwrap();
} else {
let _tb = test_builder.add_orchard_spend::<String>(&sp_key, note, merkle_path).unwrap();
}
}
}
let outputs: Vec<Houtput> = marshall_from_haskell_var(out_list, out_list_len, RW);
for output in outputs {
match output.kind {
1 => {
let recipient = TransparentAddress::PublicKeyHash(to_array(output.to));
let val = NonNegativeAmount::from_u64(output.amt).unwrap();
if net {
let _mb = main_builder.add_transparent_output(&recipient, val);
} else {
let _tb = test_builder.add_transparent_output(&recipient, val);
}
},
2 => {
let recipient = TransparentAddress::ScriptHash(to_array(output.to));
let val = NonNegativeAmount::from_u64(output.amt).unwrap();
if net {
let _mb = main_builder.add_transparent_output(&recipient, val);
} else {
let _tb = test_builder.add_transparent_output(&recipient, val);
}
},
3 => {
let ovk = Some(ExpandedSpendingKey::from_spending_key(&output.ovk).ovk);
let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap();
let val = NonNegativeAmount::from_u64(output.amt).unwrap();
let memo = MemoBytes::from_bytes(&output.memo).unwrap();
if net {
let _mb = main_builder.add_sapling_output::<String>(ovk, recipient, val, memo);
} else {
let _tb = test_builder.add_sapling_output::<String>(ovk, recipient, val, memo);
}
},
4 => {
let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap();
let ovk = if output.chg {
Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal))
}else {
Some(FullViewingKey::from(&sk).to_ovk(Scope::External))
};
let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap();
let val = output.amt;
let memo = MemoBytes::from_bytes(&output.memo).unwrap();
if net {
let _mb = main_builder.add_orchard_output::<String>(ovk, recipient, val, memo);
} else {
let _tb = test_builder.add_orchard_output::<String>(ovk, recipient, val, memo);
}
},
_ => {
continue;
}
}
}
let spend_params_in: Vec<u8> = marshall_from_haskell_var(sapspend, sapspend_len, RW);
let spend_params_reader = Cursor::new(spend_params_in);
let spend_prover = SpendParameters::read(spend_params_reader, true).unwrap();
let output_params_in: Vec<u8> = marshall_from_haskell_var(sapoutput, sapoutput_len, RW);
let output_params_reader = Cursor::new(output_params_in);
let output_prover = OutputParameters::read(output_params_reader, true).unwrap();
let result = if net {
main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard())
} else {
test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard())
};
match result {
Ok(r) => {
let mut out_bytes: Vec<u8> = Vec::new();
let _t = r.transaction().write_v5(&mut out_bytes);
let h = Hhex {bytes: out_bytes};
marshall_to_haskell_var(&h, out, out_len, RW);
},
Err(_e) => {
let x = Hhex {bytes: vec![0]};
marshall_to_haskell_var(&x, out, out_len, RW);
}
}
} }

View file

@ -275,3 +275,19 @@ import ZcashHaskell.Types
} }
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_create_transaction as rustWrapperCreateTx
{ toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, toBorshVar* `[TransparentTxSpend]'&
, toBorshVar* `[SaplingTxSpend]'&
, toBorshVar* `[OrchardTxSpend]'&
, toBorshVar* `[OutgoingNote]'&
, toBorshVar* `BS.ByteString'&
, toBorshVar* `BS.ByteString'&
, `Bool'
, `Word64'
, getVarBuffer `Buffer HexString'&
}
-> `()'
#}

View file

@ -21,9 +21,9 @@ import Crypto.Secp256k1
import qualified Data.ByteArray as BA import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58)
import Data.Char (chr)
import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Char8 as BC
import Data.HexString import Data.Char (chr)
import Data.HexString
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import Data.Word import Data.Word
@ -31,6 +31,7 @@ import Haskoin.Address (Address(..))
import qualified Haskoin.Crypto.Hash as H import qualified Haskoin.Crypto.Hash as H
import Haskoin.Crypto.Keys.Extended import Haskoin.Crypto.Keys.Extended
import ZcashHaskell.Types import ZcashHaskell.Types
-- ( AccountId -- ( AccountId
-- , CoinType(..) -- , CoinType(..)
-- , Scope(..) -- , Scope(..)
@ -44,7 +45,7 @@ import ZcashHaskell.Types
-- , getTransparentPrefix -- , getTransparentPrefix
-- , getValue -- , getValue
-- ) -- )
import ZcashHaskell.Utils( encodeBech32m, decodeBech32 ) import ZcashHaskell.Utils (decodeBech32, encodeBech32m)
-- | Required for `TransparentReceiver` encoding and decoding -- | Required for `TransparentReceiver` encoding and decoding
sha256 :: BS.ByteString -> BS.ByteString sha256 :: BS.ByteString -> BS.ByteString
@ -127,60 +128,70 @@ decodeTransparentAddress taddress = do
189 -> 189 ->
Just $ Just $
TransparentAddress MainNet $ TransparentAddress MainNet $
TransparentReceiver P2SH (fromRawBytes transparentReceiver) TransparentReceiver
P2SH
(fromRawBytes transparentReceiver)
186 -> 186 ->
Just $ Just $
TransparentAddress TestNet $ TransparentAddress TestNet $
TransparentReceiver P2SH (fromRawBytes transparentReceiver) TransparentReceiver
P2SH
(fromRawBytes transparentReceiver)
184 -> 184 ->
Just $ Just $
TransparentAddress MainNet $ TransparentAddress MainNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver) TransparentReceiver
P2PKH
(fromRawBytes transparentReceiver)
_ -> Nothing _ -> Nothing
29 -> 29 ->
if sb == 37 if sb == 37
then Just $ then Just $
TransparentAddress TestNet $ TransparentAddress TestNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver) TransparentReceiver
P2PKH
(fromRawBytes transparentReceiver)
else Nothing else Nothing
_ -> Nothing _ -> Nothing
-- | Encode an Exchange Addresss into HRF from TransparentReceiver -- | Encode an Exchange Addresss into HRF from TransparentReceiver
encodeExchangeAddress:: ZcashNet -> TransparentReceiver -> Maybe T.Text encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text
encodeExchangeAddress net tr = do encodeExchangeAddress net tr = do
case (tr_type tr) of case (tr_type tr) of
P2PKH -> do P2PKH -> do
case net of case net of
MainNet -> do MainNet -> do
let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr)) let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr))
Just vhash Just vhash
TestNet -> do TestNet -> do
let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr)) let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr))
Just vhash Just vhash
_ -> Nothing _ -> Nothing
-- | Decode an Exchange Address into a ExchangeAddress -- | Decode an Exchange Address into a ExchangeAddress
decodeExchangeAddress:: T.Text-> Maybe ExchangeAddress decodeExchangeAddress :: T.Text -> Maybe ExchangeAddress
decodeExchangeAddress ex = do decodeExchangeAddress ex = do
if (T.length ex ) > 1 if (T.length ex) > 1
then do then do
let rawd = decodeBech32 (E.encodeUtf8 ex) let rawd = decodeBech32 (E.encodeUtf8 ex)
let tMain = BS.unpack (BC.pack "tex") let tMain = BS.unpack (BC.pack "tex")
let tTest = BS.unpack (BC.pack "textest") let tTest = BS.unpack (BC.pack "textest")
let tFail = BS.unpack (BC.pack "fail") let tFail = BS.unpack (BC.pack "fail")
let hr = BS.unpack (hrp rawd) let hr = BS.unpack (hrp rawd)
if hr /= tFail if hr /= tFail
then do then do
let transparentReceiver = bytes rawd let transparentReceiver = bytes rawd
if hr == tMain if hr == tMain
then Just $ then Just $
ExchangeAddress MainNet $ ExchangeAddress MainNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver) TransparentReceiver P2PKH (fromRawBytes transparentReceiver)
else do else do
if hr == tTest if hr == tTest
then Just $ then Just $
ExchangeAddress TestNet $ ExchangeAddress TestNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver) TransparentReceiver
else Nothing P2PKH
(fromRawBytes transparentReceiver)
else Nothing
else Nothing else Nothing
else Nothing else Nothing

View file

@ -638,11 +638,60 @@ data DecodedNote = DecodedNote
, a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver. , a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver.
, a_memo :: !BS.ByteString -- ^ The decoded shielded memo field. , a_memo :: !BS.ByteString -- ^ The decoded shielded memo field.
, a_nullifier :: !HexString -- ^ The calculated nullifier , a_nullifier :: !HexString -- ^ The calculated nullifier
, a_rho :: !BS.ByteString
, a_rseed :: !Rseed
} deriving stock (Eq, Prelude.Show, GHC.Generic) } deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show) deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote
data Rseed = Rseed
{ rs_kind :: !Word8
, rs_bytes :: !BS.ByteString
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Rseed
data TransparentTxSpend = TransparentTxSpend
{ ts_sk :: !BS.ByteString
, ts_utxo :: !RawOutPoint
, ts_coin :: !RawTxOut
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct TransparentTxSpend
data SaplingTxSpend = SaplingTxSpend
{ ss_sk :: !BS.ByteString
, ss_note :: !DecodedNote
, ss_iw :: !BS.ByteString
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingTxSpend
data OrchardTxSpend = OrchardTxSpend
{ ss_sk :: !BS.ByteString
, ss_note :: !DecodedNote
, ss_iw :: !BS.ByteString
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardTxSpend
data OutgoingNote = OutGoingNote
{ on_kind :: !Word8
, on_key :: !BS.ByteString
, on_recipient :: !BS.ByteString
, on_amt :: !Word64
, on_memo :: !BS.ByteString
, on_chg :: !Bool
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OutgoingNote
-- * Classes -- * Classes
-- | Class to represent types with a bytestring representation -- | Class to represent types with a bytestring representation
class ToBytes a where class ToBytes a where

View file

@ -5,7 +5,7 @@ cabal-version: 3.0
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
name: zcash-haskell name: zcash-haskell
version: 0.6.0.0 version: 0.6.1.0
synopsis: Utilities to interact with the Zcash blockchain synopsis: Utilities to interact with the Zcash blockchain
description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme> description: Please see the README on the repo at <https://git.vergara.tech/Vergara_Tech/zcash-haskell#readme>
category: Blockchain category: Blockchain