diff --git a/lib/jormungandr/src/Cardano/Wallet/Jormungandr.hs b/lib/jormungandr/src/Cardano/Wallet/Jormungandr.hs index 19178738dc2..461f723981e 100644 --- a/lib/jormungandr/src/Cardano/Wallet/Jormungandr.hs +++ b/lib/jormungandr/src/Cardano/Wallet/Jormungandr.hs @@ -62,7 +62,7 @@ import Cardano.Wallet.Jormungandr.Transaction import Cardano.Wallet.Network ( NetworkLayer (..), defaultRetryPolicy, waitForNetwork ) import Cardano.Wallet.Primitive.AddressDerivation - ( KeyToAddress, PersistKey ) + ( PersistKey ) import Cardano.Wallet.Primitive.AddressDerivation.Random ( RndKey ) import Cardano.Wallet.Primitive.AddressDerivation.Sequential @@ -77,6 +77,8 @@ import Cardano.Wallet.Primitive.Model ( BlockchainParameters (..) ) import Cardano.Wallet.Primitive.Types ( Block, Hash (..) ) +import Cardano.Wallet.Transaction + ( TransactionLayer ) import Control.Concurrent.Async ( race_ ) import Control.DeepSeq @@ -132,8 +134,10 @@ serveWallet (cfg, tr) databaseDir hostPref listen lj beforeMainLoop = do waitForService "Jörmungandr" tr nPort $ waitForNetwork nl defaultRetryPolicy let (_, bp) = staticBlockchainParameters nl - rndApi <- apiLayer tr (toWLBlock <$> nl) - seqApi <- apiLayer tr (toWLBlock <$> nl) + let rndTl = newTransactionLayer @n (getGenesisBlockHash bp) + let seqTl = newTransactionLayer @n (getGenesisBlockHash bp) + rndApi <- apiLayer tr rndTl (toWLBlock <$> nl) + seqApi <- apiLayer tr seqTl (toWLBlock <$> nl) startServer tr nPort bp rndApi seqApi Left e -> handleNetworkStartupError e where @@ -162,20 +166,19 @@ serveWallet (cfg, tr) databaseDir hostPref listen lj beforeMainLoop = do toWLBlock = J.convertBlock apiLayer - :: forall s k . - ( KeyToAddress (Jormungandr 'Testnet) k - , IsOurs s + :: forall s k. + ( IsOurs s , NFData s , Show s , PersistState s , PersistKey k ) => Trace IO Text + -> TransactionLayer t k -> NetworkLayer IO t (Block Tx) -> IO (ApiLayer s t k) - apiLayer tracer nl = do + apiLayer tracer tl nl = do let (block0, bp) = staticBlockchainParameters nl - let tl = newTransactionLayer @n (getGenesisBlockHash bp) wallets <- maybe (pure []) (Sqlite.findDatabases @k tr) databaseDir Server.newApiLayer tracer (block0, bp) nl tl dbFactory wallets diff --git a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Binary.hs b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Binary.hs index 94394820a98..e87de08c037 100644 --- a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Binary.hs +++ b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Binary.hs @@ -39,6 +39,15 @@ module Cardano.Wallet.Jormungandr.Binary , putSignedTx , putTx + -- * Transaction witnesses + , signData + , utxoWitness + , legacyUtxoWitness + , TxWitnessTag (..) + , putTxWitnessTag + , getTxWitnessTag + , txWitnessSize + -- * Purification of chain block types , convertBlock , convertBlockHeader @@ -48,7 +57,7 @@ module Cardano.Wallet.Jormungandr.Binary , getAddress , singleAddressFromKey - -- * Legacy Decoders + -- * Legacy , decodeLegacyAddress -- * Helpers @@ -57,7 +66,6 @@ module Cardano.Wallet.Jormungandr.Binary , fragmentId , maxNumberOfInputs , maxNumberOfOutputs - , signData , withHeader -- * Re-export @@ -71,7 +79,7 @@ module Cardano.Wallet.Jormungandr.Binary import Prelude import Cardano.Crypto.Wallet - ( XPub (xpubPublicKey) ) + ( XPub (xpubPublicKey), unXPub ) import Cardano.Wallet.Jormungandr.Environment ( KnownNetwork, Network (..), single ) import Cardano.Wallet.Jormungandr.Primitive.Types @@ -266,6 +274,21 @@ data TxWitnessTag | TxWitnessMultisig deriving (Show, Eq) +putTxWitnessTag :: TxWitnessTag -> Put +putTxWitnessTag = \case + TxWitnessLegacyUTxO -> putWord8 0 + TxWitnessUTxO -> putWord8 1 + TxWitnessAccount -> putWord8 2 + TxWitnessMultisig -> putWord8 3 + +getTxWitnessTag :: Get TxWitnessTag +getTxWitnessTag = getWord8 >>= \case + 0 -> pure TxWitnessLegacyUTxO + 1 -> pure TxWitnessUTxO + 2 -> pure TxWitnessAccount + 3 -> pure TxWitnessMultisig + other -> fail $ "Invalid witness type: " ++ show other + -- | Decode a message (header + contents). getMessage :: Get Message getMessage = label "getMessage" $ do @@ -305,6 +328,22 @@ txWitnessSize = \case TxWitnessAccount -> 64 TxWitnessMultisig -> 68 +txWitnessTagSize :: Int +txWitnessTagSize = 1 + +-- | Construct a UTxO witness from a signature +utxoWitness :: ByteString -> TxWitness +utxoWitness bytes = TxWitness $ BL.toStrict $ runPut $ do + putTxWitnessTag TxWitnessUTxO + putByteString bytes + +-- | Construct a legacy UTxO witness from a public key and a signature +legacyUtxoWitness :: XPub -> ByteString -> TxWitness +legacyUtxoWitness xpub bytes = TxWitness $ BL.toStrict $ runPut $ do + putTxWitnessTag TxWitnessLegacyUTxO + putByteString (unXPub xpub) + putByteString bytes + -- | Decode the contents of a @Transaction@-message. getTransaction :: Int -> Get (Tx, [TxWitness]) getTransaction n = label "getTransaction" $ do @@ -319,20 +358,12 @@ getTransaction n = label "getTransaction" $ do where getWitness :: Get TxWitness getWitness = do - tag <- getTxWitnessTag - let len = txWitnessSize tag + tag <- lookAhead getTxWitnessTag + let len = txWitnessSize tag + txWitnessTagSize -- NOTE: Regardless of the type of witness, we decode it as a -- @TxWitness@. TxWitness <$> isolate len (getByteString len) - getTxWitnessTag :: Get TxWitnessTag - getTxWitnessTag = getWord8 >>= \case - 0 -> pure TxWitnessLegacyUTxO - 1 -> pure TxWitnessUTxO - 2 -> pure TxWitnessAccount - 3 -> pure TxWitnessMultisig - other -> fail $ "Invalid witness type: " ++ show other - getTokenTransfer :: Get ([(TxIn, Coin)], [TxOut]) getTokenTransfer = label "getTokenTransfer" $ do inCount <- fromIntegral <$> getWord8 @@ -362,9 +393,7 @@ putSignedTx inputs outputs witnesses = do where -- Assumes the `TxWitness` has been faithfully constructed putWitness :: TxWitness -> Put - putWitness (TxWitness bytes) = do - putWord8 1 - putByteString bytes + putWitness (TxWitness bytes) = putByteString bytes putTx :: [(TxIn, Coin)] -> [TxOut] -> Put putTx inputs outputs = do @@ -601,7 +630,7 @@ estimateMaxNumberOfInputsParams = EstimateMaxNumberOfInputsParams , estBlockHashSize = 32 -- The length of the smallest type of witness. - , estTxWitnessSize = txWitnessSize TxWitnessUTxO + , estTxWitnessSize = txWitnessSize TxWitnessUTxO + txWitnessTagSize } -- | Jörmungandr distinguish 'fragment id' (what we commonly call 'txId') @@ -652,7 +681,7 @@ convertBlockHeader h = (W.BlockHeader (slot h) (bh h) (Hash "") (Hash "")) bh = Quantity . fromIntegral . chainLength {------------------------------------------------------------------------------- - Legacy Decoders + Legacy -------------------------------------------------------------------------------} -- | Attempt decoding a 'ByteString' into an 'Address'. This merely checks that diff --git a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Transaction.hs b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Transaction.hs index c833150a622..9a702033afb 100644 --- a/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Transaction.hs +++ b/lib/jormungandr/src/Cardano/Wallet/Jormungandr/Transaction.hs @@ -10,6 +10,7 @@ module Cardano.Wallet.Jormungandr.Transaction ( newTransactionLayer , sign + , mkTxWitness , ErrExceededInpsOrOuts (..) ) where @@ -19,10 +20,12 @@ import Cardano.Wallet.Jormungandr.Binary ( Message (..) , fragmentId , getMessage + , legacyUtxoWitness , maxNumberOfInputs , maxNumberOfOutputs , runGetOrFail , signData + , utxoWitness ) import Cardano.Wallet.Jormungandr.Compatibility ( Jormungandr ) @@ -30,6 +33,10 @@ import Cardano.Wallet.Jormungandr.Primitive.Types ( Tx (..) ) import Cardano.Wallet.Primitive.AddressDerivation ( Depth (AddressK), KeyToAddress, Passphrase (..), WalletKey (..), XPrv ) +import Cardano.Wallet.Primitive.AddressDerivation.Random + ( RndKey ) +import Cardano.Wallet.Primitive.AddressDerivation.Sequential + ( SeqKey ) import Cardano.Wallet.Primitive.CoinSelection ( CoinSelection (..) ) import Cardano.Wallet.Primitive.Types @@ -65,10 +72,11 @@ newTransactionLayer :: forall n k t. ( t ~ Jormungandr n , KeyToAddress (Jormungandr n) k + , MkTxWitness k ) => Hash "Genesis" -> TransactionLayer t k -newTransactionLayer (Hash block0) = TransactionLayer +newTransactionLayer (Hash block0H) = TransactionLayer { mkStdTx = \keyFrom rnps outs -> do -- NOTE -- For signing, we need to embed a hash of the transaction data @@ -76,9 +84,10 @@ newTransactionLayer (Hash block0) = TransactionLayer -- this is a transaction id as Byron nodes or the http-bridge -- defines them. let inps = fmap (second coin) rnps - let bs = block0 <> getHash (signData inps outs) - wits <- forM rnps $ \(_, TxOut addr _) -> sign bs - <$> maybeToRight (ErrKeyNotFoundForAddress addr) (keyFrom addr) + wits <- forM rnps $ \(_, TxOut addr _) -> do + xprv <- maybeToRight (ErrKeyNotFoundForAddress addr) (keyFrom addr) + let payload = block0H <> getHash (signData inps outs) + pure $ mkTxWitness (fst xprv) (sign payload xprv) let tx = Tx { txid = fragmentId inps outs wits , inputs = inps @@ -109,13 +118,40 @@ newTransactionLayer (Hash block0) = TransactionLayer $ Left ErrExceededInpsOrOuts } +-- | Provide a transaction witness for a given private key. The type of witness +-- is different between types of keys and, with backward-compatible support, we +-- need to support many types for one backend target. +-- +-- We have tightly coupled the type of witness to the type of key for because: +-- +-- - RndKey can only be used with legacy / Byron wallets as they require a +-- special address structure (to embed the derivation path). +-- +-- - SeqKey could theorically be used with the legacy address structure (as +-- Yoroi does) however, our implementation only associate SeqKey to new +-- addresses. +class MkTxWitness (k :: Depth -> * -> *) where + mkTxWitness + :: k 'AddressK XPrv + -> ByteString + -> TxWitness + +instance MkTxWitness SeqKey where + mkTxWitness _ = utxoWitness + +instance MkTxWitness RndKey where + mkTxWitness xprv = legacyUtxoWitness xpub + where + xpub = getRawKey $ publicKey xprv + +-- | Sign some arbitrary binary data using a private key. sign :: WalletKey k => ByteString -> (k 'AddressK XPrv, Passphrase "encryption") - -> TxWitness + -> ByteString sign bytes (key, (Passphrase pwd)) = - TxWitness . CC.unXSignature $ CC.sign pwd (getRawKey key) bytes + CC.unXSignature $ CC.sign pwd (getRawKey key) bytes -- | Transaction with improper number of inputs and outputs is tried data ErrExceededInpsOrOuts = ErrExceededInpsOrOuts diff --git a/lib/jormungandr/test/integration/Cardano/Wallet/Jormungandr/NetworkSpec.hs b/lib/jormungandr/test/integration/Cardano/Wallet/Jormungandr/NetworkSpec.hs index 0af73058307..e35996403d2 100644 --- a/lib/jormungandr/test/integration/Cardano/Wallet/Jormungandr/NetworkSpec.hs +++ b/lib/jormungandr/test/integration/Cardano/Wallet/Jormungandr/NetworkSpec.hs @@ -17,7 +17,15 @@ import Cardano.BM.Trace import Cardano.Wallet.Jormungandr.Api ( GetTipId, api ) import Cardano.Wallet.Jormungandr.Binary - ( MessageType (..), fragmentId, putSignedTx, runPut, withHeader ) + ( MessageType (..) + , TxWitnessTag (..) + , fragmentId + , putSignedTx + , putTxWitnessTag + , runPut + , txWitnessSize + , withHeader + ) import Cardano.Wallet.Jormungandr.Compatibility ( Jormungandr, Network (..) ) import Cardano.Wallet.Jormungandr.Network @@ -348,7 +356,7 @@ spec = do either throwIO (\_ -> return ()) e pkWitness :: TxWitness - pkWitness = TxWitness $ BS.pack $ replicate 64 3 + pkWitness = TxWitness $ BS.pack $ [1] <> replicate 64 3 proxy :: Proxy (Jormungandr 'Mainnet) proxy = Proxy @@ -451,8 +459,8 @@ instance Arbitrary SignedTx where -- | Only generates single address witnesses instance Arbitrary TxWitness where - arbitrary = TxWitness <$> genFixed 64 - shrink (TxWitness bytes) = TxWitness <$> shrinkFixedBS bytes + arbitrary = taggedWitness TxWitnessUTxO . TxWitness + <$> genFixed (txWitnessSize TxWitnessUTxO) instance Arbitrary (Hash "Tx") where arbitrary = Hash <$> genFixed 32 @@ -502,6 +510,11 @@ shrinkFixedBS bs = [zeros | bs /= zeros] prependTag :: Int -> ByteString -> ByteString prependTag tag bs = BS.pack [fromIntegral tag] <> bs +taggedWitness :: TxWitnessTag -> TxWitness -> TxWitness +taggedWitness tag (TxWitness bytes) = TxWitness (prefix <> bytes) + where + prefix = BL.toStrict $ runPut $ putTxWitnessTag tag + getRollForward :: NextBlocksResult target block -> Maybe [block] getRollForward AwaitReply = Nothing getRollForward (RollForward _ _ bs) = Just bs diff --git a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/BinarySpec.hs b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/BinarySpec.hs index 08c75ea4e51..107a59cdbf4 100644 --- a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/BinarySpec.hs +++ b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/BinarySpec.hs @@ -23,15 +23,18 @@ import Cardano.Wallet.Jormungandr.Binary , Message (..) , MessageType (..) , Milli (..) + , TxWitnessTag (..) , fragmentId , getAddress , getBlock , getMessage , putAddress , putSignedTx + , putTxWitnessTag , runGet , runPut , singleAddressFromKey + , txWitnessSize , withHeader ) import Cardano.Wallet.Jormungandr.Compatibility @@ -408,8 +411,13 @@ instance Arbitrary SignedTx where -- | Only generates single address witnesses instance Arbitrary TxWitness where - arbitrary = TxWitness <$> genFixed 64 - shrink (TxWitness bytes) = TxWitness <$> shrinkFixedBS bytes + arbitrary = taggedWitness TxWitnessUTxO . TxWitness + <$> genFixed (txWitnessSize TxWitnessUTxO) prependTag :: Int -> ByteString -> ByteString prependTag tag bs = BS.pack [fromIntegral tag] <> bs + +taggedWitness :: TxWitnessTag -> TxWitness -> TxWitness +taggedWitness tag (TxWitness bytes) = TxWitness (prefix <> bytes) + where + prefix = BL.toStrict $ runPut $ putTxWitnessTag tag diff --git a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs index ae88a4c40ab..251675b8e46 100644 --- a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs +++ b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs @@ -4,6 +4,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Cardano.Wallet.Jormungandr.TransactionSpec @@ -30,8 +31,10 @@ import Cardano.Wallet.Primitive.AddressDerivation , keyToAddress , publicKey ) +import Cardano.Wallet.Primitive.AddressDerivation.Random + ( RndKey ) import Cardano.Wallet.Primitive.AddressDerivation.Sequential - ( SeqKey, unsafeGenerateKeyFromSeed ) + ( SeqKey ) import Cardano.Wallet.Primitive.CoinSelection ( CoinSelection (..) ) import Cardano.Wallet.Primitive.Types @@ -55,6 +58,8 @@ import Test.Hspec import Test.QuickCheck ( property ) +import qualified Cardano.Wallet.Primitive.AddressDerivation.Random as Rnd +import qualified Cardano.Wallet.Primitive.AddressDerivation.Sequential as Seq import qualified Data.ByteArray as BA import qualified Data.ByteString.Lazy as BL import qualified Data.Map as Map @@ -85,17 +90,27 @@ estimateMaxNumberOfInputsSpec = describe "estimateMaxNumberOfInputs" $ do mkStdTxSpec :: Spec mkStdTxSpec = do let (xprv0, pwd0) = - xprvFromSeed "arbitrary-seed-0" + xprvSeqFromSeed "arbitrary-seed-0" -- ^ 30c8422fd3cbaf54449df9f627a1f88cf85d4fee84083a91cdb6f0dbdb09c24d -- ed29409c6a8275a643a2e79d280d97a898a3366706f3f677434b75c9d98680d0 let (xprv1, pwd1) = - xprvFromSeed "arbitrary-seed-1" + xprvSeqFromSeed "arbitrary-seed-1" -- ^ b81e217576bf2683f3359f50d0bf938ca3c61fdf7a2d0c1b2b35f7fb174dc042 -- 6bb7377c9ea9cb481b4c3df601379fbf69033add18c1d272d7975c43682afc48 let (xprv2, pwd2) = - xprvFromSeed "arbitrary-seed-2" + xprvSeqFromSeed "arbitrary-seed-2" -- ^ e01bfb39e3e595fce0b9b19e386a82816e0cef8aa823c75a32bc0f39bcf7c14e -- 8a03d255df0440b6d0fcf5d5199a582d1df7d858bd7556d4941ebf6223fa66d1 + + let (xprvRnd0, pwdRnd0) = + xprvRndFromSeed "arbitrary-seed-0" + -- ^ 183b26b1127ea29c2f053ee8f9d8c0a90c5251235e0fd2bfda401da318045a4d + -- 8fe398f1d0898ee4505e6b2546c1ac5c4b201e0833708312489e26142892a4ae + let (xprvRnd1, pwdRnd1) = + xprvRndFromSeed "arbitrary-seed-1" + -- ^ 80330dc0e8382d72313a050add1fdaf8ad3f99f8a0bd56b4a27af497d43d0c53 + -- acdbfa4f24a8e04aa2298ddc623ebbb4b61fd61f6076d269aa6a30ffb6bb7574 + let txin0 = Hash $ unsafeFromHex "666984dec4bc0ff1888be97bfe0694a96b35c58d025405ead51d5cc72a3019f4" let txin1 = Hash $ unsafeFromHex @@ -104,7 +119,7 @@ mkStdTxSpec = do "13c3d835c53a198f7c8513b04d99eeb23c745c0a73364c2f0e802fa38eec9dba" describe "mkStdTx 'Mainnet" $ do - let proxy = Proxy @(Jormungandr 'Mainnet) + let tl = newTransactionLayer @'Mainnet block0 let keyToAddress' = keyToAddress @(Jormungandr 'Mainnet) let addr0 = keyToAddress' (publicKey xprv0) -- ^ ca1qvk32hg8rppc0wn0lzpkq996pd3xkxqguel8tharwrpdch6czu2luh3truq @@ -134,7 +149,7 @@ mkStdTxSpec = do -- | jcli transaction add-witness wit0.bin \ -- | jcli transaction seal \ -- | jcli transaction to-message - goldenTestStdTx proxy keystore block0 + goldenTestStdTx tl keystore [ (TxIn txin0 0, TxOut addr0 (Coin 10000)) ] [ TxOut addr1 (Coin 14) , TxOut addr2 (Coin 9986) @@ -170,7 +185,7 @@ mkStdTxSpec = do -- | jcli transaction add-witness wit1.bin \ -- | jcli transaction seal \ -- | jcli transaction to-message - goldenTestStdTx proxy keystore block0 + goldenTestStdTx tl keystore [ (TxIn txin0 0, TxOut addr0 (Coin 10000)) , (TxIn txin1 1, TxOut addr1 (Coin 999999999)) ] @@ -190,13 +205,68 @@ mkStdTxSpec = do \080f5ac10474c7f6decbbc35f1620817b15bf1594981c034b7f6a15d0a24ec743d\ \332f1774eb8733a5d40c" - describe "mkStdTx unknown input" $ do - unknownInputTest (Proxy @'Mainnet) block0 - unknownInputTest (Proxy @'Testnet) block0 + describe "mkStdTx (legacy) 'Mainnet" $ do + let tl = newTransactionLayer @'Mainnet block0 + let addrRnd0 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprvRnd0) + -- ^ DdzFFzCqrhsyySN2fbNnZf4kh2gg9t4mZzcnCiiw1EFG4ynvCGi35qgdUPh1DJp5Z28SVQxsxfNn7CaRB6DbvvvXZzdtMJ4ML2RrXvrG + let addrRnd1 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprvRnd1) + -- ^ DdzFFzCqrhsrqGWaofA6TmXeChoV5YGk8GyvLE2DCyTib8YpQn4qxsomw4oagtcpa321iQynEtT2D31xG5XGLSWTLHe9CZz26CwZZBQf + let addr0 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprv0) + -- ^ ca1qvk32hg8rppc0wn0lzpkq996pd3xkxqguel8tharwrpdch6czu2luh3truq + let addr1 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprv1) + -- ^ ca1qwedmnalvvgqqgt2dczppejvyrn2lydmk2pxya4dd076wal8v6eykzfapdx + let addr2 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprv2) + -- ^ ca1qvp2m296efkn4zy63y769x4g52g5vrt7e9dnvszt7z2vrfe0ya66vznknfn + + let keystore = mkKeystore + [ (addrRnd0, (xprvRnd0, pwdRnd0)) + , (addrRnd1, (xprvRnd1, pwdRnd1)) + ] + + -- See 'Mainnet description + goldenTestStdTx tl keystore + [ (TxIn txin0 0, TxOut addrRnd0 (Coin 10000)) ] + [ TxOut addr1 (Coin 14) + , TxOut addr2 (Coin 9986) + ] + "00ff020102000000000000002710666984dec4bc0ff1888be97bfe0694a96b35c5\ + \8d025405ead51d5cc72a3019f403b2ddcfbf631000216a6e0410e64c20e6af91bb\ + \b2826276ad6bfda777e766b24b000000000000000e0302ada8baca6d3a889a893d\ + \a29aa8a291460d7ec95b36404bf094c1a72f2775a6000000000000270200260e3f\ + \0867d341caf86f9b7bd9689f1b21f04dad05711ea3594cc441f159a723537824ae\ + \e5526bd50c6cc32113911bd591e3a3601326513ed88151ce8dbdfa488754e00ab7\ + \e4f1e8d7534cd654b82b3eab841bb387113e46f9902519a94f9296b83a5fb99793\ + \4c1cba5ccefc42e55b59c70752b356a77307f1a32e686a42970c" + + -- See 'Mainnet description + goldenTestStdTx tl keystore + [ (TxIn txin0 0, TxOut addrRnd0 (Coin 10000)) + , (TxIn txin1 1, TxOut addrRnd1 (Coin 999999999)) + ] + [ TxOut addr0 (Coin 99999) + , TxOut addr1 (Coin 42) + , TxOut addr2 (Coin 1337) + ] + "01d2020203000000000000002710666984dec4bc0ff1888be97bfe0694a96b35c5\ + \8d025405ead51d5cc72a3019f401000000003b9ac9ff1323856bc91c49e928f6f3\ + \0f4e8d665d53eb4ab6028bd0ac971809d514c92db1032d155d07184387ba6ff883\ + \6014ba0b626b1808e67e75dfa370c2dc5f581715fe000000000001869f03b2ddcf\ + \bf631000216a6e0410e64c20e6af91bbb2826276ad6bfda777e766b24b00000000\ + \0000002a0302ada8baca6d3a889a893da29aa8a291460d7ec95b36404bf094c1a7\ + \2f2775a6000000000000053900260e3f0867d341caf86f9b7bd9689f1b21f04dad\ + \05711ea3594cc441f159a723537824aee5526bd50c6cc32113911bd591e3a36013\ + \26513ed88151ce8dbdfa4817c80704772729b5c0b2583abe0a3e675e2227879a74\ + \52882ba9733baa03bfb9ec2ff75e086fdf8f8fd89c2c3ef9b9c4052e7096dd9fc1\ + \2fc22541394a0b3d0d000205b66108c02e4d6dbc4c70adaa27d22ea2adeffe0de0\ + \09c099ac230b520917d2c23ec4bf897f4fd236c676988b62877259f81bd9f08d43\ + \55d5fe6b96cda06db052067dafcc5b525efe2bd9d03d21c356aa1b82887126a218\ + \5231c749a117d70641583fe739d6b59fab049028ad68cbe935910251eb4aacb803\ + \5353e609e400" describe "mkStdTx 'Testnet" $ do - let proxy = Proxy @(Jormungandr 'Testnet) + let tl = newTransactionLayer @'Testnet block0 let keyToAddress' = keyToAddress @(Jormungandr 'Testnet) + let addr0 = keyToAddress' (publicKey xprv0) -- ^ ta1svk32hg8rppc0wn0lzpkq996pd3xkxqguel8tharwrpdch6czu2luue5k36 let addr1 = keyToAddress' (publicKey xprv1) @@ -211,7 +281,7 @@ mkStdTxSpec = do ] -- See 'Mainnet description - goldenTestStdTx proxy keystore block0 + goldenTestStdTx tl keystore [ (TxIn txin0 0, TxOut addr0 (Coin 10000)) ] [ TxOut addr1 (Coin 14) , TxOut addr2 (Coin 9986) @@ -224,7 +294,7 @@ mkStdTxSpec = do \dd66f34c185212b4a4133b9fdc857487cc4497fc356ed01e91726c0d" -- See 'Mainnet description - goldenTestStdTx proxy keystore block0 + goldenTestStdTx tl keystore [ (TxIn txin0 0, TxOut addr0 (Coin 10000)) , (TxIn txin1 1, TxOut addr1 (Coin 999999999)) ] @@ -244,6 +314,69 @@ mkStdTxSpec = do \d6bd5c813d6f3f454f4935b009eabbac1ffd768a07e37773e5b568e8c76c57e53d\ \a6b26d3da9f466b78200" + describe "mkStdTx (legacy) 'Testnet" $ do + let tl = newTransactionLayer @'Testnet block0 + + let addrRnd0 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprvRnd0) + -- ^ DdzFFzCqrhsyySN2fbNnZf4kh2gg9t4mZzcnCiiw1EFG4ynvCGi35qgdUPh1DJp5Z28SVQxsxfNn7CaRB6DbvvvXZzdtMJ4ML2RrXvrG + let addrRnd1 = keyToAddress @(Jormungandr 'Mainnet) (publicKey xprvRnd1) + -- ^ DdzFFzCqrhsrqGWaofA6TmXeChoV5YGk8GyvLE2DCyTib8YpQn4qxsomw4oagtcpa321iQynEtT2D31xG5XGLSWTLHe9CZz26CwZZBQf + let addr0 = keyToAddress @(Jormungandr 'Testnet) (publicKey xprv0) + -- ^ ta1svk32hg8rppc0wn0lzpkq996pd3xkxqguel8tharwrpdch6czu2luue5k36 + let addr1 = keyToAddress @(Jormungandr 'Testnet) (publicKey xprv1) + -- ^ ta1swedmnalvvgqqgt2dczppejvyrn2lydmk2pxya4dd076wal8v6eykfpz5qu + let addr2 = keyToAddress @(Jormungandr 'Testnet) (publicKey xprv2) + -- ^ ta1svp2m296efkn4zy63y769x4g52g5vrt7e9dnvszt7z2vrfe0ya66vfmfxyf + + let keystore = mkKeystore + [ (addrRnd0, (xprvRnd0, pwdRnd0)) + , (addrRnd1, (xprvRnd1, pwdRnd1)) + ] + + -- See 'Mainnet description + goldenTestStdTx tl keystore + [ (TxIn txin0 0, TxOut addrRnd0 (Coin 10000)) ] + [ TxOut addr1 (Coin 14) + , TxOut addr2 (Coin 9986) + ] + "00ff020102000000000000002710666984dec4bc0ff1888be97bfe0694a96b35c5\ + \8d025405ead51d5cc72a3019f483b2ddcfbf631000216a6e0410e64c20e6af91bb\ + \b2826276ad6bfda777e766b24b000000000000000e8302ada8baca6d3a889a893d\ + \a29aa8a291460d7ec95b36404bf094c1a72f2775a6000000000000270200260e3f\ + \0867d341caf86f9b7bd9689f1b21f04dad05711ea3594cc441f159a723537824ae\ + \e5526bd50c6cc32113911bd591e3a3601326513ed88151ce8dbdfa486fb2c7b4bf\ + \45910e1a07f26209654a00541d918a49f0339bb820da724f165df1c647ccfc6e5c\ + \de86dd31e154b7cc6beef06d561359a2d102f8b3de15b5990007" + + -- See 'Mainnet description + goldenTestStdTx tl keystore + [ (TxIn txin0 0, TxOut addrRnd0 (Coin 10000)) + , (TxIn txin1 1, TxOut addrRnd1 (Coin 999999999)) + ] + [ TxOut addr0 (Coin 99999) + , TxOut addr1 (Coin 42) + , TxOut addr2 (Coin 1337) + ] + "01d2020203000000000000002710666984dec4bc0ff1888be97bfe0694a96b35c5\ + \8d025405ead51d5cc72a3019f401000000003b9ac9ff1323856bc91c49e928f6f3\ + \0f4e8d665d53eb4ab6028bd0ac971809d514c92db1832d155d07184387ba6ff883\ + \6014ba0b626b1808e67e75dfa370c2dc5f581715fe000000000001869f83b2ddcf\ + \bf631000216a6e0410e64c20e6af91bbb2826276ad6bfda777e766b24b00000000\ + \0000002a8302ada8baca6d3a889a893da29aa8a291460d7ec95b36404bf094c1a7\ + \2f2775a6000000000000053900260e3f0867d341caf86f9b7bd9689f1b21f04dad\ + \05711ea3594cc441f159a723537824aee5526bd50c6cc32113911bd591e3a36013\ + \26513ed88151ce8dbdfa48b1c5041340c009641039e1dcdf436ecc2e7f219d3dd3\ + \aa93b5841662b45e04e8232050f3c42cb64ca45219deb841ecef026488fbf0d653\ + \2589c4e50fd7a11d00000205b66108c02e4d6dbc4c70adaa27d22ea2adeffe0de0\ + \09c099ac230b520917d2c23ec4bf897f4fd236c676988b62877259f81bd9f08d43\ + \55d5fe6b96cda06db22f7d78f0ff0ec3a7c7fbc3da777a6aea6aeddd8ffef0beb8\ + \0afd3ba2dd9972585ae74b5876cca3301cee381daf5f9e10b5f8da2f2b9eabba26\ + \bc5c0f01b803" + + describe "mkStdTx unknown input" $ do + unknownInputTest (Proxy @'Mainnet) block0 + unknownInputTest (Proxy @'Testnet) block0 + describe "validateSelection cannot accept selection that violates maxNumberOfInputs" $ do tooNumerousInpsTest (Proxy @'Mainnet) block0 tooNumerousInpsTest (Proxy @'Testnet) block0 @@ -253,15 +386,14 @@ mkStdTxSpec = do tooNumerousOutsTest (Proxy @'Testnet) block0 goldenTestStdTx - :: forall n. (KnownNetwork n) - => Proxy (Jormungandr n) - -> (Address -> Maybe (SeqKey 'AddressK XPrv, Passphrase "encryption")) - -> Hash "Genesis" + :: forall t n k. (t ~ Jormungandr n) + => TransactionLayer t k + -> (Address -> Maybe (k 'AddressK XPrv, Passphrase "encryption")) -> [(TxIn, TxOut)] -> [TxOut] -> ByteString -> SpecWith () -goldenTestStdTx _ keystore block0 inps outs bytes' = it title $ do +goldenTestStdTx tl keystore inps outs bytes' = it title $ do let tx = mkStdTx tl keystore inps outs let bytes = fmap (\(Tx _ i o, w) -> hex @@ -272,19 +404,29 @@ goldenTestStdTx _ keystore block0 inps outs bytes' = it title $ do tx bytes `shouldBe` Right bytes' where - tl = newTransactionLayer @n block0 title = "golden test mkStdTx: " <> show inps <> show outs -xprvFromSeed +xprvSeqFromSeed :: ByteString -> (SeqKey depth XPrv, Passphrase "encryption") -xprvFromSeed seed = - ( unsafeGenerateKeyFromSeed (Passphrase (BA.convert seed), mempty) pwd +xprvSeqFromSeed seed = + ( Seq.unsafeGenerateKeyFromSeed (Passphrase (BA.convert seed), mempty) pwd , pwd ) where pwd = mempty +xprvRndFromSeed + :: ByteString + -> (RndKey 'AddressK XPrv, Passphrase "encryption") +xprvRndFromSeed seed = + ( Rnd.unsafeGenerateKeyFromSeed derPath (Passphrase $ BA.convert seed) pwd + , pwd + ) + where + pwd = mempty + derPath = (minBound, minBound) + mkKeystore :: Ord k => [(k,v)] -> (k -> Maybe v) mkKeystore pairs k = Map.lookup k (Map.fromList pairs) @@ -298,7 +440,8 @@ unknownInputTest -> Hash "Genesis" -> SpecWith () unknownInputTest _ block0 = it title $ do - let addr = keyToAddress @(Jormungandr n) $ publicKey $ xprv "address-number-0" + let addr = keyToAddress @(Jormungandr n) $ publicKey $ fst $ + xprvSeqFromSeed "address-number-0" let res = mkStdTx tl keyFrom inps outs where tl = newTransactionLayer @n @SeqKey block0 @@ -321,7 +464,8 @@ tooNumerousInpsTest -> Hash "Genesis" -> SpecWith () tooNumerousInpsTest _ block0 = it title $ do - let addr = keyToAddress @(Jormungandr n) $ publicKey $ xprv "address-number-0" + let addr = keyToAddress @(Jormungandr n) $ publicKey $ fst $ + xprvSeqFromSeed "address-number-0" let res = validateSelection tl (CoinSelection inps outs chngs) where tl = newTransactionLayer @n @SeqKey block0 @@ -343,7 +487,8 @@ tooNumerousOutsTest -> Hash "Genesis" -> SpecWith () tooNumerousOutsTest _ block0 = it title $ do - let addr = keyToAddress @(Jormungandr n) $ publicKey $ xprv "address-number-0" + let addr = keyToAddress @(Jormungandr n) $ publicKey $ fst $ + xprvSeqFromSeed "address-number-0" let res = validateSelection tl (CoinSelection inps outs chngs) where tl = newTransactionLayer @n @SeqKey block0 @@ -358,8 +503,3 @@ tooNumerousOutsTest _ block0 = it title $ do title = "Too numerous outputs yields an error (" <> T.unpack (toText (networkVal @n)) <> ")" - - -xprv :: ByteString -> SeqKey depth XPrv -xprv seed = - unsafeGenerateKeyFromSeed (Passphrase (BA.convert seed), mempty) mempty