Compare commits

..

No commits in common. "1e795dbcea5ea0095341a2c7d3bf5934898488d5" and "477817f37f414a459d70b1f89e950f85438ca267" have entirely different histories.

10 changed files with 252 additions and 322 deletions

View file

@ -19,7 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Constants for Zcash protocol
- Types for Spending Keys and Receivers for Sapling and Orchard
- Function to generate an Orchard receiver
- Function to generate a Sapling receiver
### Changed

View file

@ -1314,6 +1314,7 @@ dependencies = [
"borsh 0.10.3",
"f4jumble",
"haskell-ffi",
"nom",
"orchard 0.7.1",
"proc-macro2",
"zcash_address 0.2.0",

View file

@ -17,6 +17,7 @@ zcash_primitives = "0.13.0"
zcash_client_backend = "0.10.0"
zip32 = "0.1.0"
proc-macro2 = "1.0.66"
nom = "7.1.3"
[features]
capi = []

View file

@ -24,23 +24,15 @@ use haskell_ffi::{
use zip32;
use zcash_primitives::{
zip32::{
Scope as SaplingScope,
sapling_find_address,
sapling::DiversifierKey
},
zip32::Scope as SaplingScope,
zip339::{Count, Mnemonic},
transaction::components::sapling::{
GrothProofBytes,
OutputDescription
OutputDescription,
},
sapling::{
PaymentAddress,
keys::{
PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
ExpandedSpendingKey,
FullViewingKey as SaplingFullViewingKey
},
keys::PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey,
note_encryption::SaplingDomain
},
transaction::Transaction,
@ -57,12 +49,13 @@ use zcash_address::{
ZcashAddress
};
use zcash_client_backend::keys::sapling::{
ExtendedFullViewingKey,
ExtendedSpendingKey
};
use zcash_client_backend::keys::{
sapling,
sapling::ExtendedFullViewingKey,
sapling::ExtendedSpendingKey};
use zcash_primitives::zip32::{ AccountId, DiversifierIndex };
use std::slice;
use orchard::{
Action,
@ -631,6 +624,7 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey(
out: *mut u8,
out_len: &mut usize
){
println!("Starting extended spending key generation....");
let seed: Vec<u8> = marshall_from_haskell_var(iseed, iseed_len, RW);
if ( seed.len() != 64 ) {
// invalid seed length
@ -640,7 +634,8 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey(
// Returns a byte array (169 bytes)
let su8 = &seed;
let seedu8 : &[u8] = &su8;
let extsk: ExtendedSpendingKey = ExtendedSpendingKey::master(&seedu8);
println!("Seed : {:?}\n", &seedu8);
let extsk: ExtendedSpendingKey = sapling::ExtendedSpendingKey::master(&seedu8);
let extsk_bytes = extsk.to_bytes().to_vec();
marshall_to_haskell_var(&extsk_bytes, out, out_len, RW);
}
@ -650,25 +645,94 @@ pub extern "C" fn rust_wrapper_sapling_spendingkey(
pub extern "C" fn rust_wrapper_sapling_paymentaddress(
extspk: *const u8,
extspk_len: usize,
div_ix: u32,
// divIx: u32,
out: *mut u8,
out_len: &mut usize
){
let extspk: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW);
let expsk = ExpandedSpendingKey::from_spending_key(&extspk);
let fvk = SaplingFullViewingKey::from_expanded_spending_key(&expsk);
let dk = DiversifierKey::master(&extspk);
let result = sapling_find_address(&fvk, &dk, DiversifierIndex::from(div_ix));
match result {
Some((_d, p_address)) => {
marshall_to_haskell_var(&p_address.to_bytes().to_vec(), out, out_len, RW);
},
None => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
// println!("Starting paymentAddress generation....");
let extspkb: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW);
if ( extspkb.len() != 169 ) {
// invalid ExtendedSpenndingKey Array length
println!("Invalid ExtendedSpendingKey....");
marshall_to_haskell_var(&vec![0], out, out_len, RW);
} else {
// Process
// println!("Extended Spending Key validated, continue ....");
let extspkbu8 = &extspkb;
let xsku8 : &[u8] = &extspkbu8;
let xsk = match sapling::ExtendedSpendingKey::from_bytes(&xsku8) {
Ok ( x ) => x,
Err ( err ) => {
// Error recovering ExtendedSpendingKey from bytes
marshall_to_haskell_var(&vec![0], out, out_len, RW);
return
}
};
// Obtain the DiversifiableFullViewingKey from ExtendedSpendingKey
let dfvk = xsk.to_diversifiable_full_viewing_key();
// Obtain the Address from the DiversifiableFullViewingKey
// println!("dfvk -> \n{:?}", dfvk);
// let divIndex : DiversifierIndex = divIx.into();
// println!("divIndex -> {:?}", divIndex);
let (divIx, paddress) = dfvk.default_address();
// println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx);
let pmtAddress = paddress.to_bytes();
// println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress);
marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW);
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_find_paymentaddress(
extspk: *const u8,
extspk_len: usize,
divIx: u32,
out: *mut u8,
out_len: &mut usize
){
println!("Starting paymentAddress generation using Find_Address()....");
let extspkb: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW);
if ( extspkb.len() != 169 ) {
// invalid ExtendedSpenndingKey Array length
println!("Invalid ExtendedSpendingKey....");
marshall_to_haskell_var(&vec![0], out, out_len, RW);
} else {
// Process
println!("Extended Spending Key validated, continue ....");
let extspkbu8 = &extspkb;
let xsku8 : &[u8] = &extspkbu8;
let xsk = match sapling::ExtendedSpendingKey::from_bytes(&xsku8) {
Ok ( x ) => x,
Err ( err ) => {
// Error recovering ExtendedSpendingKey from bytes
marshall_to_haskell_var(&vec![0], out, out_len, RW);
return
}
};
println!("Obtain a DiversifierIndex from u32 parameter {:?}....",divIx);
let diversifierIndex : DiversifierIndex = divIx.into();
// Obtain the DiversifiableFullViewingKey from ExtendedSpendingKey
let dfvk = xsk.to_diversifiable_full_viewing_key();
// Obtain the Address from the DiversifiableFullViewingKey
// println!("dfvk -> \n{:?}", dfvk);
// let divIndex : DiversifierIndex = divIx.into();
// println!("divIndex -> {:?}", divIndex);
let result = dfvk.find_address(diversifierIndex)
.map (|(divIx,paddress) | {
println!("Rust pmtAddress - \n{:?}\n\nRust Diversifier - \n{:?}\n", paddress, divIx);
let pmtAddress = paddress.to_bytes();
println!("\nRust pntAddress as byte array -\n{:?}\n", pmtAddress);
marshall_to_haskell_var(&pmtAddress.to_vec(), out, out_len, RW);
//
})
.unwrap_or_else(|| {
// Handle the case where the function returns None
println!("Rust - Error finding payment address.... ");
marshall_to_haskell_var(&vec![0], out, out_len, RW);
return
});
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_derive_orchard_spending_key(
seed: *const u8,

View file

@ -141,7 +141,14 @@ import ZcashHaskell.Types
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_paymentaddress as rustWrapperSaplingPaymentAddress
{# fun unsafe rust_wrapper_sapling_paymentaddress as rustWrapperPaymentAddress
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_sapling_find_paymentaddress as rustWrapperFindPaymentAddress
{ toBorshVar* `BS.ByteString'&
, `Word32'
, getVarBuffer `Buffer (BS.ByteString)'&

View file

@ -21,13 +21,15 @@ import C.Zcash
( rustWrapperIsShielded
, rustWrapperSaplingCheck
, rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress
, rustWrapperSaplingSpendingkey
, rustWrapperPaymentAddress
, rustWrapperFindPaymentAddress
, rustWrapperSaplingVkDecode
, rustWrapperTxParse
)
import Data.Aeson
import qualified Data.ByteString as BS
import Data.ByteString.Lazy as BL
import Data.HexString (HexString(..), toBytes)
import Data.Word
import Foreign.Rust.Marshall.Variable
@ -35,18 +37,16 @@ import Foreign.Rust.Marshall.Variable
, withPureBorshVarBuffer
)
import ZcashHaskell.Types
( AccountId
, CoinType
, DecodedNote(..)
( DecodedNote(..)
, RawData(..)
, RawTxResponse(..)
, SaplingReceiver
, SaplingSpendingKey(..)
, Seed(..)
, SaplingSKeyParams(..)
, ShieldedOutput(..)
, decodeHexText
, AccountId
, CoinType
)
import ZcashHaskell.Utils (decodeBech32)
import ZcashHaskell.Utils
-- | Check if given bytesting is a valid encoded shielded address
isValidShieldedAddress :: BS.ByteString -> Bool
@ -92,25 +92,22 @@ instance FromJSON RawTxResponse where
Just o' -> do
a <- o' .: "actions"
pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b
--
-- | Attempts to obtain a sapling SpendingKey using a HDSeed
genSaplingSpendingKey :: Seed -> Maybe SaplingSpendingKey
-- | Attempts to obtain a sapling SpendinKey using a HDSeed, a Coin Type and an Account ID
genSaplingSpendingKey :: BS.ByteString -> BS.ByteString
genSaplingSpendingKey seed = do
if BS.length res == 196
then Just res
else Nothing
where
res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed)
let res = withPureBorshVarBuffer (rustWrapperSaplingSpendingkey seed )
res
--
-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey
genSaplingPaymentAddress :: BS.ByteString -> BS.ByteString
genSaplingPaymentAddress extspk = do
let pmtaddress = withPureBorshVarBuffer (rustWrapperPaymentAddress extspk )
pmtaddress
--
-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index
genSaplingPaymentAddress :: SaplingSpendingKey -> Int -> Maybe SaplingReceiver
genSaplingPaymentAddress extspk i =
if BS.length res == 43
then Just res
else Nothing
where
res =
withPureBorshVarBuffer
(rustWrapperSaplingPaymentAddress extspk (fromIntegral i))
-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey
genSaplingFindPaymentAddress :: BS.ByteString -> Word32 -> BS.ByteString
genSaplingFindPaymentAddress extspk divIx = do
let pmtaddress = withPureBorshVarBuffer (rustWrapperFindPaymentAddress extspk divIx)
pmtaddress

View file

@ -346,6 +346,16 @@ data DecodedNote = DecodedNote
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote
-- } Type to represent parameters to call rust zcash library
data SaplingSKeyParams = SaplingSKeyParams
{ hdseed :: BS.ByteString -- ^ seed required for sappling spending key generation
, coin_type :: Word32 -- ^ coin_type
, account_id :: Word32 -- ^ account id
} deriving stock (Eq, Prelude.Show, GHC.Generic)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
deriving anyclass (Data.Structured.Show)
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingSKeyParams
-- * Helpers
-- | Helper function to turn a hex-encoded string to bytestring
decodeHexText :: String -> BS.ByteString

View file

@ -37,8 +37,8 @@ import Foreign.Marshal.Array (allocaArray, peekArray)
import Foreign.Ptr (Ptr)
import Data.Word
-- |
-- | Decode the given bytestring using Bech32
decodeBech32 :: BS.ByteString -> RawData
decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode
@ -88,3 +88,13 @@ makeZebraCall host port m params = do
setRequestHost (E.encodeUtf8 host) $
setRequestMethod "POST" defaultRequest
httpJSON myRequest
-- + Misc functions
-- Convert an array of Word8 to a ByteString
word8ArrayToByteString :: [Word8] -> BS.ByteString
word8ArrayToByteString = BS.pack
-- Convert [Word8] to String
word8ToString :: [Word8] -> String
word8ToString = map (toEnum . fromEnum)

View file

@ -41,21 +41,19 @@ import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
import ZcashHaskell.Orchard
import ZcashHaskell.Sapling
( decodeSaplingOutput
, genSaplingPaymentAddress
, genSaplingSpendingKey
, getShieldedOutputs
, isValidSaplingViewingKey
, isValidShieldedAddress
, matchSaplingAddress
, genSaplingSpendingKey
, genSaplingPaymentAddress
, genSaplingFindPaymentAddress
)
import ZcashHaskell.Transparent
--(encodeTransparent)
import ZcashHaskell.Types
( AccountId
, BlockResponse(..)
( BlockResponse(..)
, CoinType(..)
, CoinType
, DecodedNote(..)
, OrchardAction(..)
, Phrase(..)
@ -65,17 +63,20 @@ import ZcashHaskell.Types
, UnifiedAddress(..)
, UnifiedFullViewingKey(..)
, decodeHexText
, AccountId
, CoinType
, getValue
)
import ZcashHaskell.Utils
import Data.Word
import Foreign.C.Types
import Data.Word
import Haskoin.Crypto.Keys.Extended
m2bs :: Maybe BS.ByteString -> BS.ByteString
m2bs x = fromMaybe "" x
main :: IO ()
main = do
hspec $ do
@ -483,251 +484,102 @@ main = do
msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"
describe "Transparent Private and Publicc Key Generation" $ do
it "Obtain a transparent extended private key from HDSeed" $ do
let hdseed =
[ 206
, 61
, 120
, 38
, 206
, 40
, 201
, 62
, 83
, 175
, 151
, 131
, 218
, 141
, 206
, 254
, 28
, 244
, 172
, 213
, 128
, 248
, 156
, 45
, 204
, 44
, 169
, 3
, 162
, 188
, 16
, 173
, 192
, 164
, 96
, 148
, 91
, 52
, 244
, 83
, 149
, 169
, 82
, 196
, 199
, 53
, 177
, 170
, 1
, 6
, 0
, 120
, 170
, 2
, 238
, 219
, 241
, 243
, 172
, 178
, 104
, 81
, 159
, 144
] :: [Word8]
let xtpvk = genTransparentPrvKey (BS.pack hdseed)
let testpvk =
XPrvKey
0
"0000000000"
0
"fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb"
"46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943"
xtpvk `shouldBe` testpvk
let hdseed = [206, 61, 120, 38,
206, 40, 201, 62,
83, 175, 151, 131,
218, 141, 206, 254,
28, 244, 172, 213,
128, 248, 156, 45,
204, 44, 169, 3,
162, 188, 16, 173,
192, 164, 96, 148,
91, 52, 244, 83,
149, 169, 82, 196,
199, 53, 177, 170,
1, 6, 0, 120,
170, 2, 238, 219,
241, 243, 172, 178,
104, 81, 159, 144
] :: [Word8]
let xtpvk = genTransparentPrvKey (word8ArrayToByteString hdseed)
let testpvk = XPrvKey 0 "0000000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943"
xtpvk `shouldBe` testpvk
-- describe "Obtain transparent public key from private key" $ do
it "Obtain a transparent extended public key from private key" $ do
let testpvk =
XPrvKey
0
"0000000000"
0
"fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb"
"46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943"
let testpbk =
XPubKey
0
"00000000"
0
"fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb"
"279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8"
let xtpubkIO = genTransparentPubKey testpvk
xtpubk <- xtpubkIO
let testpvk = XPrvKey 0 "0000000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "46aa0cd24a6e05709591426a4e682dd5406de4e75a39c0f410ee790403880943"
let testpbk = XPubKey 0 "00000000" 0 "fb5b9b89d3e9dfdebeaabd15de8fbc7e9a140b7f2de2b4034c2573425d39aceb" "279bda9c704f6da479cedb12c7cf773b3a348569dc1cfa6002526bad67674fd737b84a2bdb1199ecab1c9fed1b9a38aba5ba19259c1510d733a2376118515cd8"
let xtpubkIO = genTransparentPubKey testpvk
xtpubk <- xtpubkIO
---print $ show xtpubk
xtpubk `shouldBe` testpbk
xtpubk `shouldBe` testpbk
describe "Sapling SpendingKey test" $ do
let hdseed =
BS.pack $
[ 206
, 61
, 120
, 38
, 206
, 40
, 201
, 62
, 83
, 175
, 151
, 131
, 218
, 141
, 206
, 254
, 28
, 244
, 172
, 213
, 128
, 248
, 156
, 45
, 204
, 44
, 169
, 3
, 162
, 188
, 16
, 173
, 192
, 164
, 96
, 148
, 91
, 52
, 244
, 83
, 149
, 169
, 82
, 196
, 199
, 53
, 177
, 170
, 1
, 6
, 0
, 120
, 170
, 2
, 238
, 219
, 241
, 243
, 172
, 178
, 104
, 81
, 159
, 144
]
--xit "Call function with parameters" $ do
--let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed)
--let msgArr = BS.unpack msg
--if (length msgArr) == 169
--then True
--else False
it "Generate Sapling spending key" $ do
p <- generateWalletSeedPhrase
let s = getWalletSeed p
genSaplingSpendingKey <$> s `shouldNotBe` Nothing
it "Call function with parameters" $ do
let hdseed = [206, 61, 120, 38,
206, 40, 201, 62,
83, 175, 151, 131,
218, 141, 206, 254,
28, 244, 172, 213,
128, 248, 156, 45,
204, 44, 169, 3,
162, 188, 16, 173,
192, 164, 96, 148,
91, 52, 244, 83,
149, 169, 82, 196,
199, 53, 177, 170,
1, 6, 0, 120,
170, 2, 238, 219,
241, 243, 172, 178,
104, 81, 159, 144
] :: [Word8]
let msg = genSaplingSpendingKey (word8ArrayToByteString hdseed)
let msgArr = BS.unpack msg
length msgArr `shouldBe` 169
describe "Sapling Payment Address generation test" $ do
it "Call genSaplingPaymentAddress" $ do
let hdseed1 =
[ 206
, 61
, 120
, 38
, 206
, 40
, 201
, 62
, 83
, 175
, 151
, 131
, 218
, 141
, 206
, 254
, 28
, 244
, 172
, 213
, 128
, 248
, 156
, 45
, 204
, 44
, 169
, 3
, 162
, 188
, 16
, 173
, 192
, 164
, 96
, 148
, 91
, 52
, 244
, 83
, 149
, 169
, 82
, 196
, 199
, 53
, 177
, 170
, 1
, 6
, 0
, 120
, 170
, 2
, 238
, 219
, 241
, 243
, 172
, 178
, 104
, 81
, 159
, 144
] :: [Word8]
p <- generateWalletSeedPhrase
let s = getWalletSeed p
genSaplingPaymentAddress (fromMaybe "" s) 0 `shouldNotBe` Nothing
prop "Sapling receivers are valid" $
forAll genSapArgs $ \(i) -> prop_SaplingReceiver i
it "Call genSaplingPaymentAddress" $ do
let hdseed1 = [206, 61, 120, 38,
206, 40, 201, 62,
83, 175, 151, 131,
218, 141, 206, 254,
28, 244, 172, 213,
128, 248, 156, 45,
204, 44, 169, 3,
162, 188, 16, 173,
192, 164, 96, 148,
91, 52, 244, 83,
149, 169, 82, 196,
199, 53, 177, 170,
1, 6, 0, 120,
170, 2, 238, 219,
241, 243, 172, 178,
104, 81, 159, 144
] :: [Word8]
let msg1 = genSaplingSpendingKey (word8ArrayToByteString hdseed1)
let pmtaddress = genSaplingPaymentAddress msg1 --(word8ArrayToByteString hdseed1)
let msgArr = BS.unpack pmtaddress
length msgArr `shouldBe` 43
describe "Sapling Payment Find Address generation test" $ do
it "Call genSaplingFindPaymentAddress" $ do
let hdseed1 = [206, 61, 120, 38,
206, 40, 201, 62,
83, 175, 151, 131,
218, 141, 206, 254,
28, 244, 172, 213,
128, 248, 156, 45,
204, 44, 169, 3,
162, 188, 16, 173,
192, 164, 96, 148,
91, 52, 244, 83,
149, 169, 82, 196,
199, 53, 177, 170,
1, 6, 0, 120,
170, 2, 238, 219,
241, 243, 172, 178,
104, 81, 159, 144
] :: [Word8]
let msg1 = genSaplingSpendingKey (word8ArrayToByteString hdseed1)
let pmtaddress = genSaplingFindPaymentAddress msg1 0 --(word8ArrayToByteString hdseed1)
let msgArr = BS.unpack pmtaddress
length msgArr `shouldBe` 42
-- | Properties
prop_PhraseLength :: Int -> Property
@ -758,14 +610,6 @@ prop_OrchardReceiver c i j =
let sk = genOrchardSpendingKey (fromMaybe "" s) c i
return $ genOrchardReceiver j (fromMaybe "" sk) =/= Nothing
prop_SaplingReceiver :: Int -> Property
prop_SaplingReceiver i =
ioProperty $ do
p <- generateWalletSeedPhrase
let s = getWalletSeed p
let sk = genSaplingSpendingKey (fromMaybe "" s)
return $ genSaplingPaymentAddress (fromMaybe "" sk) i =/= Nothing
-- | Generators
genOrcArgs :: Gen (CoinType, Int, Int)
genOrcArgs = do
@ -773,7 +617,4 @@ genOrcArgs = do
j <- arbitrarySizedNatural
c <- elements [MainNetCoin, TestNetCoin, RegTestNetCoin]
return (c, i, j)
genSapArgs :: Gen Int
genSapArgs = choose (1, 50)
-- | Arbitrary instances

View file

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