Viewing Keys #104
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/),
|
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.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]
|
## [0.7.7.0]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -135,7 +135,7 @@ use zcash_primitives::{
|
||||||
|
|
||||||
use zcash_address::{
|
use zcash_address::{
|
||||||
Network,
|
Network,
|
||||||
unified::{Address, Encoding, Ufvk, Container, Fvk, Receiver},
|
unified::{Address, Encoding, Ufvk, Uivk, Ivk, Container, Fvk, Receiver},
|
||||||
ZcashAddress
|
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)]
|
#[derive(Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub struct Hsvk {
|
pub struct Hsvk {
|
||||||
vk: Vec<u8>,
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
|
pub extern "C" fn rust_wrapper_sapling_esk_decrypt(
|
||||||
key: *const u8,
|
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
|
{# fun unsafe rust_wrapper_orchard_note_decrypt as rustWrapperOrchardNoteDecode
|
||||||
{ toBorshVar* `BS.ByteString'&
|
{ toBorshVar* `BS.ByteString'&
|
||||||
, toBorshVar* `OrchardAction'&
|
, 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
|
module ZcashHaskell.Keys where
|
||||||
|
|
||||||
import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
|
import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
|
||||||
|
import Crypto.Secp256k1 (createContext)
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
|
import Data.HexString (hexBytes)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Encoding as E
|
||||||
|
import Data.Word (Word8(..))
|
||||||
import Foreign.Rust.Marshall.Variable
|
import Foreign.Rust.Marshall.Variable
|
||||||
( withBorshVarBuffer
|
( withBorshVarBuffer
|
||||||
, withPureBorshVarBuffer
|
, 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.
|
-- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses.
|
||||||
generateWalletSeedPhrase :: IO Phrase
|
generateWalletSeedPhrase :: IO Phrase
|
||||||
|
@ -36,3 +65,97 @@ getWalletSeed p =
|
||||||
where
|
where
|
||||||
result :: Seed
|
result :: Seed
|
||||||
result = (withPureBorshVarBuffer . rustWrapperGetSeed) p
|
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
|
import C.Zcash
|
||||||
( rustWrapperCombineOrchardNodes
|
( rustWrapperCombineOrchardNodes
|
||||||
|
, rustWrapperCreateOrchardFvk
|
||||||
|
, rustWrapperCreateOrchardIvk
|
||||||
, rustWrapperGenOrchardReceiver
|
, rustWrapperGenOrchardReceiver
|
||||||
, rustWrapperGenOrchardSpendKey
|
, rustWrapperGenOrchardSpendKey
|
||||||
, rustWrapperGetOrchardRootTest
|
, rustWrapperGetOrchardRootTest
|
||||||
|
@ -37,6 +39,7 @@ import C.Zcash
|
||||||
, rustWrapperReadOrchardWitnessAnchor
|
, rustWrapperReadOrchardWitnessAnchor
|
||||||
, rustWrapperUADecode
|
, rustWrapperUADecode
|
||||||
, rustWrapperUfvkDecode
|
, rustWrapperUfvkDecode
|
||||||
|
, rustWrapperUivkDecode
|
||||||
, rustWrapperUpdateOrchardWitness
|
, rustWrapperUpdateOrchardWitness
|
||||||
)
|
)
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
|
@ -157,6 +160,15 @@ decodeUfvk str =
|
||||||
where
|
where
|
||||||
decodedKey = (withPureBorshVarBuffer . rustWrapperUfvkDecode) str
|
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
|
-- | Check if the given UVK matches the UA given
|
||||||
matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool
|
matchOrchardAddress :: BS.ByteString -> BS.ByteString -> Bool
|
||||||
matchOrchardAddress = rustWrapperOrchardCheck
|
matchOrchardAddress = rustWrapperOrchardCheck
|
||||||
|
@ -337,3 +349,25 @@ compareAddress a u =
|
||||||
Sapling s -> s_rec u == Just (sa_receiver s) && ua_net u == net_type s
|
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
|
Transparent t -> t_rec u == Just (ta_receiver t) && ua_net u == ta_network t
|
||||||
Exchange x -> False
|
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
|
import C.Zcash
|
||||||
( rustWrapperCombineSaplingNodes
|
( rustWrapperCombineSaplingNodes
|
||||||
|
, rustWrapperCreateSaplingFvk
|
||||||
|
, rustWrapperCreateSaplingIvk
|
||||||
, rustWrapperDecodeSaplingAddress
|
, rustWrapperDecodeSaplingAddress
|
||||||
, rustWrapperGetSaplingRootTest
|
, rustWrapperGetSaplingRootTest
|
||||||
, rustWrapperIsShielded
|
, rustWrapperIsShielded
|
||||||
|
@ -318,3 +320,25 @@ decodeSaplingAddress sapling_address = do
|
||||||
where
|
where
|
||||||
sa =
|
sa =
|
||||||
withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address
|
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 anyclass (Data.Structured.Show)
|
||||||
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct UnifiedFullViewingKey
|
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)
|
-- | 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
|
data OrchardAction = OrchardAction
|
||||||
{ nf :: !HexString -- ^ The nullifier of the input note
|
{ 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
|
||||||
import Test.Hspec.QuickCheck
|
import Test.Hspec.QuickCheck
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
|
import ZcashHaskell.Keys
|
||||||
|
( deriveUfvk
|
||||||
|
, deriveUivk
|
||||||
|
, generateWalletSeedPhrase
|
||||||
|
, getWalletSeed
|
||||||
|
)
|
||||||
import ZcashHaskell.Orchard
|
import ZcashHaskell.Orchard
|
||||||
import ZcashHaskell.Sapling
|
import ZcashHaskell.Sapling
|
||||||
( decodeSaplingAddress
|
( decodeSaplingAddress
|
||||||
|
@ -1162,6 +1167,33 @@ main = do
|
||||||
Just addr -> do
|
Just addr -> do
|
||||||
let eadr = decodeExchangeAddress (E.encodeUtf8 addr)
|
let eadr = decodeExchangeAddress (E.encodeUtf8 addr)
|
||||||
eadr `shouldNotBe` Nothing
|
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
|
-- | Properties
|
||||||
prop_PhraseLength :: Property
|
prop_PhraseLength :: Property
|
||||||
|
|
|
@ -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.7.7.0
|
version: 0.7.8.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
|
||||||
|
|
Loading…
Reference in a new issue