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/),
|
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.4.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Function to encode a human-readable transparent address
|
||||||
|
- Function to generate a seed phrase
|
||||||
|
|
||||||
## [0.3.0]
|
## [0.3.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -37,6 +37,7 @@ use haskell_ffi::{
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
zip32::Scope as SaplingScope,
|
zip32::Scope as SaplingScope,
|
||||||
|
zip339::{Count, Mnemonic},
|
||||||
transaction::components::sapling::{
|
transaction::components::sapling::{
|
||||||
read_zkproof,
|
read_zkproof,
|
||||||
GrothProofBytes,
|
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
|
name: zcash-haskell
|
||||||
version: 0.3.0
|
version: 0.4.0
|
||||||
git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell"
|
git: "https://git.vergara.tech/Vergara_Tech/zcash-haskell"
|
||||||
license: LGPL-3
|
license: LGPL-3
|
||||||
author: "Rene Vergara"
|
author: "Rene Vergara"
|
||||||
|
@ -53,3 +53,4 @@ tests:
|
||||||
- bytestring
|
- bytestring
|
||||||
- text
|
- text
|
||||||
- aeson
|
- aeson
|
||||||
|
- haskoin-core
|
||||||
|
|
|
@ -29,7 +29,7 @@ module C.Zcash where
|
||||||
|
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import Codec.Borsh
|
import Codec.Borsh
|
||||||
import Data.Text (Text)
|
import qualified Data.Text as T
|
||||||
import Data.Word
|
import Data.Word
|
||||||
import Data.Int
|
import Data.Int
|
||||||
import Data.Structured
|
import Data.Structured
|
||||||
|
@ -43,6 +43,12 @@ import qualified Generics.SOP as SOP
|
||||||
import qualified GHC.Generics as GHC
|
import qualified GHC.Generics as GHC
|
||||||
import ZcashHaskell.Types
|
import ZcashHaskell.Types
|
||||||
|
|
||||||
|
{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
|
||||||
|
{ toBorshVar* `BS.ByteString'&
|
||||||
|
, getVarBuffer `Buffer RawData'&
|
||||||
|
}
|
||||||
|
-> `()'
|
||||||
|
#}
|
||||||
|
|
||||||
{# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble
|
{# fun unsafe rust_wrapper_f4jumble as rustWrapperF4Jumble
|
||||||
{ toBorshVar* `BS.ByteString'&
|
{ toBorshVar* `BS.ByteString'&
|
||||||
|
@ -71,13 +77,6 @@ import ZcashHaskell.Types
|
||||||
-> `Bool'
|
-> `Bool'
|
||||||
#}
|
#}
|
||||||
|
|
||||||
{# fun unsafe rust_wrapper_bech32decode as rustWrapperBech32Decode
|
|
||||||
{ toBorshVar* `BS.ByteString'&
|
|
||||||
, getVarBuffer `Buffer RawData'&
|
|
||||||
}
|
|
||||||
-> `()'
|
|
||||||
#}
|
|
||||||
|
|
||||||
{# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode
|
{# fun pure unsafe rust_wrapper_svk_decode as rustWrapperSaplingVkDecode
|
||||||
{ toBorshVar* `BS.ByteString'&
|
{ 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
|
module ZcashHaskell.Transparent where
|
||||||
|
|
||||||
|
import Crypto.Hash
|
||||||
|
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 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
|
-- Module : ZcashHaskell.Types
|
||||||
-- Copyright : Vergara Technologies 2023
|
-- Copyright : 2022-2024 Vergara Technologies
|
||||||
-- License : BOSL
|
-- License : LGPL-3
|
||||||
--
|
--
|
||||||
-- Maintainer : rene@vergara.network
|
-- Maintainer : pitmut@vergara.tech
|
||||||
-- Stability : experimental
|
-- Stability : experimental
|
||||||
-- Portability : unknown
|
-- Portability : unknown
|
||||||
--
|
--
|
||||||
|
@ -37,12 +37,10 @@
|
||||||
module ZcashHaskell.Types where
|
module ZcashHaskell.Types where
|
||||||
|
|
||||||
import Codec.Borsh
|
import Codec.Borsh
|
||||||
import Control.Exception (MaskingState(Unmasked))
|
|
||||||
import Crypto.Hash
|
import Crypto.Hash
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
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 qualified Data.ByteString.Char8 as C
|
import qualified Data.ByteString.Char8 as C
|
||||||
import Data.Int
|
import Data.Int
|
||||||
import Data.Structured
|
import Data.Structured
|
||||||
|
@ -53,6 +51,14 @@ import qualified GHC.Generics as GHC
|
||||||
import qualified Generics.SOP as SOP
|
import qualified Generics.SOP as SOP
|
||||||
|
|
||||||
-- * General
|
-- * 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
|
-- | Type to represent data after Bech32 decoding
|
||||||
data RawData = RawData
|
data RawData = RawData
|
||||||
{ hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding
|
{ hrp :: BS.ByteString -- ^ Human-readable part of the Bech32 encoding
|
||||||
|
@ -144,35 +150,14 @@ data ZcashNet
|
||||||
data TransparentType
|
data TransparentType
|
||||||
= P2SH
|
= P2SH
|
||||||
| P2PKH
|
| P2PKH
|
||||||
deriving (Eq)
|
deriving (Eq, Prelude.Show)
|
||||||
|
|
||||||
-- | Type to represent a transparent Zcash addresses
|
-- | Type to represent a transparent Zcash addresses
|
||||||
data TransparentAddress = TransparentAddress
|
data TransparentAddress = TransparentAddress
|
||||||
{ ta_type :: TransparentType
|
{ ta_type :: !TransparentType
|
||||||
, ta_net :: ZcashNet
|
, ta_net :: !ZcashNet
|
||||||
, ta_bytes :: BS.ByteString
|
, ta_bytes :: !BS.ByteString
|
||||||
} deriving (Eq)
|
} deriving (Eq, Prelude.Show)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
-- * Sapling
|
-- * Sapling
|
||||||
-- | Type to represent a Sapling Shielded Output as provided by the @getrawtransaction@ RPC method of @zcashd@.
|
-- | 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: ./custom-snapshot.yaml
|
||||||
# resolver: https://example.com/snapshots/2018-01-01.yaml
|
# resolver: https://example.com/snapshots/2018-01-01.yaml
|
||||||
resolver: lts-21.21
|
resolver: lts-21.22
|
||||||
|
|
||||||
# User packages to be built.
|
# User packages to be built.
|
||||||
# Various formats can be used as shown in the example below.
|
# 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.Aeson
|
||||||
import Data.Bool (Bool(True))
|
import Data.Bool (Bool(True))
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
|
import Data.Either (isRight)
|
||||||
|
import Data.Foldable (sequenceA_)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
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
|
||||||
|
@ -29,7 +31,9 @@ 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 Data.Word
|
import Data.Word
|
||||||
import GHC.Float.RealFracMethods (properFractionDoubleInteger)
|
import GHC.Float.RealFracMethods (properFractionDoubleInteger)
|
||||||
|
import Haskoin.Keys.Mnemonic
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
import ZcashHaskell.Keys (generateWalletSeedPhrase, getWalletSeed)
|
||||||
import ZcashHaskell.Orchard
|
import ZcashHaskell.Orchard
|
||||||
import ZcashHaskell.Sapling
|
import ZcashHaskell.Sapling
|
||||||
( decodeSaplingOutput
|
( decodeSaplingOutput
|
||||||
|
@ -38,6 +42,7 @@ import ZcashHaskell.Sapling
|
||||||
, isValidShieldedAddress
|
, isValidShieldedAddress
|
||||||
, matchSaplingAddress
|
, matchSaplingAddress
|
||||||
)
|
)
|
||||||
|
import ZcashHaskell.Transparent (encodeTransparent)
|
||||||
import ZcashHaskell.Types
|
import ZcashHaskell.Types
|
||||||
( BlockResponse(..)
|
( BlockResponse(..)
|
||||||
, DecodedNote(..)
|
, DecodedNote(..)
|
||||||
|
@ -280,6 +285,18 @@ main = do
|
||||||
Right x ->
|
Right x ->
|
||||||
rt_id x `shouldBe`
|
rt_id x `shouldBe`
|
||||||
"5242b51f22a7d6fe9dee237137271cde704d306a5fff6a862bffaebb6f0e7e56"
|
"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
|
describe "Sapling address" $ do
|
||||||
it "succeeds with valid address" $ do
|
it "succeeds with valid address" $ do
|
||||||
let sa =
|
let sa =
|
||||||
|
@ -427,5 +444,5 @@ main = do
|
||||||
let msg =
|
let msg =
|
||||||
case isValidUnifiedAddress ua of
|
case isValidUnifiedAddress ua of
|
||||||
Nothing -> "Bad UA"
|
Nothing -> "Bad UA"
|
||||||
Just u -> maybe "No transparent" show $ t_rec u
|
Just u -> maybe "No transparent" encodeTransparent $ t_rec u
|
||||||
msg `shouldBe` "Got it"
|
msg `shouldBe` "t1LPWuQnjCRH7JAeEErSXKixcUteLJRJjKD"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
cabal-version: 1.12
|
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
|
-- see: https://github.com/sol/hpack
|
||||||
|
|
||||||
name: zcash-haskell
|
name: zcash-haskell
|
||||||
version: 0.3.0
|
version: 0.4.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
|
||||||
|
@ -26,6 +26,8 @@ source-repository head
|
||||||
library
|
library
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
C.Zcash
|
C.Zcash
|
||||||
|
ZcashHaskell.DB
|
||||||
|
ZcashHaskell.Keys
|
||||||
ZcashHaskell.Orchard
|
ZcashHaskell.Orchard
|
||||||
ZcashHaskell.Sapling
|
ZcashHaskell.Sapling
|
||||||
ZcashHaskell.Transparent
|
ZcashHaskell.Transparent
|
||||||
|
@ -63,6 +65,7 @@ test-suite zcash-haskell-test
|
||||||
aeson
|
aeson
|
||||||
, base >=4.7 && <5
|
, base >=4.7 && <5
|
||||||
, bytestring
|
, bytestring
|
||||||
|
, haskoin-core
|
||||||
, hspec
|
, hspec
|
||||||
, text
|
, text
|
||||||
, zcash-haskell
|
, zcash-haskell
|
||||||
|
|
Loading…
Reference in a new issue