Correct Sapling receiver generation #32

Merged
pitmutt merged 2 commits from rav001 into dev040 2024-03-13 19:25:43 +00:00
5 changed files with 67 additions and 145 deletions

View file

@ -59,11 +59,12 @@ use zcash_address::{
}; };
use zcash_client_backend::keys::sapling::{ use zcash_client_backend::keys::sapling::{
spending_key,
ExtendedFullViewingKey, ExtendedFullViewingKey,
ExtendedSpendingKey ExtendedSpendingKey
}; };
use zcash_primitives::zip32::{ AccountId, DiversifierIndex }; use zcash_primitives::zip32::DiversifierIndex;
use orchard::{ use orchard::{
Action, Action,
@ -627,23 +628,16 @@ pub extern "C" fn rust_wrapper_recover_seed(
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_sapling_spendingkey( pub extern "C" fn rust_wrapper_sapling_spendingkey(
iseed: *const u8, seed: *const u8,
iseed_len: usize, seed_len: usize,
ix: u32, coin_type: u32,
acc_id: u32,
out: *mut u8, out: *mut u8,
out_len: &mut usize out_len: &mut usize
){ ){
let seed: Vec<u8> = marshall_from_haskell_var(iseed, iseed_len, RW); let s: Vec<u8> = marshall_from_haskell_var(seed, seed_len, RW);
let su8 = &seed; let sk = spending_key(&s, coin_type, zcash_primitives::zip32::AccountId::try_from(acc_id).unwrap());
let seedu8 : &[u8] = &su8; marshall_to_haskell_var(&sk.to_bytes().to_vec(), out, out_len, RW);
let extsk: ExtendedSpendingKey = ExtendedSpendingKey::master(&seedu8);
if ix == 0 {
let extsk_bytes = extsk.to_bytes().to_vec();
marshall_to_haskell_var(&extsk_bytes, out, out_len, RW);
} else {
let child_sk = extsk.derive_child(ChildIndex::from_index(ix + (1 << 31)));
marshall_to_haskell_var(&child_sk.to_bytes().to_vec(), out, out_len, RW);
}
} }
#[no_mangle] #[no_mangle]

View file

@ -137,6 +137,7 @@ import ZcashHaskell.Types
{# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey {# fun unsafe rust_wrapper_sapling_spendingkey as rustWrapperSaplingSpendingkey
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, `Word32' , `Word32'
, `Word32'
, getVarBuffer `Buffer (BS.ByteString)'& , getVarBuffer `Buffer (BS.ByteString)'&
} }
-> `()' -> `()'

View file

@ -46,6 +46,7 @@ import ZcashHaskell.Types
, Seed(..) , Seed(..)
, ShieldedOutput(..) , ShieldedOutput(..)
, decodeHexText , decodeHexText
, getValue
) )
import ZcashHaskell.Utils (decodeBech32) import ZcashHaskell.Utils (decodeBech32)
@ -95,15 +96,18 @@ instance FromJSON RawTxResponse where
pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b pure $ RawTxResponse i h (getShieldedOutputs h) a ht c b
-- | Attempts to obtain a sapling SpendingKey using a HDSeed -- | Attempts to obtain a sapling SpendingKey using a HDSeed
genSaplingSpendingKey :: Seed -> Int -> Maybe SaplingSpendingKey genSaplingSpendingKey :: Seed -> CoinType -> Int -> Maybe SaplingSpendingKey
genSaplingSpendingKey seed i = do genSaplingSpendingKey seed c i = do
if BS.length res == 169 if BS.length res == 169
then Just res then Just res
else Nothing else Nothing
where where
res = res =
withPureBorshVarBuffer withPureBorshVarBuffer
(rustWrapperSaplingSpendingkey seed (fromIntegral i)) (rustWrapperSaplingSpendingkey
seed
(fromIntegral $ getValue c)
(fromIntegral i))
-- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index -- | Attempts to generate a sapling Payment Address using an ExtendedSpendingKey and a Diversifier Index
genSaplingPaymentAddress :: Int -> SaplingSpendingKey -> Maybe SaplingReceiver genSaplingPaymentAddress :: Int -> SaplingSpendingKey -> Maybe SaplingReceiver

View file

