Complete Unified Address generation #37

Merged
pitmutt merged 8 commits from rav001 into dev040 2024-03-15 16:31:51 +00:00
5 changed files with 310 additions and 31 deletions
Showing only changes of commit d5c50d58c5 - Show all commits

View file

@ -61,7 +61,8 @@ use zcash_address::{
use zcash_client_backend::keys::sapling::{ use zcash_client_backend::keys::sapling::{
spending_key, spending_key,
ExtendedFullViewingKey, ExtendedFullViewingKey,
ExtendedSpendingKey ExtendedSpendingKey,
DiversifiableFullViewingKey
}; };
use zcash_primitives::zip32::DiversifierIndex; use zcash_primitives::zip32::DiversifierIndex;
@ -676,6 +677,29 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress(
} }
} }
#[no_mangle]
pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress(
extspk: *const u8,
extspk_len: usize,
out: *mut u8,
out_len: &mut usize
){
let vexspk: Vec<u8> = marshall_from_haskell_var(extspk, extspk_len, RW);
let vexspkp = &vexspk;
let extspku8 : &[u8] = &vexspkp;
let extspk = match ExtendedSpendingKey::from_bytes(&extspku8) {
Ok( k ) => k,
Err( e ) => {
// error recovering ExtendedSpendingKey
marshall_to_haskell_var(&vec![0], out, out_len, RW);
return
}
};
let dfvk = extspk.to_diversifiable_full_viewing_key();
let ( divIx, cPmtAddress ) = dfvk.change_address();
marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW);
}
#[no_mangle] #[no_mangle]
pub extern "C" fn rust_wrapper_derive_orchard_spending_key( pub extern "C" fn rust_wrapper_derive_orchard_spending_key(
seed: *const u8, seed: *const u8,

View file

@ -151,6 +151,13 @@ import ZcashHaskell.Types
-> `()' -> `()'
#} #}
{# fun unsafe rust_wrapper_sapling_chgpaymentaddress as rustWrapperSaplingChgPaymentAddress
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer (BS.ByteString)'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_derive_orchard_spending_key as rustWrapperGenOrchardSpendKey {# fun unsafe rust_wrapper_derive_orchard_spending_key as rustWrapperGenOrchardSpendKey
{ toBorshVar* `BS.ByteString'& { toBorshVar* `BS.ByteString'&
, `Word32' , `Word32'

View file

@ -20,6 +20,7 @@ module ZcashHaskell.Sapling where
import C.Zcash import C.Zcash
( rustWrapperIsShielded ( rustWrapperIsShielded
, rustWrapperSaplingCheck , rustWrapperSaplingCheck
, rustWrapperSaplingChgPaymentAddress
, rustWrapperSaplingNoteDecode , rustWrapperSaplingNoteDecode
, rustWrapperSaplingPaymentAddress , rustWrapperSaplingPaymentAddress
, rustWrapperSaplingSpendingkey , rustWrapperSaplingSpendingkey
@ -124,4 +125,10 @@ genSaplingPaymentAddress i extspk =
-- | Generate an internal Sapling address -- | Generate an internal Sapling address
genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver genSaplingInternalAddress :: SaplingSpendingKey -> Maybe SaplingReceiver
genSaplingInternalAddress sk = undefined genSaplingInternalAddress sk =
if BS.length res > 0
then Just $ SaplingReceiver res
else Nothing
where
res =
withPureBorshVarBuffer (rustWrapperSaplingChgPaymentAddress $ getBytes sk)

View file

@ -17,28 +17,30 @@ module ZcashHaskell.Transparent where
import Control.Exception (throwIO) import Control.Exception (throwIO)
import Crypto.Hash import Crypto.Hash
import Crypto.Secp256k1
import qualified Data.ByteArray as BA import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58) import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58)
import Data.HexString
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 Data.Word
import Haskoin.Address (Address(..))
import qualified Haskoin.Crypto.Hash as H
import Haskoin.Crypto.Keys.Extended
import ZcashHaskell.Types import ZcashHaskell.Types
( AccountId ( AccountId
, CoinType(..)
, Scope(..)
, Seed(..) , Seed(..)
, ToBytes(..) , ToBytes(..)
, TransparentAddress(..) , TransparentAddress(..)
, TransparentType(..) , TransparentType(..)
, ZcashNet(..) , ZcashNet(..)
, getTransparentPrefix , getTransparentPrefix
, getValue
) )
import Crypto.Secp256k1
import Data.HexString
import Data.Word
import Haskoin.Address (Address(..))
import qualified Haskoin.Crypto.Hash as H
import Haskoin.Crypto.Keys.Extended
encodeTransparent :: ZcashNet -> TransparentAddress -> T.Text encodeTransparent :: ZcashNet -> TransparentAddress -> T.Text
encodeTransparent zNet t = encodeTransparent zNet t =
encodeTransparent' (getTransparentPrefix zNet (ta_type t)) $ encodeTransparent' (getTransparentPrefix zNet (ta_type t)) $
@ -53,19 +55,36 @@ encodeTransparent zNet t =
digest = BS.pack [a, b] <> h digest = BS.pack [a, b] <> h
checksum = sha256 $ sha256 digest checksum = sha256 $ sha256 digest
-- | Attempts to generate an Extended Private Key from a known HDSeed. -- | Generate an Extended Private Key from a known HDSeed.
genTransparentPrvKey :: Seed -> AccountId -> IO XPrvKey genTransparentPrvKey :: Seed -> CoinType -> AccountId -> IO XPrvKey
genTransparentPrvKey hdseed i = do genTransparentPrvKey hdseed ctype accid = do
let prvKey = makeXPrvKey $ getBytes hdseed let coin = getValue ctype
ioCtx <- createContext ioCtx <- createContext
return $ hardSubKey ioCtx prvKey (fromIntegral i) let path = Deriv :| 44 :| coin :| fromIntegral accid :: DerivPath
let prvKey = makeXPrvKey $ getBytes hdseed
return $ derivePath ioCtx path prvKey
genTransparentPubKey :: XPrvKey -> IO XPubKey
genTransparentPubKey xPrvKey = do
ioCtx <- createContext
return $ deriveXPubKey ioCtx xPrvKey
genTransparentPubAddress :: XPubKey -> IO Address
genTransparentPubAddress xPubKey = do
ioCtx <- createContext
return $ xPubAddr ioCtx xPubKey
-- | Generate a transparent receiver -- | Generate a transparent receiver
genTransparentReceiver :: Int -> XPrvKey -> IO TransparentAddress genTransparentReceiver :: Int -> Scope -> XPrvKey -> IO TransparentAddress
genTransparentReceiver i xprvk = do genTransparentReceiver i scope xprvk = do
ioCtx <- createContext ioCtx <- createContext
let rootPubKey = deriveXPubKey ioCtx xprvk let s =
let childPubKey = pubSubKey ioCtx rootPubKey (fromIntegral i) case scope of
External -> 0
Internal -> 1
let path = Deriv :/ s :/ fromIntegral i :: DerivPath
let childPrvKey = derivePath ioCtx path xprvk
let childPubKey = deriveXPubKey ioCtx childPrvKey
let x = xPubAddr ioCtx childPubKey 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

View file

@ -33,7 +33,6 @@ import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import qualified Data.Text.Lazy.Encoding as LE import qualified Data.Text.Lazy.Encoding as LE
import qualified Data.Text.Lazy.IO as LTIO import qualified Data.Text.Lazy.IO as LTIO
import GHC.Float.RealFracMethods (properFractionDoubleInteger) import GHC.Float.RealFracMethods (properFractionDoubleInteger)
import Test.Hspec import Test.Hspec
import Test.Hspec.QuickCheck import Test.Hspec.QuickCheck
@ -42,6 +41,7 @@ import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
import ZcashHaskell.Orchard import ZcashHaskell.Orchard
import ZcashHaskell.Sapling import ZcashHaskell.Sapling
( decodeSaplingOutput ( decodeSaplingOutput
, genSaplingInternalAddress
, genSaplingPaymentAddress , genSaplingPaymentAddress
, genSaplingSpendingKey , genSaplingSpendingKey
, getShieldedOutputs , getShieldedOutputs
@ -60,6 +60,7 @@ import ZcashHaskell.Types
, Phrase(..) , Phrase(..)
, RawData(..) , RawData(..)
, RawTxResponse(..) , RawTxResponse(..)
, SaplingReceiver(..)
, SaplingSpendingKey(..) , SaplingSpendingKey(..)
, Scope(..) , Scope(..)
, Seed(..) , Seed(..)
@ -503,7 +504,7 @@ main = do
maybe "No transparent" (encodeTransparent (ua_net u)) $ maybe "No transparent" (encodeTransparent (ua_net u)) $
t_rec u t_rec u
msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD" msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"
it "Recover UA from YWallet:" $ it "Recover UA from YWallet" $
ioProperty $ do ioProperty $ do
let p = let p =
Phrase Phrase
@ -517,10 +518,10 @@ main = do
Just s' -> do Just s' -> do
let oK = genOrchardSpendingKey s' MainNetCoin 0 let oK = genOrchardSpendingKey s' MainNetCoin 0
let sK = genSaplingSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0
let tK = genTransparentPrvKey s' 0 let tK = genTransparentPrvKey s' MainNetCoin 0
let oR = genOrchardReceiver 0 External =<< oK let oR = genOrchardReceiver 0 External =<< oK
let sR = genSaplingPaymentAddress 0 =<< sK let sR = genSaplingPaymentAddress 0 =<< sK
tR <- genTransparentReceiver 0 =<< tK tR <- genTransparentReceiver 0 External =<< 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:" $
@ -537,12 +538,233 @@ main = do
Just s' -> do Just s' -> do
let oK = genOrchardSpendingKey s' MainNetCoin 0 let oK = genOrchardSpendingKey s' MainNetCoin 0
let sK = genSaplingSpendingKey s' MainNetCoin 0 let sK = genSaplingSpendingKey s' MainNetCoin 0
let tK = genTransparentPrvKey s' 0 let tK = genTransparentPrvKey s' MainNetCoin 0
let oR = genOrchardReceiver 0 External =<< oK let oR = genOrchardReceiver 0 External =<< oK
let sR = genSaplingPaymentAddress 0 =<< sK let sR = genSaplingPaymentAddress 0 =<< sK
tR <- genTransparentReceiver 0 =<< tK tR <- genTransparentReceiver 0 External =<< 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
describe "Sapling Change Payment Address generation test" $ do
it "Call genSaplingInternalAddress" $ do
let sk =
[ 3
, 183
, 26
, 151
, 89
, 0
, 0
, 0
, 128
, 199
, 189
, 33
, 169
, 114
, 194
, 50
, 0
, 139
, 140
, 162
, 100
, 100
, 35
, 58
, 226
, 6
, 47
, 232
, 34
, 214
, 11
, 173
, 142
, 40
, 45
, 163
, 190
, 207
, 49
, 130
, 158
, 113
, 232
, 251
, 79
, 98
, 77
, 195
, 196
, 40
, 42
, 113
, 133
, 35
, 211
, 68
, 146
, 104
, 5
, 56
, 244
, 162
, 55
, 239
, 55
, 112
, 37
, 38
, 189
, 183
, 121
, 201
, 1
, 60
, 158
, 151
, 141
, 123
, 250
, 95
, 169
, 123
, 208
, 56
, 103
, 74
, 85
, 49
, 152
, 207
, 245
, 216
, 58
, 37
, 0
, 127
, 186
, 245
, 234
, 47
, 68
, 11
, 78
, 76
, 12
, 171
, 37
, 63
, 172
, 90
, 111
, 94
, 88
, 152
, 211
, 53
, 243
, 142
, 16
, 195
, 142
, 50
, 14
, 13
, 32
, 91
, 5
, 82
, 182
, 121
, 136
, 109
, 125
, 165
, 125
, 123
, 226
, 54
, 60
, 34
, 62
, 111
, 167
, 88
, 254
, 113
, 204
, 47
, 181
, 97
, 18
, 220
, 46
, 51
, 160
, 62
, 16
, 199
, 143
, 184
, 200
, 209
, 124
, 154
, 175
, 29
, 216
, 48
, 201
] :: [Word8]
let cAdr =
[ 31
, 232
, 31
, 17
, 196
, 178
, 208
, 227
, 206
, 199
, 105
, 55
, 147
, 23
, 151
, 206
, 117
, 59
, 249
, 162
, 218
, 140
, 189
, 17
, 60
, 116
, 106
, 56
, 64
, 203
, 152
, 52
, 155
, 133
, 179
, 118
, 47
, 161
, 70
, 155
, 21
, 22
, 41
] :: [Word8]
let bscAdr = SaplingReceiver $ BS.pack cAdr
let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk)
fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr
-- | Properties -- | Properties
prop_PhraseLength :: Property prop_PhraseLength :: Property
@ -604,18 +826,18 @@ prop_OrchardRecRepeated s c (NonNegative i) (NonNegative j) scope =
scope scope
(fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i) (fromMaybe (OrchardSpendingKey "") $ genOrchardSpendingKey s c i)
prop_TransparentSpendingKey :: Seed -> NonNegative Int -> Property prop_TransparentSpendingKey :: Seed -> CoinType -> NonNegative Int -> Property
prop_TransparentSpendingKey s (NonNegative i) = prop_TransparentSpendingKey s coinType (NonNegative i) =
ioProperty $ do ioProperty $ do
k <- genTransparentPrvKey s i k <- genTransparentPrvKey s coinType i
return $ xPrvChild k == fromIntegral i return $ xPrvChild k == fromIntegral i
prop_TransparentReceiver :: prop_TransparentReceiver ::
Seed -> NonNegative Int -> NonNegative Int -> Property Seed -> CoinType -> Scope -> NonNegative Int -> NonNegative Int -> Property
prop_TransparentReceiver s (NonNegative i) (NonNegative j) = prop_TransparentReceiver s coinType scope (NonNegative i) (NonNegative j) =
ioProperty $ do ioProperty $ do
k <- genTransparentPrvKey s i k <- genTransparentPrvKey s coinType i
r <- genTransparentReceiver j k r <- genTransparentReceiver j scope k
return $ ta_type r == P2PKH return $ ta_type r == P2PKH
-- | Generators -- | Generators