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

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/),
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]
### Added

View file

@ -318,6 +318,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "bumpalo"
version = "3.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -703,6 +709,19 @@ dependencies = [
"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]]
name = "heck"
version = "0.4.1"
@ -796,6 +815,15 @@ dependencies = [
"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]]
name = "jubjub"
version = "0.10.0"
@ -1313,6 +1341,30 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "rustix"
version = "0.37.20"
@ -1336,10 +1388,13 @@ dependencies = [
"f4jumble",
"haskell-ffi",
"incrementalmerkletree",
"jubjub",
"nonempty",
"orchard",
"proc-macro2",
"rand_core",
"sapling-crypto",
"secp256k1",
"zcash_address 0.2.0",
"zcash_client_backend",
"zcash_note_encryption",
@ -1385,6 +1440,24 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "secrecy"
version = "0.8.0"
@ -1661,6 +1734,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "version_check"
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"
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]]
name = "which"
version = "4.4.0"
@ -1684,6 +1827,28 @@ dependencies = [
"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]]
name = "windows-sys"
version = "0.48.0"
@ -1882,6 +2047,7 @@ dependencies = [
"ff",
"fpe",
"group",
"hdwallet",
"hex",
"incrementalmerkletree",
"jubjub",
@ -1891,7 +2057,9 @@ dependencies = [
"rand",
"rand_core",
"redjubjub",
"ripemd",
"sapling-crypto",
"secp256k1",
"sha2 0.10.6",
"subtle",
"tracing",

View file

@ -13,13 +13,16 @@ borsh = "0.10"
bech32 = "0.11"
orchard = "0.7.1"
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"
sapling-crypto = "0.1.3"
zip32 = "0.1.0"
proc-macro2 = "1.0.66"
nonempty = "0.7.0"
incrementalmerkletree = "0.5.0"
secp256k1 = "0.26.0"
jubjub = "0.10.0"
rand_core = { version = "0.6.4", features = ["getrandom"]}
[features]

View file

@ -12,6 +12,8 @@ use std::{
use nonempty::NonEmpty;
use rand_core::OsRng;
use f4jumble;
use borsh::{BorshDeserialize, BorshSerialize};
@ -23,6 +25,10 @@ use haskell_ffi::{
FromHaskell, HaskellSize, ToHaskell
};
use secp256k1::SecretKey;
use jubjub::Fr;
use incrementalmerkletree::{
frontier::CommitmentTree,
witness::IncrementalWitness
@ -39,8 +45,13 @@ use sapling_crypto::{
Node,
MerklePath,
PaymentAddress,
Anchor as SaplingAnchor,
value::ValueCommitment as SaplingValueCommitment,
note::ExtractedNoteCommitment as SaplingNoteCommitment,
note::{
ExtractedNoteCommitment as SaplingNoteCommitment,
Note as SaplingNote,
Rseed
},
keys::{
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
ExpandedSpendingKey,
@ -57,6 +68,11 @@ use sapling_crypto::{
Authorized as SaplingAuthorized,
Bundle as SaplingBundle
},
value::NoteValue as SaplingNoteValue,
circuit::{
SpendParameters,
OutputParameters
},
zip32::{
sapling_find_address,
DiversifierKey
@ -70,18 +86,34 @@ use zcash_primitives::{
read_incremental_witness,
write_incremental_witness
},
legacy::{
Script,
TransparentAddress
},
zip339::{Count, Mnemonic},
transaction::components::{
amount::Amount,
transparent::{
Bundle as TransparentBundle,
TxIn,
TxOut,
OutPoint,
Authorized
transaction::{
Transaction,
fees::zip317::FeeRule,
builder::{
Builder,
Error,
BuildConfig
},
components::{
amount::{
Amount,
NonNegativeAmount
},
transparent::{
Bundle as TransparentBundle,
TxIn,
TxOut,
OutPoint,
Authorized
}
}
},
transaction::Transaction,
memo::MemoBytes,
consensus::{
BranchId::Nu5,
MainNetwork,
@ -107,6 +139,7 @@ use zcash_client_backend::keys::sapling::{
use zcash_primitives::zip32::DiversifierIndex;
use orchard::{
Address as OrchardAddress,
Bundle as OrchardBundle,
bundle::{
Authorized as OrchardAuthorized,
@ -114,11 +147,18 @@ use orchard::{
},
Action,
keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope},
note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note::{RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment},
note_encryption::OrchardDomain,
primitives::redpallas::{VerificationKey, SpendAuth, Signature},
tree::MerkleHashOrchard,
value::ValueCommitment
tree::{
MerklePath as OrchardMerklePath,
MerkleHashOrchard,
Anchor as OrchardAnchor
},
value::{
ValueCommitment,
NoteValue
}
};
use bech32::{
@ -255,7 +295,9 @@ pub struct Hnote {
note: u64,
recipient: Vec<u8>,
memo: Vec<u8>,
nullifier: Vec<u8>
nullifier: Vec<u8>,
rho: Vec<u8>,
rseed: Hrseed
}
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)]
pub struct Hua {
net: u8,
@ -363,6 +426,13 @@ pub struct HTxOut {
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 {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
@ -374,6 +444,9 @@ impl 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() }
}
pub fn unpack(&self) -> TxOut {
TxOut { value: NonNegativeAmount::from_nonnegative_i64(self.amt).unwrap(), script_pubkey: Script(self.script.clone())}
}
}
#[derive(BorshSerialize, BorshDeserialize)]
@ -382,6 +455,13 @@ pub struct Houtpoint {
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 {
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
self.serialize(writer)?;
@ -393,6 +473,31 @@ impl Houtpoint {
pub fn pack(o: &OutPoint) -> Houtpoint {
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)]
@ -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)]
pub struct HOBundle {
empty: bool,
@ -771,23 +922,31 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
match result {
Some((n, r, m)) => {
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);
},
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);
}
}
},
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);
}
}
},
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);
}
}
@ -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);
match result {
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);
}
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);
}
}
},
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);
}
}
}
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);
}
}
@ -866,17 +1033,19 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action);
match result {
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);
}
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);
}
}
},
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);
}
}
@ -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);
match result {
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);
}
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);
}
}
@ -1454,8 +1625,193 @@ pub extern "C" fn rust_wrapper_decode_sapling_address(
#[no_mangle]
pub extern "C" fn rust_wrapper_create_transaction(
sapling: *const u8,
sapling_len: usize,
sap_wit: *const u8,
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_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.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58)
import Data.Char (chr)
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.Encoding as E
import Data.Word
@ -31,6 +31,7 @@ import Haskoin.Address (Address(..))
import qualified Haskoin.Crypto.Hash as H
import Haskoin.Crypto.Keys.Extended
import ZcashHaskell.Types
-- ( AccountId
-- , CoinType(..)
-- , Scope(..)
@ -44,7 +45,7 @@ import ZcashHaskell.Types
-- , getTransparentPrefix
-- , getValue
-- )
import ZcashHaskell.Utils( encodeBech32m, decodeBech32 )
import ZcashHaskell.Utils (decodeBech32, encodeBech32m)
-- | Required for `TransparentReceiver` encoding and decoding
sha256 :: BS.ByteString -> BS.ByteString
@ -127,60 +128,70 @@ decodeTransparentAddress taddress = do
189 ->
Just $
TransparentAddress MainNet $
TransparentReceiver P2SH (fromRawBytes transparentReceiver)
TransparentReceiver
P2SH
(fromRawBytes transparentReceiver)
186 ->
Just $
TransparentAddress TestNet $
TransparentReceiver P2SH (fromRawBytes transparentReceiver)
TransparentReceiver
P2SH
(fromRawBytes transparentReceiver)
184 ->
Just $
TransparentAddress MainNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver)
TransparentReceiver
P2PKH
(fromRawBytes transparentReceiver)
_ -> Nothing
29 ->
if sb == 37
then Just $
TransparentAddress TestNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver)
TransparentReceiver
P2PKH
(fromRawBytes transparentReceiver)
else Nothing
_ -> Nothing
-- | Encode an Exchange Addresss into HRF from TransparentReceiver
encodeExchangeAddress:: ZcashNet -> TransparentReceiver -> Maybe T.Text
encodeExchangeAddress net tr = do
encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text
encodeExchangeAddress net tr = do
case (tr_type tr) of
P2PKH -> do
P2PKH -> do
case net of
MainNet -> do
let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr))
Just vhash
TestNet -> do
TestNet -> do
let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr))
Just vhash
_ -> Nothing
_ -> Nothing
-- | Decode an Exchange Address into a ExchangeAddress
decodeExchangeAddress:: T.Text-> Maybe ExchangeAddress
decodeExchangeAddress ex = do
if (T.length ex ) > 1
then do
let rawd = decodeBech32 (E.encodeUtf8 ex)
let tMain = BS.unpack (BC.pack "tex")
let tTest = BS.unpack (BC.pack "textest")
let tFail = BS.unpack (BC.pack "fail")
let hr = BS.unpack (hrp rawd)
decodeExchangeAddress :: T.Text -> Maybe ExchangeAddress
decodeExchangeAddress ex = do
if (T.length ex) > 1
then do
let rawd = decodeBech32 (E.encodeUtf8 ex)
let tMain = BS.unpack (BC.pack "tex")
let tTest = BS.unpack (BC.pack "textest")
let tFail = BS.unpack (BC.pack "fail")
let hr = BS.unpack (hrp rawd)
if hr /= tFail
then do
let transparentReceiver = bytes rawd
if hr == tMain
if hr == tMain
then Just $
ExchangeAddress MainNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver)
else do
if hr == tTest
else do
if hr == tTest
then Just $
ExchangeAddress TestNet $
TransparentReceiver P2PKH (fromRawBytes transparentReceiver)
else Nothing
TransparentReceiver
P2PKH
(fromRawBytes transparentReceiver)
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_memo :: !BS.ByteString -- ^ The decoded shielded memo field.
, a_nullifier :: !HexString -- ^ The calculated nullifier
, a_rho :: !BS.ByteString
, a_rseed :: !Rseed
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
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
-- | Class to represent types with a bytestring representation
class ToBytes a where

View file

@ -5,7 +5,7 @@ cabal-version: 3.0
-- see: https://github.com/sol/hpack
name: zcash-haskell
version: 0.6.0.0
version: 0.6.1.0
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>
category: Blockchain