Add functions to generate seed phrases and seeds

This commit is contained in:
Rene Vergara 2024-01-16 16:15:05 -06:00
parent 72e3700aa6
commit ca8010145b
Signed by: pitmutt
GPG key ID: 65122AD495A7F5B2
10 changed files with 183 additions and 45 deletions

View file

@ -5,6 +5,13 @@ 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.4.0]
### Added
- Function to encode a human-readable transparent address
- Function to generate a seed phrase
## [0.3.0]
### Added

View file

@ -37,6 +37,7 @@ use haskell_ffi::{
use zcash_primitives::{
zip32::Scope as SaplingScope,
zip339::{Count, Mnemonic},
transaction::components::sapling::{
read_zkproof,
GrothProofBytes,
@ -582,3 +583,33 @@ pub extern "C" fn rust_wrapper_tx_parse(
}
}
}
#[no_mangle]
pub extern "C" fn rust_wrapper_gen_seed_phrase(
out: *mut u8,
out_len: &mut usize
){
let mnemonic = Mnemonic::generate(Count::Words24);
let seed = mnemonic.phrase().as_bytes().to_vec();
marshall_to_haskell_var(&seed, out, out_len, RW);
}
#[no_mangle]
pub extern "C" fn rust_wrapper_recover_seed(
input: *const u8,
input_len: usize,
out: *mut u8,
out_len: &mut usize
){
let phrase: String = marshall_from_haskell_var(input, input_len, RW);
let mnemonic = Mnemonic::from_phrase(phrase);
match mnemonic {
Ok(m) => {
let s = m.to_seed("").to_vec();
marshall_to_haskell_var(&s, out, out_len, RW);
},
Err(_e) => {
marshall_to_haskell_var(&vec![0], out, out_len, RW);
}
}
}

View file

@ -1,5 +1,5 @@
name: zcash-haskell
version: 0.3.0
version: 0.4.0
git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell"
license: LGPL-3
author: "Rene Vergara"
@ -53,3 +53,4 @@ tests:
- bytestring
- text
- aeson
- haskoin-core

View file

@ -29,7 +29,7 @@ module C.Zcash where
import qualified Data.ByteString as BS
import Codec.Borsh
import Data.Text (Text)
import qualified Data.Text as T
import Data.Word
import Data.Int
import Data.Structured
@ -43,6 +43,12 @@ import qualified Generics.SOP as SOP
import qualified GHC.Generics as GHC
import ZcashHaskell.Types
{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer RawData'&
}
-> `()'
#}
{# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble
{ toBorshVar* `BS.ByteString'&
@ -71,13 +77,6 @@ import ZcashHaskell.Types
-> `Bool'
#}
{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
{ toBorshVar* `BS.ByteString'&
, getVarBuffer `Buffer RawData'&
}
-> `()'
#}
{# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode
{ toBorshVar* `BS.ByteString'&
}
@ -127,3 +126,14 @@ import ZcashHaskell.Types
}
-> `()'
#}
{# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase
{ getVarBuffer `Buffer Phrase'& } -> `()'
#}
{# fun unsafe rust_wrapper_recover_seed as rustWrapperGetSeed
{ toBorshVar* `Phrase'&
, getVarBuffer `Buffer Seed'&
}
-> `()'
#}

52
src/ZcashHaskell/Keys.hs Normal file
View file

@ -0,0 +1,52 @@
{- Copyright 2022-2024 Vergara Technologies LLC
This file is part of Zcash-Haskell.
Zcash-Haskell is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
Zcash-Haskell is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
Zcash-Haskell. If not, see <https://www.gnu.org/licenses/>.
-}
-- |
-- Module : ZcashHaskell.Keys
-- Copyright : 2022-2024 Vergara Technologies
-- License : LGPL-3
--
-- Maintainer : pitmutt@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
-- Functions to generate keys for the Zcash blockchain
--
module ZcashHaskell.Keys where
import C.Zcash (rustWrapperGenSeedPhrase, rustWrapperGetSeed)
import qualified Data.ByteString as BS
import qualified Data.Text as T
import Foreign.Rust.Marshall.Variable
( withBorshVarBuffer
, withPureBorshVarBuffer
)
import ZcashHaskell.Types (Phrase, Seed)
-- | Generate a random seed that can be used to generate private keys for shielded addresses and transparent addresses.
generateWalletSeedPhrase :: IO Phrase
generateWalletSeedPhrase = withBorshVarBuffer rustWrapperGenSeedPhrase
-- | Get
getWalletSeed :: Phrase -> Maybe Seed
getWalletSeed p =
if BS.length result > 0
then Just result
else Nothing
where
result :: Seed
result = (withPureBorshVarBuffer . rustWrapperGetSeed) p

View file

@ -31,4 +31,36 @@
--
module ZcashHaskell.Transparent where
import Crypto.Hash
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58)
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import Data.Word
import ZcashHaskell.Types
( TransparentAddress(..)
, TransparentType(..)
, ZcashNet(..)
)
encodeTransparent :: TransparentAddress -> T.Text
encodeTransparent t =
case ta_type t of
P2SH ->
case ta_net t of
MainNet -> encodeTransparent' (0x1c, 0xbd) $ ta_bytes t
_ -> encodeTransparent' (0x1c, 0xba) $ ta_bytes t
P2PKH ->
case ta_net t of
MainNet -> encodeTransparent' (0x1c, 0xb8) $ ta_bytes t
_ -> encodeTransparent' (0x1d, 0x25) $ ta_bytes t
where
encodeTransparent' :: (Word8, Word8) -> BS.ByteString -> T.Text
encodeTransparent' (a, b) h =
E.decodeUtf8 $ encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum
where
sha256 :: BS.ByteString -> BS.ByteString
sha256 bs = BA.convert (hash bs :: Digest SHA256)
digest = BS.pack [a, b] <> h
checksum = sha256 $ sha256 digest

View file

@ -25,10 +25,10 @@
-- |
-- Module : ZcashHaskell.Types
-- Copyright : Vergara Technologies 2023
-- License : BOSL
-- Copyright : 2022-2024 Vergara Technologies
-- License : LGPL-3
--
-- Maintainer : rene@vergara.network
-- Maintainer : pitmut@vergara.tech
-- Stability : experimental
-- Portability : unknown
--
@ -37,12 +37,10 @@
module ZcashHaskell.Types where
import Codec.Borsh
import Control.Exception (MaskingState(Unmasked))
import Crypto.Hash
import Data.Aeson
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import Data.ByteString.Base58 (bitcoinAlphabet, encodeBase58)
import qualified Data.ByteString.Char8 as C
import Data.Int
import Data.Structured
@ -53,6 +51,14 @@ import qualified GHC.Generics as GHC
import qualified Generics.SOP as SOP
-- * General
--
-- | A seed for generating private keys
type Seed = C.ByteString
-- | A mnemonic phrase used to derive seeds
type Phrase = BS.ByteString
--
-- | Type to represent data after Bech32 decoding
data RawData = RawData
{ hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding
@ -144,35 +150,14 @@ data ZcashNet
data TransparentType
= P2SH
| P2PKH
deriving (Eq)
deriving (Eq, Prelude.Show)
-- | Type to represent a transparent Zcash addresses
data TransparentAddress = TransparentAddress
{ ta_type :: TransparentType
, ta_net :: ZcashNet
, ta_bytes :: BS.ByteString
} deriving (Eq)
instance Prelude.Show TransparentAddress where
show t =
case ta_type t of
P2SH ->
case ta_net t of
MainNet -> Prelude.show $ encodeTransparent (0x1c, 0xbd) $ ta_bytes t
_ -> Prelude.show $ encodeTransparent (0x1c, 0xba) $ ta_bytes t
P2PKH ->
case ta_net t of
MainNet -> Prelude.show $ encodeTransparent (0x1c, 0xb8) $ ta_bytes t
_ -> Prelude.show $ encodeTransparent (0x1d, 0x25) $ ta_bytes t
where
encodeTransparent :: (Word8, Word8) -> BS.ByteString -> BS.ByteString
encodeTransparent (a, b) h =
encodeBase58 bitcoinAlphabet $ digest <> BS.take 4 checksum
where
sha256 :: BS.ByteString -> BS.ByteString
sha256 bs = BA.convert (hash bs :: Digest SHA256)
digest = BS.pack [a, b] <> h
checksum = sha256 $ sha256 digest
{ ta_type :: !TransparentType
, ta_net :: !ZcashNet
, ta_bytes :: !BS.ByteString
} deriving (Eq, Prelude.Show)
-- * Sapling
-- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@.

View file

@ -17,7 +17,7 @@
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver: lts-21.21
resolver: lts-21.22
# User packages to be built.
# Various formats can be used as shown in the example below.

View file

@ -22,6 +22,8 @@ import C.Zcash (rustWrapperUADecode)
import Data.Aeson
import Data.Bool (Bool(True))
import qualified Data.ByteString as BS
import Data.Either (isRight)
import Data.Foldable (sequenceA_)
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
@ -29,7 +31,9 @@ import qualified Data.Text.Lazy.Encoding as LE
import qualified Data.Text.Lazy.IO as LTIO
import Data.Word
import GHC.Float.RealFracMethods (properFractionDoubleInteger)
import Haskoin.Keys.Mnemonic
import Test.Hspec
import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
import ZcashHaskell.Orchard
import ZcashHaskell.Sapling
( decodeSaplingOutput
@ -38,6 +42,7 @@ import ZcashHaskell.Sapling
, isValidShieldedAddress
, matchSaplingAddress
)
import ZcashHaskell.Transparent (encodeTransparent)
import ZcashHaskell.Types
( BlockResponse(..)
, DecodedNote(..)
@ -280,6 +285,18 @@ main = do
Right x ->
rt_id x `shouldBe`
"5242b51f22a7d6fe9dee237137271cde704d306a5fff6a862bffaebb6f0e7e56"
describe "Seeds" $ do
it "generate seed phrase" $ do
s <- generateWalletSeedPhrase
BS.length s `shouldNotBe` 0
it "get seed from phrase" $ do
s <- generateWalletSeedPhrase
let x = getWalletSeed s
let result =
case x of
Nothing -> False
Just s' -> True
result `shouldBe` True
describe "Sapling address" $ do
it "succeeds with valid address" $ do
let sa =
@ -427,5 +444,5 @@ main = do
let msg =
case isValidUnifiedAddress ua of
Nothing -> "Bad UA"
Just u -> maybe "No transparent" show $ t_rec u
msg `shouldBe` "Got it"
Just u -> maybe "No transparent" encodeTransparent $ t_rec u
msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"

View file

@ -1,11 +1,11 @@
cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.35.2.
-- This file has been generated from package.yaml by hpack version 0.36.0.
--
-- see: https://github.com/sol/hpack
name: zcash-haskell
version: 0.3.0
version: 0.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
@ -26,6 +26,8 @@ source-repository head
library
exposed-modules:
C.Zcash
ZcashHaskell.DB
ZcashHaskell.Keys
ZcashHaskell.Orchard
ZcashHaskell.Sapling
ZcashHaskell.Transparent
@ -63,6 +65,7 @@ test-suite zcash-haskell-test
aeson
, base >=4.7 && <5
, bytestring
, haskoin-core
, hspec
, text
, zcash-haskell