@ -23,7 +23,9 @@ import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import ZcashHaskell.Types import ZcashHaskell.Types
( TransparentAddress(..) ( AccountId
, Seed
, TransparentAddress(..)
, TransparentType(..) , TransparentType(..)
, ZcashNet(..) , ZcashNet(..)
, getTransparentPrefix , getTransparentPrefix
@ -51,21 +53,19 @@ encodeTransparent zNet t =
checksum = sha256 $ sha256 digest checksum = sha256 $ sha256 digest
-- | Attempts to generate an Extended Private Key from a known HDSeed. -- | Attempts to generate an Extended Private Key from a known HDSeed.
genTransparentPrvKey :: BS.ByteString -> XPrvKey genTransparentPrvKey :: Seed -> AccountId -> IO XPrvKey
genTransparentPrvKey hdseed = do genTransparentPrvKey hdseed i = do
makeXPrvKey hdseed let prvKey = makeXPrvKey hdseed
-- | Attempts to obtain an Extended Public Key from a known Extended Private Key
genTransparentPubKey :: XPrvKey -> IO XPubKey
genTransparentPubKey xpvk = do
ioCtx <- createContext ioCtx <- createContext
let xpubk = deriveXPubKey ioCtx xpvk return $ hardSubKey ioCtx prvKey (fromIntegral i)
return xpubk
genTransparentReceiver :: XPubKey -> IO TransparentAddress -- | Generate a transparent receiver
genTransparentReceiver xpubk = do genTransparentReceiver :: Int -> XPrvKey -> IO TransparentAddress
genTransparentReceiver i xprvk = do
ioCtx <- createContext ioCtx <- createContext
let x = xPubAddr ioCtx xpubk let rootPubKey = deriveXPubKey ioCtx xprvk
let childPubKey = pubSubKey ioCtx rootPubKey (fromIntegral i)
let x = xPubAddr ioCtx childPubKey
case x of case x of
PubKeyAddress k -> return $ TransparentAddress P2PKH $ fromBinary k PubKeyAddress k -> return $ TransparentAddress P2PKH $ fromBinary k
ScriptAddress j -> return $ TransparentAddress P2SH $ fromBinary j ScriptAddress j -> return $ TransparentAddress P2SH $ fromBinary j

View file

@ -50,8 +50,6 @@ import ZcashHaskell.Sapling
, matchSaplingAddress , matchSaplingAddress
) )
import ZcashHaskell.Transparent import ZcashHaskell.Transparent
--(encodeTransparent)
import ZcashHaskell.Types import ZcashHaskell.Types
( AccountId ( AccountId
, BlockResponse(..) , BlockResponse(..)
@ -64,6 +62,8 @@ import ZcashHaskell.Types
, RawTxResponse(..) , RawTxResponse(..)
, Seed(..) , Seed(..)
, ShieldedOutput(..) , ShieldedOutput(..)
, TransparentAddress(..)
, TransparentType(..)
, UnifiedAddress(..) , UnifiedAddress(..)
, UnifiedFullViewingKey(..) , UnifiedFullViewingKey(..)
, ZcashNet(..) , ZcashNet(..)
@ -471,104 +471,12 @@ main = do
describe "Wallet seed phrase" $ do describe "Wallet seed phrase" $ do
prop "Generated phrases are valid" $ again prop_PhraseLength prop "Generated phrases are valid" $ again prop_PhraseLength
prop "Derived seeds are valid" $ again prop_SeedLength prop "Derived seeds are valid" $ again prop_SeedLength
describe "Transparent Private and Public 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
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
---print $ show xtpubk
xtpubk `shouldBe` testpbk
before getSeed $ before getSeed $
describe "Optimized spending key tests" $ do describe "Optimized spending key tests" $ do
it "Transparent spending keys are valid" $ \s ->
property $ prop_TransparentSpendingKey s
it "Transparent receivers are valid" $ \s ->
property $ prop_TransparentReceiver s
it "Sapling spending keys are valid" $ \s -> it "Sapling spending keys are valid" $ \s ->
property $ prop_SaplingSpendingKey s property $ prop_SaplingSpendingKey s
it "Sapling receivers are valid" $ \s -> it "Sapling receivers are valid" $ \s ->
@ -604,11 +512,11 @@ main = do
Nothing -> return $ expectationFailure "Failed to generate seed" Nothing -> return $ expectationFailure "Failed to generate seed"
Just s' -> do Just s' -> do
let oK = genOrchardSpendingKey s' MainNetCoin 0 let oK = genOrchardSpendingKey s' MainNetCoin 0
let sK = genSaplingSpendingKey s' 0 let sK = genSaplingSpendingKey s' MainNetCoin 0
let tK = genTransparentPrvKey s' let tK = genTransparentPrvKey s' 0
let oR = genOrchardReceiver 0 =<< oK let oR = genOrchardReceiver 0 =<< oK
let sR = genSaplingPaymentAddress 0 =<< sK let sR = genSaplingPaymentAddress 0 =<< sK
tR <- genTransparentReceiver =<< genTransparentPubKey tK tR <- genTransparentReceiver 0 =<< tK
let newUA = UnifiedAddress MainNet oR sR $ Just tR let newUA = UnifiedAddress MainNet oR sR $ Just tR
return $ Just newUA `shouldBe` targetUA return $ Just newUA `shouldBe` targetUA
it "Recover UA from Zingo" $ it "Recover UA from Zingo" $
@ -623,11 +531,11 @@ main = do
Nothing -> return $ expectationFailure "Failed to generate seed" Nothing -> return $ expectationFailure "Failed to generate seed"
Just s' -> do Just s' -> do
let oK = genOrchardSpendingKey s' MainNetCoin 0 let oK = genOrchardSpendingKey s' MainNetCoin 0
let sK = genSaplingSpendingKey s' 0 let sK = genSaplingSpendingKey s' MainNetCoin 0
let tK = genTransparentPrvKey s' let tK = genTransparentPrvKey s' 0
let oR = genOrchardReceiver 0 =<< oK let oR = genOrchardReceiver 0 =<< oK
let sR = genSaplingPaymentAddress 0 =<< sK let sR = genSaplingPaymentAddress 0 =<< sK
tR <- genTransparentReceiver =<< genTransparentPubKey tK tR <- genTransparentReceiver 0 =<< tK
let newUA = UnifiedAddress MainNet oR sR $ Just tR let newUA = UnifiedAddress MainNet oR sR $ Just tR
return $ Just newUA `shouldBe` targetUA return $ Just newUA `shouldBe` targetUA
@ -654,19 +562,20 @@ prop_OrchardReceiver ::
prop_OrchardReceiver s c (NonNegative i) (NonNegative j) = prop_OrchardReceiver s c (NonNegative i) (NonNegative j) =
genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= Nothing genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= Nothing
prop_SaplingSpendingKey :: Seed -> NonNegative Int -> Property prop_SaplingSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
prop_SaplingSpendingKey s (NonNegative i) = prop_SaplingSpendingKey s c (NonNegative i) =
genSaplingSpendingKey s i =/= Nothing genSaplingSpendingKey s c i =/= Nothing
prop_SaplingReceiver :: Seed -> NonNegative Int -> NonNegative Int -> Property prop_SaplingReceiver ::
prop_SaplingReceiver s (NonNegative i) (NonNegative j) = Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property
genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s j) =/= prop_SaplingReceiver s c (NonNegative i) (NonNegative j) =
genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s c j) =/=
Nothing Nothing
prop_SaplingRecRepeated :: Seed -> NonNegative Int -> Property prop_SaplingRecRepeated :: Seed -> CoinType -> NonNegative Int -> Property
prop_SaplingRecRepeated s (NonNegative i) = prop_SaplingRecRepeated s c (NonNegative i) =
genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s 1) =/= genSaplingPaymentAddress i (fromMaybe "" $ genSaplingSpendingKey s c 1) =/=
genSaplingPaymentAddress (i + 1) (fromMaybe "" $ genSaplingSpendingKey s 1) genSaplingPaymentAddress (i + 1) (fromMaybe "" $ genSaplingSpendingKey s c 1)
prop_OrchardRecRepeated :: prop_OrchardRecRepeated ::
Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property Seed -> CoinType -> NonNegative Int -> NonNegative Int -> Property
@ -674,6 +583,20 @@ prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) =
genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/= genOrchardReceiver j (fromMaybe "" $ genOrchardSpendingKey s c i) =/=
genOrchardReceiver (j + 1) (fromMaybe "" $ genOrchardSpendingKey s c i) genOrchardReceiver (j + 1) (fromMaybe "" $ genOrchardSpendingKey s c i)
prop_TransparentSpendingKey :: Seed -> NonNegative Int -> Property
prop_TransparentSpendingKey s (NonNegative i) =
ioProperty $ do
k <- genTransparentPrvKey s i
return $ xPrvChild k == fromIntegral i
prop_TransparentReceiver ::
Seed -> NonNegative Int -> NonNegative Int -> Property
prop_TransparentReceiver s (NonNegative i) (NonNegative j) =
ioProperty $ do
k <- genTransparentPrvKey s i
r <- genTransparentReceiver j k
return $ ta_type r == P2PKH
-- | Generators -- | Generators
genOrcArgs :: Gen (CoinType, Int, Int) genOrcArgs :: Gen (CoinType, Int, Int)
genOrcArgs = do genOrcArgs = do