Viewing Keys (#104)
This PR contains the code to generate Unified Full Viewing Keys and Unified Incoming Viewing Keys. Reviewed-on: #104 Co-authored-by: Rene Vergara <rene@vergara.network> Co-committed-by: Rene Vergara <rene@vergara.network>
This commit is contained in:
parent
7d3ae36d2b
commit
cfa862ec94
9 changed files with 416 additions and 4 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -5,6 +5,17 @@ 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.7.8.0]
|
||||
|
||||
## Added
|
||||
|
||||
- New `UnifiedIncomingViewingKey` type
|
||||
- Functions to derive Orchard full viewing key
|
||||
- Functions to derive Sapling full viewing key
|
||||
- Functions to derive transparent "full viewing key"
|
||||
- Functions to encode Unified Full Viewing Keys
|
||||
- Functions to encode Unified Incoming Viewing Keys
|
||||
|
||||
## [0.7.7.0]
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -135,7 +135,7 @@ use zcash_primitives::{
|
|||
|
||||
use zcash_address::{
|
||||
Network,
|
||||
unified::{Address, Encoding, Ufvk, Container, Fvk, Receiver},
|
||||
unified::{Address, Encoding, Ufvk, Uivk, Ivk, Container, Fvk, Receiver},
|
||||
ZcashAddress
|
||||
};
|
||||
|
||||
|
@ -690,6 +690,35 @@ impl Hufvk {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Huivk {
|
||||
net: u8,
|
||||
orchard: Vec<u8>,
|
||||
sapling: Vec<u8>,
|
||||
transparent: Vec<u8>
|
||||
}
|
||||
|
||||
impl<RW> ToHaskell<RW> for Huivk {
|
||||
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
|
||||
self.serialize(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Huivk {
|
||||
fn add_key_section(&mut self, ivk: &Ivk) {
|
||||
if let Ivk::Orchard(v) = ivk {
|
||||
self.orchard = v.to_vec();
|
||||
}
|
||||
if let Ivk::Sapling(w) = ivk {
|
||||
self.sapling = w.to_vec();
|
||||
}
|
||||
if let Ivk::P2pkh(x) = ivk {
|
||||
self.transparent = x.to_vec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Hsvk {
|
||||
vk: Vec<u8>,
|
||||
|
@ -968,6 +997,34 @@ pub extern "C" fn rust_wrapper_ufvk_decode(
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_wrapper_uivk_decode(
|
||||
input: *const u8,
|
||||
input_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: &mut usize
|
||||
) {
|
||||
let input: String = marshall_from_haskell_var(input, input_len, RW);
|
||||
let dec_key = Uivk::decode(&input);
|
||||
match dec_key {
|
||||
Ok((n, uivk)) => {
|
||||
let x = match n {
|
||||
Network::Main => 1,
|
||||
Network::Test => 2,
|
||||
Network::Regtest => 3
|
||||
};
|
||||
let mut hk = Huivk { net: x, orchard: vec![0], sapling: vec![0], transparent: vec![0] };
|
||||
let ivks = uivk.items();
|
||||
ivks.iter().for_each(|k| hk.add_key_section(k));
|
||||
marshall_to_haskell_var(&hk, out, out_len, RW);
|
||||
}
|
||||
Err(_e) => {
|
||||
let hk0 = Hufvk { net: 0, orchard: vec![0], sapling: vec![0], transparent: vec![0] };
|
||||
marshall_to_haskell_var(&hk0, out, out_len, RW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
|
||||
key: *const u8,
|
||||
|
@ -2508,3 +2565,87 @@ pub extern "C" fn rust_wrapper_create_transaction(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_wrapper_create_orchard_fvk(
|
||||
orch_in: *const u8,
|
||||
orch_in_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: &mut usize
|
||||
){
|
||||
let input: Vec<u8> = marshall_from_haskell_var(orch_in, orch_in_len, RW);
|
||||
let sk = SpendingKey::from_bytes(to_array(input));
|
||||
if sk.is_some().into() {
|
||||
let fvk = FullViewingKey::from(&sk.unwrap());
|
||||
let x = Hhex {bytes: fvk.to_bytes().to_vec()};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
} else {
|
||||
let x = Hhex {bytes: vec![0]};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_wrapper_create_orchard_ivk(
|
||||
orch_in: *const u8,
|
||||
orch_in_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: &mut usize
|
||||
){
|
||||
let input: Vec<u8> = marshall_from_haskell_var(orch_in, orch_in_len, RW);
|
||||
let sk = SpendingKey::from_bytes(to_array(input));
|
||||
if sk.is_some().into() {
|
||||
let fvk = FullViewingKey::from(&sk.unwrap()).to_ivk(Scope::External);
|
||||
let x = Hhex {bytes: fvk.to_bytes().to_vec()};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
} else {
|
||||
let x = Hhex {bytes: vec![0]};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_wrapper_create_sapling_fvk(
|
||||
sap_in: *const u8,
|
||||
sap_in_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: &mut usize
|
||||
){
|
||||
let input: Vec<u8> = marshall_from_haskell_var(sap_in, sap_in_len, RW);
|
||||
let in_bytes: [u8; 169] = to_array(input);
|
||||
let sk = ExtendedSpendingKey::from_bytes(&in_bytes);
|
||||
match sk {
|
||||
Ok(k) => {
|
||||
let fvk = k.to_diversifiable_full_viewing_key();
|
||||
let x = Hhex {bytes: fvk.to_bytes().to_vec()};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
},
|
||||
Err(_e) => {
|
||||
let x = Hhex {bytes: vec![0]};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_wrapper_create_sapling_ivk(
|
||||
sap_in: *const u8,
|
||||
sap_in_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: &mut usize
|
||||
){
|
||||
let input: Vec<u8> = marshall_from_haskell_var(sap_in, sap_in_len, RW);
|
||||
let in_bytes: [u8; 169] = to_array(input);
|
||||
let sk = ExtendedSpendingKey::from_bytes(&in_bytes);
|
||||
match sk {
|
||||
Ok(k) => {
|
||||
let ivk = k.to_diversifiable_full_viewing_key().to_external_ivk();
|
||||
let x = Hhex {bytes: ivk.to_bytes().to_vec()};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
},
|
||||
Err(_e) => {
|
||||
let x = Hhex {bytes: vec![0]};
|
||||
marshall_to_haskell_var(&x, out, out_len, RW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,13 @@ import ZcashHaskell.Types
|
|||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_uivk_decode as rustWrapperUivkDecode
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, getVarBuffer `Buffer UnifiedIncomingViewingKey'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, toBorshVar* `OrchardAction'&
|
||||
|
@ -410,3 +417,31 @@ import ZcashHaskell.Types
|
|||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_create_orchard_fvk as rustWrapperCreateOrchardFvk
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, getVarBuffer `Buffer HexString'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_create_orchard_ivk as rustWrapperCreateOrchardIvk
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, getVarBuffer `Buffer HexString'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_create_sapling_fvk as rustWrapperCreateSaplingFvk
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, getVarBuffer `Buffer HexString'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
||||
{# fun unsafe rust_wrapper_create_sapling_ivk as rustWrapperCreateSaplingIvk
|
||||
{ toBorshVar* `BS.ByteString'&
|
||||
, getVarBuffer `Buffer HexString'&
|
||||
}
|
||||
-> `()'
|
||||
#}
|
||||
|
|
|
@ -15,13 +15,42 @@
|
|||
module ZcashHaskell.Keys where
|
||||
|
||||
import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
|
||||
import Crypto.Secp256k1 (createContext)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.HexString (hexBytes)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import Data.Word (Word8(..))
|
||||
import Foreign.Rust.Marshall.Variable
|
||||
( withBorshVarBuffer
|
||||
, withPureBorshVarBuffer
|
||||
)
|
||||
import ZcashHaskell.Types (Phrase, Seed(..), ToBytes(..))
|
||||
import Haskoin.Address.Base58 (decodeBase58)
|
||||
import Haskoin.Crypto.Keys.Extended
|
||||
( DerivPath(..)
|
||||
, DerivPathI(..)
|
||||
, XPubKey(..)
|
||||
, derivePath
|
||||
, deriveXPubKey
|
||||
, xPubExport
|
||||
)
|
||||
import Haskoin.Network.Constants (btc)
|
||||
import ZcashHaskell.Orchard (deriveOrchardFvk, deriveOrchardIvk)
|
||||
import ZcashHaskell.Sapling (deriveSaplingFvk, deriveSaplingIvk)
|
||||
import ZcashHaskell.Types
|
||||
( OrchardSpendingKey(..)
|
||||
, Phrase
|
||||
, SaplingSpendingKey(..)
|
||||
, Seed(..)
|
||||
, ToBytes(..)
|
||||
, TransparentSpendingKey(..)
|
||||
, ZcashNet(..)
|
||||
, uniFullViewingKeyHrp
|
||||
, uniIncomingViewingKeyHrp
|
||||
, uniTestFullViewingKeyHrp
|
||||
, uniTestIncomingViewingKeyHrp
|
||||
)
|
||||
import ZcashHaskell.Utils (encodeBech32m, f4Jumble)
|
||||
|
||||
-- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses.
|
||||
generateWalletSeedPhrase :: IO Phrase
|
||||
|
@ -36,3 +65,97 @@ getWalletSeed p =
|
|||
where
|
||||
result :: Seed
|
||||
result = (withPureBorshVarBuffer . rustWrapperGetSeed) p
|
||||
|
||||
-- | Derive a transparent root node for unified viewing keys
|
||||
deriveFullTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
|
||||
deriveFullTransparentNode sk = do
|
||||
ioCtx <- createContext
|
||||
let tPubKey = deriveXPubKey ioCtx sk
|
||||
let tPubKeyBytes = decodeBase58 $ xPubExport btc ioCtx tPubKey
|
||||
case tPubKeyBytes of
|
||||
Nothing -> fail "Unable to get transparent key bytes"
|
||||
Just pb -> return $ BS.takeEnd 65 pb
|
||||
|
||||
-- | Derive a transparent incoming root node for unified incoming viewing keys
|
||||
deriveIncomingTransparentNode :: TransparentSpendingKey -> IO BS.ByteString
|
||||
deriveIncomingTransparentNode sk = do
|
||||
ioCtx <- createContext
|
||||
let path = Deriv :/ 0 :: DerivPath
|
||||
let childPrvKey = derivePath ioCtx path sk
|
||||
let tPubKey = deriveXPubKey ioCtx childPrvKey
|
||||
let tPubKeyBytes = decodeBase58 $ xPubExport btc ioCtx tPubKey
|
||||
case tPubKeyBytes of
|
||||
Nothing -> fail "Unable to get transparent key bytes"
|
||||
Just pb -> return $ BS.takeEnd 65 pb
|
||||
|
||||
-- | Derive a Unified Full Viewing Key
|
||||
deriveUfvk ::
|
||||
ZcashNet
|
||||
-> OrchardSpendingKey
|
||||
-> SaplingSpendingKey
|
||||
-> TransparentSpendingKey
|
||||
-> IO T.Text
|
||||
deriveUfvk net okey skey tkey = do
|
||||
tSec <- deriveFullTransparentNode tkey
|
||||
let oSec = deriveOrchardFvk okey
|
||||
let sSec = deriveSaplingFvk skey
|
||||
case oSec of
|
||||
Nothing -> fail "Unable to derive Orchard viewing key"
|
||||
Just oSec' -> do
|
||||
case sSec of
|
||||
Nothing -> fail "Unable to derive Sapling viewing key"
|
||||
Just sSec' ->
|
||||
return $ encodeVK (hexBytes oSec') (hexBytes sSec') tSec net True
|
||||
|
||||
-- | Derive a Unified Incoming Viewing Key
|
||||
deriveUivk ::
|
||||
ZcashNet
|
||||
-> OrchardSpendingKey
|
||||
-> SaplingSpendingKey
|
||||
-> TransparentSpendingKey
|
||||
-> IO T.Text
|
||||
deriveUivk net okey skey tkey = do
|
||||
tSec <- deriveIncomingTransparentNode tkey
|
||||
let oSec = deriveOrchardIvk okey
|
||||
let sSec = deriveSaplingIvk skey
|
||||
case oSec of
|
||||
Nothing -> fail "Unable to derive Orchard viewing key"
|
||||
Just oSec' -> do
|
||||
case sSec of
|
||||
Nothing -> fail "Unable to derive Sapling viewing key"
|
||||
Just sSec' ->
|
||||
return $ encodeVK (hexBytes oSec') (hexBytes sSec') tSec net False
|
||||
|
||||
-- | Encode a Unified Viewing Key per [ZIP-316](https://zips.z.cash/zip-0316)
|
||||
encodeVK ::
|
||||
BS.ByteString -- ^ Orchard FVK
|
||||
-> BS.ByteString -- ^ Sapling FVK
|
||||
-> BS.ByteString -- ^ Transparent root node
|
||||
-> ZcashNet -- ^ Network
|
||||
-> Bool -- ^ Full?
|
||||
-> T.Text
|
||||
encodeVK ovk svk tvk net full = encodeBech32m (E.encodeUtf8 hr) b
|
||||
where
|
||||
tReceiver = packReceiver 0x00 $ Just tvk
|
||||
b = f4Jumble $ tReceiver <> sReceiver <> oReceiver <> padding
|
||||
hr =
|
||||
if full
|
||||
then case net of
|
||||
MainNet -> uniFullViewingKeyHrp
|
||||
TestNet -> uniTestFullViewingKeyHrp
|
||||
else case net of
|
||||
MainNet -> uniIncomingViewingKeyHrp
|
||||
TestNet -> uniTestIncomingViewingKeyHrp
|
||||
sReceiver = packReceiver 0x02 $ Just svk
|
||||
oReceiver = packReceiver 0x03 $ Just ovk
|
||||
padding = E.encodeUtf8 $ T.justifyLeft 16 '\NUL' hr
|
||||
packReceiver :: Word8 -> Maybe BS.ByteString -> BS.ByteString
|
||||
packReceiver typeCode receiver' =
|
||||
case receiver' of
|
||||
Just receiver ->
|
||||
if BS.length receiver > 1
|
||||
then BS.singleton typeCode `BS.append`
|
||||
(BS.singleton . toEnum . BS.length) receiver `BS.append`
|
||||
receiver
|
||||
else BS.empty
|
||||
Nothing -> BS.empty
|
||||
|
|
|
@ -19,6 +19,8 @@ module ZcashHaskell.Orchard where
|
|||
|
||||
import C.Zcash
|
||||
( rustWrapperCombineOrchardNodes
|
||||
, rustWrapperCreateOrchardFvk
|
||||
, rustWrapperCreateOrchardIvk
|
||||
, rustWrapperGenOrchardReceiver
|
||||
, rustWrapperGenOrchardSpendKey
|
||||
, rustWrapperGetOrchardRootTest
|
||||
|
@ -37,6 +39,7 @@ import C.Zcash
|
|||
, rustWrapperReadOrchardWitnessAnchor
|
||||
, rustWrapperUADecode
|
||||
, rustWrapperUfvkDecode
|
||||
, rustWrapperUivkDecode
|
||||
, rustWrapperUpdateOrchardWitness
|
||||
)
|
||||
import qualified Data.ByteString as BS
|
||||
|
@ -157,6 +160,15 @@ decodeUfvk str =
|
|||
where
|
||||
decodedKey = (withPureBorshVarBuffer . rustWrapperUfvkDecode) str
|
||||
|
||||
-- | Attempts to decode the given bytestring into a Unified Full Viewing Key
|
||||
decodeUivk :: BS.ByteString -> Maybe UnifiedIncomingViewingKey
|
||||
decodeUivk str =
|
||||
case i_net decodedKey of
|
||||
0 -> Nothing
|
||||
_ -> Just decodedKey
|
||||
where
|
||||
decodedKey = (withPureBorshVarBuffer . rustWrapperUivkDecode) str
|
||||
|
||||
-- | Check if the given UVK matches the UA given
|
||||
matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool
|
||||
matchOrchardAddress = rustWrapperOrchardCheck
|
||||
|
@ -337,3 +349,25 @@ compareAddress a u =
|
|||
Sapling s -> s_rec u == Just (sa_receiver s) && ua_net u == net_type s
|
||||
Transparent t -> t_rec u == Just (ta_receiver t) && ua_net u == ta_network t
|
||||
Exchange x -> False
|
||||
|
||||
-- | Derive an Orchard Full Viewing Key
|
||||
deriveOrchardFvk ::
|
||||
OrchardSpendingKey -- ^ The Orchard spending key
|
||||
-> Maybe HexString
|
||||
deriveOrchardFvk sk =
|
||||
if BS.length (hexBytes r) > 1
|
||||
then Just r
|
||||
else Nothing
|
||||
where
|
||||
r = withPureBorshVarBuffer $ rustWrapperCreateOrchardFvk $ getBytes sk
|
||||
|
||||
-- | Derive an Orchard Incoming Viewing Key
|
||||
deriveOrchardIvk ::
|
||||
OrchardSpendingKey -- ^ The Orchard spending key
|
||||
-> Maybe HexString
|
||||
deriveOrchardIvk sk =
|
||||
if BS.length (hexBytes r) > 1
|
||||
then Just r
|
||||
else Nothing
|
||||
where
|
||||
r = withPureBorshVarBuffer $ rustWrapperCreateOrchardIvk $ getBytes sk
|
||||
|
|
|
@ -19,6 +19,8 @@ module ZcashHaskell.Sapling where
|
|||
|
||||
import C.Zcash
|
||||
( rustWrapperCombineSaplingNodes
|
||||
, rustWrapperCreateSaplingFvk
|
||||
, rustWrapperCreateSaplingIvk
|
||||
, rustWrapperDecodeSaplingAddress
|
||||
, rustWrapperGetSaplingRootTest
|
||||
, rustWrapperIsShielded
|
||||
|
@ -318,3 +320,25 @@ decodeSaplingAddress sapling_address = do
|
|||
where
|
||||
sa =
|
||||
withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address
|
||||
|
||||
-- | Derive a Sapling Full Viewing Key
|
||||
deriveSaplingFvk ::
|
||||
SaplingSpendingKey -- ^ The Sapling spending key
|
||||
-> Maybe HexString
|
||||
deriveSaplingFvk sk =
|
||||
if BS.length (hexBytes r) > 1
|
||||
then Just r
|
||||
else Nothing
|
||||
where
|
||||
r = withPureBorshVarBuffer $ rustWrapperCreateSaplingFvk $ getBytes sk
|
||||
|
||||
-- | Derive a Sapling Incoming Viewing Key
|
||||
deriveSaplingIvk ::
|
||||
SaplingSpendingKey -- ^ The Sapling spending key
|
||||
-> Maybe HexString
|
||||
deriveSaplingIvk sk =
|
||||
if BS.length (hexBytes r) > 1
|
||||
then Just r
|
||||
else Nothing
|
||||
where
|
||||
r = withPureBorshVarBuffer $ rustWrapperCreateSaplingIvk $ getBytes sk
|
||||
|
|
|
@ -696,6 +696,18 @@ data UnifiedFullViewingKey = UnifiedFullViewingKey
|
|||
deriving anyclass (Data.Structured.Show)
|
||||
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct UnifiedFullViewingKey
|
||||
|
||||
-- | Type to represent a Unified Incoming Viewing Key
|
||||
data UnifiedIncomingViewingKey = UnifiedIncomingViewingKey
|
||||
{ i_net :: !Word8 -- ^ Number representing the network the key belongs to. @1@ for @mainnet@, @2@ for @testnet@ and @3@ for @regtestnet@.
|
||||
, i_o_key :: !BS.ByteString -- ^ Raw bytes of the Orchard Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
|
||||
, i_s_key :: !BS.ByteString -- ^ Raw bytes of the Sapling Incoming Viewing Key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
|
||||
, i_t_key :: !BS.ByteString -- ^ Raw bytes of the P2PKH chain code and public key as specified in [ZIP-316](https://zips.z.cash/zip-0316)
|
||||
} deriving stock (Eq, Prelude.Show, GHC.Generic)
|
||||
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
||||
deriving anyclass (Data.Structured.Show)
|
||||
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct
|
||||
UnifiedIncomingViewingKey
|
||||
|
||||
-- | Type to represent an Orchard Action as provided by the @getrawtransaction@ RPC method of @zcashd@, and defined in the [Zcash Protocol](https://zips.z.cash/protocol/protocol.pdf)
|
||||
data OrchardAction = OrchardAction
|
||||
{ nf :: !HexString -- ^ The nullifier of the input note
|
||||
|
|
34
test/Spec.hs
34
test/Spec.hs
|
@ -45,7 +45,12 @@ import Test.HUnit
|
|||
import Test.Hspec
|
||||
import Test.Hspec.QuickCheck
|
||||
import Test.QuickCheck
|
||||
import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
|
||||
import ZcashHaskell.Keys
|
||||
( deriveUfvk
|
||||
, deriveUivk
|
||||
, generateWalletSeedPhrase
|
||||
, getWalletSeed
|
||||
)
|
||||
import ZcashHaskell.Orchard
|
||||
import ZcashHaskell.Sapling
|
||||
( decodeSaplingAddress
|
||||
|
@ -1162,6 +1167,33 @@ main = do
|
|||
Just addr -> do
|
||||
let eadr = decodeExchangeAddress (E.encodeUtf8 addr)
|
||||
eadr `shouldNotBe` Nothing
|
||||
describe "Generate Viewing Keys" $ do
|
||||
let p =
|
||||
Phrase
|
||||
"cloth swing left trap random tornado have great onion element until make shy dad success art tuition canvas thunder apple decade elegant struggle invest"
|
||||
let seed = getWalletSeed p
|
||||
let oK = genOrchardSpendingKey (fromJust seed) MainNetCoin 0
|
||||
let sK = genSaplingSpendingKey (fromJust seed) MainNetCoin 0
|
||||
it "Generate FVK" $ do
|
||||
tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
|
||||
case oK of
|
||||
Nothing -> assertFailure "Failed to generate Orchard SK"
|
||||
Just o ->
|
||||
case sK of
|
||||
Nothing -> assertFailure "Failed to generate Sapling SK"
|
||||
Just s -> do
|
||||
fvk <- deriveUfvk MainNet o s tK
|
||||
decodeUfvk (E.encodeUtf8 fvk) `shouldNotBe` Nothing
|
||||
it "Generate IVK" $ do
|
||||
tK <- genTransparentPrvKey (fromJust seed) MainNetCoin 0
|
||||
case oK of
|
||||
Nothing -> assertFailure "Failed to generate Orchard SK"
|
||||
Just o ->
|
||||
case sK of
|
||||
Nothing -> assertFailure "Failed to generate Sapling SK"
|
||||
Just s -> do
|
||||
ivk <- deriveUivk MainNet o s tK
|
||||
decodeUivk (E.encodeUtf8 ivk) `shouldNotBe` Nothing
|
||||
|
||||
-- | Properties
|
||||
prop_PhraseLength :: Property
|
||||
|
|
|
@ -5,7 +5,7 @@ cabal-version: 3.0
|
|||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: zcash-haskell
|
||||
version: 0.7.7.0
|
||||
version: 0.7.8.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
|
||||
|
|
Loading…
Reference in a new issue