From 3ea9de3023ebb761401c871d76f19648356ba675 Mon Sep 17 00:00:00 2001 From: Nicolas Pouillard Date: Thu, 15 Jan 2015 23:40:14 +0100 Subject: [PATCH] Add support for hx hd-decode This command show details in JSON about extended public and private keys. --- DetailedTx.hs | 45 ++++++++++++++++++- hx.hs | 11 ++++- tests/hd-decode-xprv-m-44'-0'-0'.t/TESTRECIPE | 42 +++++++++++++++++ tests/hd-decode-xprv-m-44'-0'-0'.t/stdin | 1 + tests/hd-decode-xprv-m-44'-0'-0'.t/stdout | 1 + tests/hd-decode-xprv-m-44'-0'.t/TESTRECIPE | 42 +++++++++++++++++ tests/hd-decode-xprv-m-44'-0'.t/stdin | 1 + tests/hd-decode-xprv-m-44'-0'.t/stdout | 1 + tests/hd-decode-xprv-m-44'.t/TESTRECIPE | 42 +++++++++++++++++ tests/hd-decode-xprv-m-44'.t/stdin | 1 + tests/hd-decode-xprv-m-44'.t/stdout | 1 + tests/hd-decode-xpub-m-44'-0'-0'.t/TESTRECIPE | 42 +++++++++++++++++ tests/hd-decode-xpub-m-44'-0'-0'.t/stdin | 1 + tests/hd-decode-xpub-m-44'-0'-0'.t/stdout | 1 + tests/hd-decode-xpub-m-44'-0'.t/TESTRECIPE | 42 +++++++++++++++++ tests/hd-decode-xpub-m-44'-0'.t/stdin | 1 + tests/hd-decode-xpub-m-44'-0'.t/stdout | 1 + tests/hd-decode-xpub-m-44'.t/TESTRECIPE | 42 +++++++++++++++++ tests/hd-decode-xpub-m-44'.t/stdin | 1 + tests/hd-decode-xpub-m-44'.t/stdout | 1 + 20 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 tests/hd-decode-xprv-m-44'-0'-0'.t/TESTRECIPE create mode 100644 tests/hd-decode-xprv-m-44'-0'-0'.t/stdin create mode 100644 tests/hd-decode-xprv-m-44'-0'-0'.t/stdout create mode 100644 tests/hd-decode-xprv-m-44'-0'.t/TESTRECIPE create mode 100644 tests/hd-decode-xprv-m-44'-0'.t/stdin create mode 100644 tests/hd-decode-xprv-m-44'-0'.t/stdout create mode 100644 tests/hd-decode-xprv-m-44'.t/TESTRECIPE create mode 100644 tests/hd-decode-xprv-m-44'.t/stdin create mode 100644 tests/hd-decode-xprv-m-44'.t/stdout create mode 100644 tests/hd-decode-xpub-m-44'-0'-0'.t/TESTRECIPE create mode 100644 tests/hd-decode-xpub-m-44'-0'-0'.t/stdin create mode 100644 tests/hd-decode-xpub-m-44'-0'-0'.t/stdout create mode 100644 tests/hd-decode-xpub-m-44'-0'.t/TESTRECIPE create mode 100644 tests/hd-decode-xpub-m-44'-0'.t/stdin create mode 100644 tests/hd-decode-xpub-m-44'-0'.t/stdout create mode 100644 tests/hd-decode-xpub-m-44'.t/TESTRECIPE create mode 100644 tests/hd-decode-xpub-m-44'.t/stdin create mode 100644 tests/hd-decode-xpub-m-44'.t/stdout diff --git a/DetailedTx.hs b/DetailedTx.hs index a04354e..324c18a 100644 --- a/DetailedTx.hs +++ b/DetailedTx.hs @@ -2,19 +2,29 @@ module DetailedTx where import Data.Aeson hiding (decode') +import Data.Aeson.Types (Pair) import qualified Data.ByteString.Lazy as LBS +import qualified Data.Text as T -import Network.Haskoin.Crypto (txHash) +import Network.Haskoin.Crypto (txHash,pubKeyAddr,addrToBase58,derivePubKey,toWIF) import Network.Haskoin.Internals (Tx(..), TxIn(..), TxOut(..) ,scriptSender, scriptRecipient + ,XPrvKey(..),XPubKey(..) + ,xPrvIsPrime,xPrvChild,xPubIsPrime,xPubChild ) import Network.Haskoin.Util (eitherToMaybe,decode') +import Utils (putHex) import PrettyScript (showDoc, prettyScript) +(.=$) :: T.Text -> String -> Pair +(.=$) x y = x .= y + newtype DetailedTx = DetailedTx { _unDetailedTx :: Tx } newtype DetailedTxIn = DetailedTxIn { _unDetailedTxIn :: TxIn } newtype DetailedTxOut = DetailedTxOut { _unDetailedTxOut :: TxOut } +newtype DetailedXPrvKey = DetailedXPrvKey { _unDetailedXPrvKey :: XPrvKey } +newtype DetailedXPubKey = DetailedXPubKey { _unDetailedXPubKey :: XPubKey } instance ToJSON DetailedTx where toJSON (DetailedTx tx) = @@ -45,5 +55,38 @@ instance ToJSON DetailedTxOut where ] where script = decode' $ scriptOutput o +instance ToJSON DetailedXPrvKey where + toJSON (DetailedXPrvKey k) = + object + ["type" .=$ "xprv" + ,"depth" .= xPrvDepth k + ,"parent" .= xPrvParent k + ,"index" .= object ["value" .= xPrvIndex k + ,(if xPrvIsPrime k then "hard" else "soft") .= xPrvChild k + ] + ,"chain" .= xPrvChain k + ,"prvkey" .= toWIF (xPrvKey k) + ,"pubkey" .=$ putHex pub + ,"address" .= addrToBase58 addr + ] + where pub = derivePubKey (xPrvKey k) + addr = pubKeyAddr pub + +instance ToJSON DetailedXPubKey where + toJSON (DetailedXPubKey k) = + object + ["type" .=$ "xpub" + ,"depth" .= xPubDepth k + ,"parent" .= xPubParent k + ,"index" .= object ["value" .= xPubIndex k + ,(if xPubIsPrime k then "hard" else "soft") .= xPubChild k + ] + ,"chain" .= xPubChain k + ,"pubkey" .=$ putHex pub + ,"address" .= addrToBase58 addr + ] + where pub = xPubKey k + addr = pubKeyAddr pub + txDetailedJSON :: Tx -> LBS.ByteString txDetailedJSON = encode . toJSON . DetailedTx diff --git a/hx.hs b/hx.hs index 8774027..ce4dc2b 100644 --- a/hx.hs +++ b/hx.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-} +import qualified Data.Aeson as A import Data.Maybe import Data.Either (partitionEithers) import Data.Word @@ -31,7 +32,7 @@ import Network.Haskoin.Util import PrettyScript import ParseScript import Mnemonic (hex_to_mn, mn_to_hex) -import DetailedTx (txDetailedJSON) +import DetailedTx (txDetailedJSON,DetailedXPrvKey(..),DetailedXPubKey(..)) import Utils import Electrum @@ -101,6 +102,9 @@ xKeyImport s xKeyImportE :: BS -> XKey xKeyImportE = fromMaybe (error "invalid extended public or private key") . xKeyImport . ignoreSpaces +xKeyDetails :: XKey -> BS +xKeyDetails = toStrictBS . A.encode . onXKey (A.toJSON . DetailedXPrvKey) (A.toJSON . DetailedXPubKey) + pubXKey :: XKey -> XPubKey pubXKey (XPub k) = k pubXKey (XPrv k) = deriveXPubKey k @@ -287,6 +291,9 @@ hx_hd_path mp = [] -> error "Empty path" (m:p) -> xKeyExportC m . derivePath p . xKeyImportE +hx_hd_decode :: BS -> BS +hx_hd_decode = xKeyDetails . xKeyImportE + hx_bip39_mnemonic :: Hex s => s -> BS hx_bip39_mnemonic = either error B8.pack . toMnemonic . decodeHex "seed" @@ -515,6 +522,7 @@ mainArgs ["hd-priv", "--hard", i] = interactLn . hx_hd_priv $ Just (primeSubK mainArgs ["hd-pub"] = interactLn $ hx_hd_pub Nothing mainArgs ["hd-pub", i] = interactLn . hx_hd_pub . Just $ parseWord32 "hd-pub index" i mainArgs ["hd-path", p] = interactLn $ hx_hd_path p +mainArgs ["hd-decode"] = interactLn hx_hd_decode mainArgs ["hd-to-wif"] = interactLn hx_hd_to_wif mainArgs ["hd-to-pubkey"] = interactLn hx_hd_to_pubkey mainArgs ["hd-to-address"] = interactLn hx_hd_to_address @@ -628,6 +636,7 @@ mainArgs _ = error $ unlines ["Unexpected arguments." ,"hx hd-pub [0]" ,"hx hd-pub " ,"hx hd-path [0]" + ,"hx hd-decode [0]" ,"hx hd-to-wif" ,"hx hd-to-address" ,"hx hd-to-pubkey [0]" diff --git a/tests/hd-decode-xprv-m-44'-0'-0'.t/TESTRECIPE b/tests/hd-decode-xprv-m-44'-0'-0'.t/TESTRECIPE new file mode 100644 index 0000000..df55b5c --- /dev/null +++ b/tests/hd-decode-xprv-m-44'-0'-0'.t/TESTRECIPE @@ -0,0 +1,42 @@ +#!/bin/bash + +testname=hd-decode-xprv-m-44\'-0\'-0\'.t +command=hx +args=( hd-decode ) +exit_code=0 +stdin_file=stdin +stdout_file=stdout +stderr_file=/dev/null +sources=( ) +products=( ) + +# Environment variables: +env_vars=( ) + +setup(){ + : Perform here actions to be run before the tested program +} + +munge(){ + : Munge here the results of the tested program to ease the check +} + +check(){ + check_exit_code && + check_stderr && + check_stdout && + check_products && + : Perform here extra checks on the tested program +} + +explain(){ + explain_exit_code + explain_stdout + explain_stderr + explain_products + : Explain here more potential differences +} + +teardown(){ + : Undo here the actions of setup +} diff --git a/tests/hd-decode-xprv-m-44'-0'-0'.t/stdin b/tests/hd-decode-xprv-m-44'-0'-0'.t/stdin new file mode 100644 index 0000000..751d992 --- /dev/null +++ b/tests/hd-decode-xprv-m-44'-0'-0'.t/stdin @@ -0,0 +1 @@ +xprv9yzHmz5N7z12y2XtmLQ1Y3rZmWsfB5Wirwx2kYWCz4edUXyUBbQWEuZJieyBoiL2imYeMmFAJGzzKnvfGJSpyzMboQBR5PgNQVaxdTmcoMq diff --git a/tests/hd-decode-xprv-m-44'-0'-0'.t/stdout b/tests/hd-decode-xprv-m-44'-0'-0'.t/stdout new file mode 100644 index 0000000..f272430 --- /dev/null +++ b/tests/hd-decode-xprv-m-44'-0'-0'.t/stdout @@ -0,0 +1 @@ +{"parent":3025294812,"prvkey":"L5SC9vZgSVxZKHR2xQ9pp1ArHSJmwy1B7btJbLmqMBFcbridjFev","address":"1DPyt4vhtvMQ3eeYiaYMcGF13W6VdKrinv","depth":3,"chain":"c806eb2807f69a5a3728fbdcaa3f8661ddc2d3cce78b05689634a80c5faabe93","pubkey":"029d1d167674838ad483ae10f230143b3db2db15fca0f13227e436ed605dcb4391","type":"xprv","index":{"value":2147483648,"hard":0}} diff --git a/tests/hd-decode-xprv-m-44'-0'.t/TESTRECIPE b/tests/hd-decode-xprv-m-44'-0'.t/TESTRECIPE new file mode 100644 index 0000000..603864e --- /dev/null +++ b/tests/hd-decode-xprv-m-44'-0'.t/TESTRECIPE @@ -0,0 +1,42 @@ +#!/bin/bash + +testname=hd-decode-xprv-m-44\'-0\'.t +command=hx +args=( hd-decode ) +exit_code=0 +stdin_file=stdin +stdout_file=stdout +stderr_file=/dev/null +sources=( ) +products=( ) + +# Environment variables: +env_vars=( ) + +setup(){ + : Perform here actions to be run before the tested program +} + +munge(){ + : Munge here the results of the tested program to ease the check +} + +check(){ + check_exit_code && + check_stderr && + check_stdout && + check_products && + : Perform here extra checks on the tested program +} + +explain(){ + explain_exit_code + explain_stdout + explain_stderr + explain_products + : Explain here more potential differences +} + +teardown(){ + : Undo here the actions of setup +} diff --git a/tests/hd-decode-xprv-m-44'-0'.t/stdin b/tests/hd-decode-xprv-m-44'-0'.t/stdin new file mode 100644 index 0000000..56ab176 --- /dev/null +++ b/tests/hd-decode-xprv-m-44'-0'.t/stdin @@ -0,0 +1 @@ +xprv9wyuDintoWNqEjbei49i6k6AiRLpKrM9dEHvGdfo1j2nBfFHbpv8jQkjSeeFAsxRyUmNucqFQqiq62WaGgvt6YnmU19UwctPKUCoNssSHAw diff --git a/tests/hd-decode-xprv-m-44'-0'.t/stdout b/tests/hd-decode-xprv-m-44'-0'.t/stdout new file mode 100644 index 0000000..4eb0f62 --- /dev/null +++ b/tests/hd-decode-xprv-m-44'-0'.t/stdout @@ -0,0 +1 @@ +{"parent":2739840753,"prvkey":"L2bohhjq3krJfvtoybofjeEsc1imGfyQW9nQvVZ6vF18vPFSafV1","address":"1HSTGHzJ9zhJY1iAwhB3zVEAgpeoDunX5B","depth":2,"chain":"cb09583fdd881f4f750d4cfab1a38a0fc6ab899573ad17dcae6b59d4205d14fd","pubkey":"03a6f7fd6253cebe4967fac207ae9cdb98d141abb4cd4885084e2465017c19fd5c","type":"xprv","index":{"value":2147483648,"hard":0}} diff --git a/tests/hd-decode-xprv-m-44'.t/TESTRECIPE b/tests/hd-decode-xprv-m-44'.t/TESTRECIPE new file mode 100644 index 0000000..d41f1b7 --- /dev/null +++ b/tests/hd-decode-xprv-m-44'.t/TESTRECIPE @@ -0,0 +1,42 @@ +#!/bin/bash + +testname=hd-decode-xprv-m-44\'.t +command=hx +args=( hd-decode ) +exit_code=0 +stdin_file=stdin +stdout_file=stdout +stderr_file=/dev/null +sources=( ) +products=( ) + +# Environment variables: +env_vars=( ) + +setup(){ + : Perform here actions to be run before the tested program +} + +munge(){ + : Munge here the results of the tested program to ease the check +} + +check(){ + check_exit_code && + check_stderr && + check_stdout && + check_products && + : Perform here extra checks on the tested program +} + +explain(){ + explain_exit_code + explain_stdout + explain_stderr + explain_products + : Explain here more potential differences +} + +teardown(){ + : Undo here the actions of setup +} diff --git a/tests/hd-decode-xprv-m-44'.t/stdin b/tests/hd-decode-xprv-m-44'.t/stdin new file mode 100644 index 0000000..527a580 --- /dev/null +++ b/tests/hd-decode-xprv-m-44'.t/stdin @@ -0,0 +1 @@ +xprv9uYPC6rsyUrcgiVwFS5ESa6asoTsKK898e3w2rAt6hvDksRhp8Mf167pqBto8GcQ4Su9VW7j73w29tbcwpLA599d92d3UakC5onCkJzSYnB diff --git a/tests/hd-decode-xprv-m-44'.t/stdout b/tests/hd-decode-xprv-m-44'.t/stdout new file mode 100644 index 0000000..ac93dbe --- /dev/null +++ b/tests/hd-decode-xprv-m-44'.t/stdout @@ -0,0 +1 @@ +{"parent":1465455704,"prvkey":"Kzk4Q6UWkVVDR1anst2h8PYWYNYTZT1tMcLrzBDcp3xWjhdV9v8d","address":"1FtVN7a9Kbahjx3bVbwNRi4MFtJm7ysAXB","depth":1,"chain":"7c0b9ed2bac0312ef72666cf1fe7f052acc822146ebadad3fef01c39187b740d","pubkey":"027b4a1b4d128f7e410bab035aa3074b59cda166a9b93cf90d6d33cfafcad849f0","type":"xprv","index":{"value":2147483692,"hard":44}} diff --git a/tests/hd-decode-xpub-m-44'-0'-0'.t/TESTRECIPE b/tests/hd-decode-xpub-m-44'-0'-0'.t/TESTRECIPE new file mode 100644 index 0000000..c1e2499 --- /dev/null +++ b/tests/hd-decode-xpub-m-44'-0'-0'.t/TESTRECIPE @@ -0,0 +1,42 @@ +#!/bin/bash + +testname=hd-decode-xpub-m-44\'-0\'-0\'.t +command=hx +args=( hd-decode ) +exit_code=0 +stdin_file=stdin +stdout_file=stdout +stderr_file=/dev/null +sources=( ) +products=( ) + +# Environment variables: +env_vars=( ) + +setup(){ + : Perform here actions to be run before the tested program +} + +munge(){ + : Munge here the results of the tested program to ease the check +} + +check(){ + check_exit_code && + check_stderr && + check_stdout && + check_products && + : Perform here extra checks on the tested program +} + +explain(){ + explain_exit_code + explain_stdout + explain_stderr + explain_products + : Explain here more potential differences +} + +teardown(){ + : Undo here the actions of setup +} diff --git a/tests/hd-decode-xpub-m-44'-0'-0'.t/stdin b/tests/hd-decode-xpub-m-44'-0'-0'.t/stdin new file mode 100644 index 0000000..dfa247b --- /dev/null +++ b/tests/hd-decode-xpub-m-44'-0'-0'.t/stdin @@ -0,0 +1 @@ +xpub6CyeBVcFxMZLBWcMsMw1uBoJKYi9aYEaEAsdYvupYQBcMLJcj8iknhsnZuLCEKRbc45YezdRQDc7hhpehYh3UeF6SaC4uVdnH7F1YSzix4n diff --git a/tests/hd-decode-xpub-m-44'-0'-0'.t/stdout b/tests/hd-decode-xpub-m-44'-0'-0'.t/stdout new file mode 100644 index 0000000..440e49d --- /dev/null +++ b/tests/hd-decode-xpub-m-44'-0'-0'.t/stdout @@ -0,0 +1 @@ +{"parent":3025294812,"address":"1DPyt4vhtvMQ3eeYiaYMcGF13W6VdKrinv","depth":3,"chain":"c806eb2807f69a5a3728fbdcaa3f8661ddc2d3cce78b05689634a80c5faabe93","pubkey":"029d1d167674838ad483ae10f230143b3db2db15fca0f13227e436ed605dcb4391","type":"xpub","index":{"value":2147483648,"hard":0}} diff --git a/tests/hd-decode-xpub-m-44'-0'.t/TESTRECIPE b/tests/hd-decode-xpub-m-44'-0'.t/TESTRECIPE new file mode 100644 index 0000000..e255a73 --- /dev/null +++ b/tests/hd-decode-xpub-m-44'-0'.t/TESTRECIPE @@ -0,0 +1,42 @@ +#!/bin/bash + +testname=hd-decode-xpub-m-44\'-0\'.t +command=hx +args=( hd-decode ) +exit_code=0 +stdin_file=stdin +stdout_file=stdout +stderr_file=/dev/null +sources=( ) +products=( ) + +# Environment variables: +env_vars=( ) + +setup(){ + : Perform here actions to be run before the tested program +} + +munge(){ + : Munge here the results of the tested program to ease the check +} + +check(){ + check_exit_code && + check_stderr && + check_stdout && + check_products && + : Perform here extra checks on the tested program +} + +explain(){ + explain_exit_code + explain_stdout + explain_stderr + explain_products + : Explain here more potential differences +} + +teardown(){ + : Undo here the actions of setup +} diff --git a/tests/hd-decode-xpub-m-44'-0'.t/stdin b/tests/hd-decode-xpub-m-44'-0'.t/stdin new file mode 100644 index 0000000..3e17cbc --- /dev/null +++ b/tests/hd-decode-xpub-m-44'-0'.t/stdin @@ -0,0 +1 @@ +xpub6AyFdEKndsw8TDg7p5giTt2uGTBJjK4zzTDX525Qa4Zm4TaS9NEPHD5DHwfaf17Ub1H1qZU1r8KP2eEjibsprJQfFqDPpfdAgBcBPiNDLZK diff --git a/tests/hd-decode-xpub-m-44'-0'.t/stdout b/tests/hd-decode-xpub-m-44'-0'.t/stdout new file mode 100644 index 0000000..8740c0d --- /dev/null +++ b/tests/hd-decode-xpub-m-44'-0'.t/stdout @@ -0,0 +1 @@ +{"parent":2739840753,"address":"1HSTGHzJ9zhJY1iAwhB3zVEAgpeoDunX5B","depth":2,"chain":"cb09583fdd881f4f750d4cfab1a38a0fc6ab899573ad17dcae6b59d4205d14fd","pubkey":"03a6f7fd6253cebe4967fac207ae9cdb98d141abb4cd4885084e2465017c19fd5c","type":"xpub","index":{"value":2147483648,"hard":0}} diff --git a/tests/hd-decode-xpub-m-44'.t/TESTRECIPE b/tests/hd-decode-xpub-m-44'.t/TESTRECIPE new file mode 100644 index 0000000..2f3ef61 --- /dev/null +++ b/tests/hd-decode-xpub-m-44'.t/TESTRECIPE @@ -0,0 +1,42 @@ +#!/bin/bash + +testname=hd-decode-xpub-m-44\'.t +command=hx +args=( hd-decode ) +exit_code=0 +stdin_file=stdin +stdout_file=stdout +stderr_file=/dev/null +sources=( ) +products=( ) + +# Environment variables: +env_vars=( ) + +setup(){ + : Perform here actions to be run before the tested program +} + +munge(){ + : Munge here the results of the tested program to ease the check +} + +check(){ + check_exit_code && + check_stderr && + check_stdout && + check_products && + : Perform here extra checks on the tested program +} + +explain(){ + explain_exit_code + explain_stdout + explain_stderr + explain_products + : Explain here more potential differences +} + +teardown(){ + : Undo here the actions of setup +} diff --git a/tests/hd-decode-xpub-m-44'.t/stdin b/tests/hd-decode-xpub-m-44'.t/stdin new file mode 100644 index 0000000..7464c37 --- /dev/null +++ b/tests/hd-decode-xpub-m-44'.t/stdin @@ -0,0 +1 @@ +xpub68XjbcPmorQuuCaQMTcEoi3KRqJMimqzVryXqEaVf3TCdfkrMffuYtSJgT4ZZjMmkFqTXt8nZKwhgyaEb6ohypz61SGzQYjBAvUDdm1FmWy diff --git a/tests/hd-decode-xpub-m-44'.t/stdout b/tests/hd-decode-xpub-m-44'.t/stdout new file mode 100644 index 0000000..baca96f --- /dev/null +++ b/tests/hd-decode-xpub-m-44'.t/stdout @@ -0,0 +1 @@ +{"parent":1465455704,"address":"1FtVN7a9Kbahjx3bVbwNRi4MFtJm7ysAXB","depth":1,"chain":"7c0b9ed2bac0312ef72666cf1fe7f052acc822146ebadad3fef01c39187b740d","pubkey":"027b4a1b4d128f7e410bab035aa3074b59cda166a9b93cf90d6d33cfafcad849f0","type":"xpub","index":{"value":2147483692,"hard":44}}