Add functions to generate seed phrases and seeds
This commit is contained in:
parent
72e3700aa6
commit
ca8010145b
10 changed files with 183 additions and 45 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
52
src/ZcashHaskell/Keys.hs
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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@.
|
||||
|
|
|
@ -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.
|
||||
|
|
21
test/Spec.hs
21
test/Spec.hs
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue