packages feed

cardano-transactions (empty) → 1.0.0

raw patch · 10 files changed

+2057/−0 lines, 10 filesdep +QuickCheckdep +ansi-terminaldep +basesetup-changed

Dependencies added: QuickCheck, ansi-terminal, base, base58-bytestring, bytestring, cardano-binary, cardano-crypto, cardano-crypto-wrapper, cardano-ledger, cardano-ledger-test, cardano-transactions, cborg, cryptonite, extra, hedgehog-quickcheck, hspec, memory, optparse-applicative, process, text

Files

+ ChangeLog.md view
@@ -0,0 +1,15 @@+## [1.0.0] - 2020-04-21++### Added++- Module 'Data.UTxO.Transaction' which defines a state-machine abstraction for constructing, signing and serializing payments.+- Module 'Data.UTxO.Transaction.Cardano.Byron' which implements the abstraction from 'Data.UTxO.Transaction' for Byron.+- A command-line tool 'cardano-tx' which offers a manual handle on the Byron implementation.++### Changed++N/A++### Removed++N/A
+ LICENSE view
@@ -0,0 +1,201 @@+                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright © 2020 Input Output (Hong Kong) Ltd.++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ README.md view
@@ -0,0 +1,147 @@+<p align="center">+  <big><strong>Cardano Transactions</strong></big>+</p>++<p align="center">+  <a href="https://hackage.haskell.org/package/cardano-transactions"><img src="https://img.shields.io/hackage/v/cardano-transactions?style=for-the-badge" /></a>+  <a href="https://github.com/input-output-hk/cardano-transactions/releases"><img src="https://img.shields.io/github/v/release/input-output-hk/cardano-transactions?color=%239b59b6&label=RELEASE&sort=semver&style=for-the-badge"/></a>+  <a href="https://github.com/input-output-hk/cardano-transactions/actions?query=workflow%3ATest"><img src="https://img.shields.io/github/workflow/status/input-output-hk/cardano-transactions/Test?style=for-the-badge" /></a>+  <a href="https://input-output-hk.github.io/cardano-transactions/coverage/hpc_index.html"><img src="https://input-output-hk.github.io/cardano-transactions/coverage/badge.svg" /></a>+</p>++## Overview++This library provides a simple interface for building transactions on Cardano. It also+provides a small command-line interface for playing around in a terminal.++## Payment++We call _Payment_ a simple UTxO transactions with no metadata, moving funds from a set of inputs to a set of outputs.++Payments are constructed from a small set of primitives, following the given state-machine:++![](https://mermaid.ink/img/eyJjb2RlIjoic3RhdGVEaWFncmFtXG5cdFsqXSAtLT4gQ29pblNlbGVjdGlvbjogZW1wdHlcbiAgICBDb2luU2VsZWN0aW9uIC0tPiBDb2luU2VsZWN0aW9uOiBhZGRJbnB1dFxuICAgIENvaW5TZWxlY3Rpb24gLS0-IENvaW5TZWxlY3Rpb246IGFkZE91dHB1dFxuICAgIENvaW5TZWxlY3Rpb24gLS0-IFRyYW5zYWN0aW9uOiBsb2NrXG4gICAgVHJhbnNhY3Rpb24gLS0-IFRyYW5zYWN0aW9uOiBzaWduV2l0aFxuICAgIFRyYW5zYWN0aW9uIC0tPiBbKl06IHNlcmlhbGl6ZSIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0In0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9)++### Example (Haskell)++<details>+  <summary>Imports</summary>++```hs+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}++import Data.Function+    ( (&) )+import Data.Maybe+    ( fromMaybe )+import Data.UTxO.Transaction.Cardano.Byron+    ( fromBase16+    , fromBase58+    , mkInput+    , mkOutput+    , mkSignKey+    , testnetMagic+    )++import qualified Data.ByteString as BS+import qualified Data.UTxO.Transaction as Tx+```+</details>++<details>+  <summary>Constructing Inputs / Outputs</summary>++```hs+-- Say we want to construct a transaction from a known input to two+-- different addresses. Let's start by constructing the primitive types+-- for /Byron/ by using the smart-constructors from:+--+--   'Data.UTxO.Transaction.Cardano.Byron'++let (Just input0) = mkInput 0 =<< fromBase16+      "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7"++let Just key0 = mkSignKey =<< fromBase16+      "e0860dab46f13e74ab834142e8877b80bf22044cae8ebab7a21ed1b8dc00c155\+      \f6b78eee2a5bbd453ce7e7711b2964abb6a36837e475271f18ff36ae5fc8af73\+      \e25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf834ee1fb"++let oneAda = 1_000_000++let (Just output0) = mkOutput oneAda =<< fromBase58+      "2cWKMJemoBajc46Wu4Z7e6bG48myZWfB7Z6pD77L6PrJQWt9HZ3Yv7o8CYZTBMqHTPTkv"++let (Just output1) = mkOutput oneAda =<< fromBase58+      "2cWKMJemoBaiLiNB8QpHKjkQhnPdQSyxaLb8JJFUQYpiVzgEJE59fN7V7StqnyDuDjHYJ"+```+</details>++<details>+  <summary>Constructing The Transaction</summary>++```hs+-- Next, we can construct the transaction using the DSL provided by:+--+--   'Data.UTxO.Transaction#MkPayment'++let eitherTx = Tx.empty testnetMagic+      & Tx.addInput input0+      & Tx.addOutput output0+      & Tx.addOutput output1+      & Tx.lock+      & Tx.signWith key0+      & Tx.serialize+```+</details>++<details>+  <summary>Writing Binary Transaction to a File</summary>++```hs+-- Finally, let's export the binary transaction to a file, if we didn't screw+-- up the in the above example ^^"++case eitherTx of+  Left e ->+    fail $ show e+  Right bytes ->+    BS.writeFile "transaction.bin" bytes+```+</details>++### Example (CLI)++```shell+cardano-tx empty 764824073 \+  | cardano-tx add-input 0 3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7 \+  | cardano-tx add-output 42 Ae2tdPwUPEZETXfbQxKMkMJQY1MoHCBS7bkw6TmhLjRvi9LZh1uDnXy319f \+  | cardano-tx lock \+  | cardano-tx sign-with e0860dab46f13e74ab834142e8877b80bf22044cae8ebab7a21ed1b8dc00c155f6b78eee2a5bbd453ce7e7711b2964abb6a36837e475271f18ff36ae5fc8af73e25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf834ee1fb \+  | cardano-tx serialize+```++## About Library Dependencies++This library requires quite a few exotic dependencies from the cardano realm which aren't+necessarily on hackage nor stackage. The dependencies are listed in [stack.yaml](https://github.com/input-output-hk/cardano-transactions/blob/master/stack.yaml#L7-L33),+make sure to also include those for importing `cardano-transactions`.++## Documentation++API documentation is available [here](https://input-output-hk.github.io/cardano-transactions/haddock).++[End-to-end example](https://github.com/input-output-hk/cardano-transactions/wiki/How-to-submit-transaction-via-cardano-tx-CLI) of constructing transaction via `cardano-tx` and then submitting it.++## Contributing++Pull requests are welcome.++When creating a pull request, please make sure that your code adheres to our+[coding standards](https://github.com/input-output-hk/adrestia/wiki/Coding-Standards).++<hr/>++<p align="center">+  <a href="https://github.com/input-output-hk/cardano-transactions/blob/master/LICENSE"><img src="https://img.shields.io/github/license/input-output-hk/cardano-transactions.svg?style=for-the-badge" /></a>+</p>
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,461 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}++module Main where++import Prelude++import Control.Applicative+    ( (<|>) )+import Control.Exception+    ( bracket )+import Control.Monad+    ( guard, void )+import Data.ByteArray.Encoding+    ( Base (..), convertFromBase, convertToBase )+import Data.ByteString+    ( ByteString )+import Data.Maybe+    ( isNothing )+import Data.Text+    ( Text )+import Data.UTxO.Transaction+    ( MkPayment (..) )+import Data.UTxO.Transaction.Cardano.Byron+    ( Byron+    , decodeCoinSel+    , decodeTx+    , encodeCoinSel+    , encodeTx+    , fromBase16+    , fromBase58+    , mkInit+    , mkInput+    , mkOutput+    , mkSignKey+    )+import Data.Word+    ( Word32 )+import Numeric.Natural+    ( Natural )+import Options.Applicative+    ( ArgumentFields+    , CommandFields+    , Mod+    , Parser+    , ParserInfo+    , argument+    , auto+    , command+    , customExecParser+    , flag+    , flag'+    , footerDoc+    , header+    , headerDoc+    , helper+    , info+    , long+    , maybeReader+    , metavar+    , prefs+    , progDesc+    , showHelpOnEmpty+    , subparser+    )+import Options.Applicative.Help.Pretty+    ( hardline, indent, string, vsep )+import System.Console.ANSI+    ( Color (..)+    , ColorIntensity (..)+    , ConsoleLayer (..)+    , SGR (..)+    , hSetSGR+    , hSupportsANSIWithoutEmulation+    )+import System.Exit+    ( exitFailure )+import System.IO+    ( Handle, hIsTerminalDevice, stderr, stdin, stdout )++import qualified Codec.CBOR.Decoding as CBOR+import qualified Codec.CBOR.Encoding as CBOR+import qualified Codec.CBOR.Read as CBOR+import qualified Codec.CBOR.Write as CBOR+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as TIO+import qualified Data.UTxO.Transaction as Tx+++main :: IO ()+main = setup >> parseCmd >>= \case+    CmdEmpty Empty{protocolMagic} -> do+        let state0 = Tx.empty protocolMagic+        hPutState stdout (encodeCoinSel state0)++    CmdAddInput AddInput{inputIx,inputTxId} -> do+        state <- hGetState stdin decodeCoinSel+        case mkInput inputIx inputTxId of+            Nothing -> failWith "Invalid input index or transaction id."+            Just input -> do+                let state' = Tx.addInput input state+                hPutState stdout (encodeCoinSel state')++    CmdAddOutput AddOutput{coin,address} -> do+        state <- hGetState stdin decodeCoinSel+        case mkOutput coin address of+            Nothing -> failWith "Invalid output value or address."+            Just output -> do+                let state' = Tx.addOutput output state+                hPutState stdout (encodeCoinSel state')++    CmdLock -> do+        state <- hGetState stdin decodeCoinSel+        let state' = Tx.lock state+        hPutState stdout (encodeTx state')++    CmdSignWith SignWith{prvKey} -> do+        state <- hGetState stdin decodeTx+        case mkSignKey prvKey of+            Nothing -> failWith "Invalid signing key."+            Just signKey -> do+                let state' = Tx.signWith signKey state+                hPutState stdout (encodeTx state')++    CmdSerialize Serialize{base} -> do+        state <- hGetState stdin decodeTx+        case Tx.serialize state of+            Left e -> failWith (show e)+            Right bytes -> TIO.putStr $ T.decodeUtf8 $ convertToBase base bytes+  where+    setup :: IO ()+    setup = do+        -- Enable ANSI colors on Windows.+        void $ hSupportsANSIWithoutEmulation stderr++parseCmd :: IO Cmd+parseCmd = customExecParser (prefs showHelpOnEmpty) cmd++data Cmd+    = CmdEmpty Empty+    | CmdAddInput AddInput+    | CmdAddOutput AddOutput+    | CmdLock+    | CmdSignWith SignWith+    | CmdSerialize Serialize+    deriving (Show)++cmd :: ParserInfo Cmd+cmd = info (helper <*> cmds) $ progDesc "cardano-tx"+    <> headerDoc (Just $ vsep+        [ string "Construct and sign transactions according to the following state-machine:"+        , hardline+        , string "                           empty                    "+        , string "                             |                      "+        , string "      *------------------*   |   *-----------------*"+        , string "      |                  |   |   |                 |"+        , string "      |                  v   v   v                 |"+        , string "      *--- add-output ---=========--- add-input ---*"+        , string "                             |                      "+        , string "                             |                      "+        , string "                           lock   *----------------*"+        , string "                             |    |                |"+        , string "                             |    v                |"+        , string "                         =========--- sign-with ---*"+        , string "                             |                      "+        , string "                             |                      "+        , string "                         serialize                  "+        , string "                             |                      "+        , string "                             |                      "+        , string "                             v                      "+        , hardline+        , string "/!\\ Except 'serialize', every command outputs an intermediate \+            \state that is of little use and shouldn't be tempered with."+        , string "Redirect the output to a file, or use Unix pipes as shown below."+        ])+    <> footerDoc (Just $ vsep+        [ "Example:"+        , indent 2 $ string "cardano-tx empty 764824073 \\"+        , indent 2 $ string "  | cardano-tx add-input 0 \\"+        , indent 2 $ string "      3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7 \\"+        , indent 2 $ string "  | cardano-tx add-output 42 \\"+        , indent 2 $ string "      Ae2tdPwUPEZETXfbQxKMkMJQY1MoHCBS7bkw6TmhLjRvi9LZh1uDnXy319f \\"+        , indent 2 $ string "  | cardano-tx lock \\"+        , indent 2 $ string "  | cardano-tx sign-with \\"+        , indent 2 $ string "      e0860dab46f13e74ab834142e8877b80bf22044cae8ebab7a21ed1b8dc00c155 \\"+        , indent 2 $ string "      f6b78eee2a5bbd453ce7e7711b2964abb6a36837e475271f18ff36ae5fc8af73 \\"+        , indent 2 $ string "      e25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf834ee1fb \\"+        , indent 2 $ string "  | cardano-tx serialize"+        ])+  where+    cmds = subparser $ mconcat+        [ cmdEmpty+        , cmdAddInput+        , cmdAddOutput+        , cmdLock+        , cmdSignWith+        , cmdSerialize+        ]++--                       _+--                      | |+--   ___ _ __ ___  _ __ | |_ _   _+--  / _ \ '_ ` _ \| '_ \| __| | | |+-- |  __/ | | | | | |_) | |_| |_| |+--  \___|_| |_| |_| .__/ \__|\__, |+--                | |         __/ |+--                |_|        |___/++newtype Empty = Empty+    { protocolMagic :: Init Byron+    } deriving (Show)++cmdEmpty :: Mod CommandFields Cmd+cmdEmpty = command "empty" $+    info (helper <*> fmap CmdEmpty subCmd) mempty+  where+    subCmd = Empty <$> protocolMagicArg++protocolMagicArg :: Parser (Init Byron)+protocolMagicArg = fmap mkInit $ argument auto $+    metavar "PROTOCOL MAGIC"++--            _     _        _                   _+--           | |   | |      (_)                 | |+--   __ _  __| | __| |______ _ _ __  _ __  _   _| |_+--  / _` |/ _` |/ _` |______| | '_ \| '_ \| | | | __|+-- | (_| | (_| | (_| |      | | | | | |_) | |_| | |_+--  \__,_|\__,_|\__,_|      |_|_| |_| .__/ \__,_|\__|+--                                  | |+--                                  |_|++data AddInput = AddInput+    { inputIx :: Word32+    , inputTxId :: ByteString+    } deriving (Show)++cmdAddInput :: Mod CommandFields Cmd+cmdAddInput = command "add-input" $+    info (helper <*> fmap CmdAddInput subCmd) $ mconcat+        [ progDesc "Add a new input to the transaction."+        , headerDoc (Just $ vsep+            [ string "An input is made of:"+            , indent 2 $ string "- A input index."+            , indent 2 $ string "- An transaction id, 32 bytes, base16-encoded."+            ])+        , footerDoc (Just $ vsep+            [ string "Example:"+            , indent 2 $ string "add-input 0 3b40265...aad1c0b7"+            , indent 2 $ string "            <--- 64 CHARS --->"+            ])+        ]+  where+    subCmd = AddInput <$> inputIxArg <*> inputTxIdArg++inputIxArg :: Parser Word32+inputIxArg = argument auto $+    metavar "INDEX"++inputTxIdArg :: Parser ByteString+inputTxIdArg = bytesArgument (Just 32) fromBase16 $+    metavar "TXID"++--            _     _                   _               _+--           | |   | |                 | |             | |+--   __ _  __| | __| |______ ___  _   _| |_ _ __  _   _| |_+--  / _` |/ _` |/ _` |______/ _ \| | | | __| '_ \| | | | __|+-- | (_| | (_| | (_| |     | (_) | |_| | |_| |_) | |_| | |_+--  \__,_|\__,_|\__,_|      \___/ \__,_|\__| .__/ \__,_|\__|+--                                         | |+--                                         |_|++data AddOutput = AddOutput+    { coin :: Natural+    , address :: ByteString+    } deriving (Show)++cmdAddOutput :: Mod CommandFields Cmd+cmdAddOutput = command "add-output" $+    info (helper <*> fmap CmdAddOutput subCmd) $ mconcat+        [ progDesc "Add a new output to the transaction."+        , headerDoc (Just $ vsep+            [ string "An output is made of:"+            , indent 2 $ string "- A coin value in Lovelace (1 Ada = 1e6 Lovelace)."+            , indent 2 $ string "- A target address, base58-encoded."+            ])+        , footerDoc (Just $ vsep+            [ string "Example:"+            , indent 2 $ string "add-output 1000000 2cWKMJemo...ZTBMqHTPTkv"+            ])+        ]+  where+    subCmd = AddOutput <$> coinArg <*> addressArg++coinArg :: Parser Natural+coinArg = argument auto $+    metavar "LOVELACE"++addressArg :: Parser ByteString+addressArg = bytesArgument Nothing fromBase58 $+    metavar "ADDRESS"++--  _            _+-- | |          | |+-- | | ___   ___| | __+-- | |/ _ \ / __| |/ /+-- | | (_) | (__|   <+-- |_|\___/ \___|_|\_\++cmdLock :: Mod CommandFields Cmd+cmdLock = command "lock" $+    info (helper <*> pure CmdLock) $ mconcat+        [ progDesc "Lock the transaction and start signing inputs."+        , header+            "Once locked, it is no longer possible to add inputs or outputs to \+            \a transaction. This is a necessary step to be able to sign the \+            \transaction with private keys corresponding to inputs."+        ]++--      _             _    _ _ _   _+--     (_)           | |  | (_) | | |+--  ___ _  __ _ _ __ | |  | |_| |_| |__+-- / __| |/ _` | '_ \| |/\| | | __| '_ \+-- \__ \ | (_| | | | \  /\  / | |_| | | |+-- |___/_|\__, |_| |_|\/  \/|_|\__|_| |_|+--         __/ |+--        |___/++newtype SignWith = SignWith+    { prvKey :: ByteString+    } deriving (Show)++cmdSignWith :: Mod CommandFields Cmd+cmdSignWith = command "sign-with" $+    info (helper <*> fmap CmdSignWith subCmd) $ mconcat+        [ progDesc "Add a signature."+        ]+  where+    subCmd = SignWith <$> prvKeyArg++prvKeyArg :: Parser ByteString+prvKeyArg = bytesArgument (Just 96) fromBase16 $+    metavar "XPRV"++--                _       _ _+--               (_)     | (_)+--  ___  ___ _ __ _  __ _| |_ _______+-- / __|/ _ \ '__| |/ _` | | |_  / _ \+-- \__ \  __/ |  | | (_| | | |/ /  __/+-- |___/\___|_|  |_|\__,_|_|_/___\___|+--++newtype Serialize = Serialize+    { base :: Base+    } deriving Show++cmdSerialize :: Mod CommandFields Cmd+cmdSerialize = command "serialize" $+    info (helper <*> fmap CmdSerialize subCmd) $ mconcat+        [ progDesc "Serialize the signed transaction to binary."+        ]+  where+    subCmd = serializeArg++serializeArg :: Parser Serialize+serializeArg = base16Flag <|> base64Flag+  where+    base16Flag = flag' (Serialize Base16) (long "base16")+    base64Flag = flag (Serialize Base64) (Serialize Base64) (long "base64")++--  _   _      _+-- | | | |    | |+-- | |_| | ___| |_ __   ___ _ __ ___+-- |  _  |/ _ \ | '_ \ / _ \ '__/ __|+-- | | | |  __/ | |_) |  __/ |  \__ \+-- \_| |_/\___|_| .__/ \___|_|  |___/+--              | |+--              |_|++-- | Parse a encoded 'Bytestring' argument.+bytesArgument+    :: Maybe Int -- ^ Number of bytes that are expected, if known.+    -> (Text -> Maybe ByteString) -- ^ A base conversion+    -> Mod ArgumentFields ByteString+    -> Parser ByteString+bytesArgument len fromBase =+    argument (maybeReader readerT)+  where+    readerT :: String -> Maybe ByteString+    readerT str = do+        bytes <- fromBase $ T.pack str+        guard (isNothing len || Just (BS.length bytes) == len)+        pure bytes++-- | Convert a 'ByteString' to 'Base64'+base64 :: ByteString -> Text+base64 = T.decodeUtf8 . convertToBase Base64++-- | Convert a /Base64/ 'Text' into a 'ByteString'+fromBase64 :: Text -> Maybe ByteString+fromBase64 = either (const Nothing) Just . convertFromBase Base64 . T.encodeUtf8++-- | Deserialize data from a /Base64/ CBOR 'ByteString', or fail.+hGetState :: Handle -> (forall s. CBOR.Decoder s a) -> IO a+hGetState h decoder = do+    bytes <- stripPEM . T.decodeUtf8 <$> BS.hGetContents h+    case fromBase64 bytes of+        Nothing -> failWith+            "Unable to decode intermediate buffer. Did you manually crafted one?"+        Just cbor -> case CBOR.deserialiseFromBytes decoder (BL.fromStrict cbor) of+            Left e -> failWith (show e)+            Right (_, a) -> pure a+  where+    stripPEM =+        T.replace "\n" "" . T.unlines . dropFromEnd 1 . drop 2 . T.lines+    dropFromEnd n =+        reverse . drop n . reverse++-- | Helper to output a given state to the console+hPutState :: Handle -> CBOR.Encoding -> IO ()+hPutState h =+    TIO.hPutStr h . encodePEM . base64 . CBOR.toStrictByteString+  where+    encodePEM :: Text -> Text+    encodePEM body = T.unlines+        [ "-----BEGIN CARDANO TX-----"+        , "version: 1.0.0"+        , T.intercalate "\n" (mkGroupsOf 64 body)+        , "-----END CARDANO TX-----"+        ]++    mkGroupsOf :: Int -> Text -> [Text]+    mkGroupsOf n xs+        | T.null xs = []+        | otherwise = (T.take n xs) : mkGroupsOf n (T.drop n xs)+++-- | Fail with a colored red error message.+failWith :: String -> IO a+failWith msg = do+    withSGR stderr (SetColor Foreground Vivid Red) $ do+        TIO.hPutStrLn stderr (T.pack msg)+    exitFailure++-- | Bracket-style constructor for applying ANSI Select Graphic Rendition to an+-- action and revert back to normal after.+--+-- This does nothing if the device isn't an ANSI terminal.+withSGR :: Handle -> SGR -> IO a -> IO a+withSGR h sgr action = hIsTerminalDevice h >>= \case+    True  -> bracket aFirst aLast aBetween+    False -> action+  where+    aFirst = ([] <$ hSetSGR h [sgr])+    aLast = hSetSGR h+    aBetween = const action
+ cardano-transactions.cabal view
@@ -0,0 +1,109 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: b9405fb4feb0c9a4a545e76859db770d6ce65af4316862025cd7466c9e29eb69++name:           cardano-transactions+version:        1.0.0+synopsis:       Library utilities for constructing and signing Cardano transactions.+description:    Please see the README on GitHub at <https://github.com/input-output-hk/cardano-transactions>+category:       Cardano+homepage:       https://github.com/input-output-hk/cardano-transactions#readme+bug-reports:    https://github.com/input-output-hk/cardano-transactions/issues+author:         IOHK+maintainer:     operations@iohk.io+copyright:      2020 IOHK+license:        Apache-2.0+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/input-output-hk/cardano-transactions++flag release+  description: Compile executables for a release.+  manual: True+  default: False++library+  exposed-modules:+      Data.UTxO.Transaction+      Data.UTxO.Transaction.Cardano.Byron+  other-modules:+      Paths_cardano_transactions+  hs-source-dirs:+      src+  ghc-options: -Wcompat -fwarn-redundant-constraints+  build-depends:+      base >=4.7 && <5+    , base58-bytestring+    , bytestring+    , cardano-binary+    , cardano-crypto+    , cardano-crypto-wrapper+    , cardano-ledger+    , cborg+    , cryptonite+    , extra+    , memory+    , text+  if flag(release)+    ghc-options: -Werror -Wall+  default-language: Haskell2010++executable cardano-tx+  main-is: Main.hs+  other-modules:+      Paths_cardano_transactions+  hs-source-dirs:+      app+  ghc-options: -Wcompat -fwarn-redundant-constraints -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      ansi-terminal+    , base >=4.7 && <5+    , bytestring+    , cardano-transactions+    , cborg+    , memory+    , optparse-applicative+    , text+  if flag(release)+    ghc-options: -Werror -Wall -static -O2+    cc-options: -static+    ld-options: -static -pthread+  default-language: Haskell2010++test-suite unit+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules:+      Data.UTxO.Transaction.Cardano.ByronSpec+      Paths_cardano_transactions+  hs-source-dirs:+      test+  ghc-options: -Wcompat -fwarn-redundant-constraints -threaded -rtsopts -with-rtsopts=-N+  build-tools:+      cardano-tx+  build-depends:+      QuickCheck+    , base >=4.7 && <5+    , bytestring+    , cardano-crypto-wrapper+    , cardano-ledger+    , cardano-ledger-test+    , cardano-transactions+    , cborg+    , hedgehog-quickcheck+    , hspec+    , process+    , text+  if flag(release)+    ghc-options: -Werror -Wall+  default-language: Haskell2010
+ src/Data/UTxO/Transaction.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilyDependencies #-}++module Data.UTxO.Transaction+    (+    -- * Overview+    -- $overview++    -- * Abstraction+      MkPayment (..)+    , ErrMkPayment (..)++    -- * Note about type-level constraints+    -- $note+    ) where++import Prelude++import Data.ByteString+    ( ByteString )++-- $overview+--+-- This module offers an abstraction over the construction of simple UTxO+-- transactions. Such a transaction is called a /Payment/. Payments are+-- constructed by selecting inputs and outputs, and signing each input+-- independently.+--+-- This can be represented as a state machine, such as:+--+-- @+--                          empty+--                            |+--      *-----------------*   |   *----------------*+--      |                 |   |   |                |+--      |                 v   v   v                |+--      *--- addOutput ---=========--- addInput ---*+--                            |+--                            |+--                          lock  *----------------*+--                            |   |                |+--                            |   v                |+--                        =========--- signWith ---*+--                            |+--                            |+--                        serialize+--                            |+--                            |+--                            v+--                           ...+-- @+--+-- Functions from the 'MkPayment' interfaces are designed to be easy to chain+-- like pipes. To give a concrete example, let's see how to construct a+-- transaction with /one input/ and /two outputs/:+--+-- @+-- import Data.Function+--     ( (&) )+-- import qualified Data.UTxO.Transaction as Tx+--+-- bytes = Tx.empty+--       & Tx.addInput  input0+--       & Tx.addOutput output0+--       & Tx.addOutput output1+--       & Tx.lock+--       & Tx.signWith  key0+--       & Tx.serialize+-- @+--+-- The reason why the 'lock' step is required is because, in order to create+-- witnesses, one must produce signatures of the whole transaction body (inputs+-- + outputs). Hence, signatures can only be added once all inputs and all+-- outputs have already been addded.+--+-- This is correct-by-construction thanks to the 'lock' step and the distinction+-- between 'CoinSel' and 'Tx'.++-- | Abstract interface for constructing a /Payment/.+class MkPayment dom where+    -- | Some initial extra data needed to construct and or serialize the+    -- transaction. This is a free-type which is implementation-specific.+    --+    -- For example, Byron transactions requires the underlying protocol magic in+    -- order to produce witnesses. This is an appropriate place for.+    type Init    dom :: *++    -- | Type representation of transaction 'Input'+    type Input   dom = inp | inp -> dom++    -- | Type representation of transaction 'Output'+    type Output  dom = out | out -> dom++    -- | Type representation of transaction 'SignKey'+    type SignKey dom = key | key -> dom++    -- | An intermediate type for representing a transaction under construction.+    -- 'Input' and 'Output' can still be added to a 'CoinSel' and must be added+    -- before trying to sign any of the inputs.+    type CoinSel dom = sel | sel -> dom++    -- | A transaction is a locked 'CoinSel' to which it is no longer possible+    -- to add new 'Input' and or 'Output'. Only 'Tx' can be serialized.+    type Tx      dom = tx  | tx  -> dom++    -- * Transaction construction+    empty     :: Init dom -> CoinSel dom+    addInput  :: Input  dom -> CoinSel dom -> CoinSel dom+    addOutput :: Output dom -> CoinSel dom -> CoinSel dom++    -- * Transaction signature+    lock      :: CoinSel dom -> Tx dom+    signWith  :: SignKey dom -> Tx dom -> Tx dom++    -- * Transaction serialization+    serialize :: Tx dom -> Either ErrMkPayment ByteString++-- | Possible errors occuring when constructing a /Payment/. This can in+-- practice be safely ignored if the transaction is known to be well-formed.+data ErrMkPayment+    = MissingInput+        -- ^ Payments must have at least one input.+    | MissingOutput+        -- ^ Payments must have at least one output+    | MissingSignature+        -- ^ Payments must have a signature for each input.+    deriving (Show, Read, Eq)++-- $note+--+-- It is relatively easy to tweak the interface below to get it fully type-safe+-- by using an inductive type, such as:+--+-- @+-- data N = Zero | Some N+--+-- class MkPayment dom where+--     type Init    dom :: *+--+--     type Input   dom = inp | inp -> dom+--     type Output  dom = out | out -> dom+--     type SignKey dom = key | key -> dom+--+--     type CoinSel dom = (sel :: N -> N -> *) | sel -> dom+--     type Tx      dom = (tx  :: N -> *)      | tx  -> dom+--+--     empty     :: Init dom -> CoinSel dom Zero Zero+--+--     addInput  :: Input  dom -> CoinSel dom n m -> CoinSel dom (Some n) m+--     addOutput :: Output dom -> CoinSel dom n m -> CoinSel dom n (Some m)+--+--     lock      :: CoinSel dom (Some n) (Some m) -> Tx dom (Some n)+--+--     signWith :: SignKey dom -> Tx dom (Some n) -> Tx dom n+--+--     serialize :: Tx dom Zero -> ByteString+-- @+--+-- Such approach is great for ensuring that the transaction can't be ill-formed+-- and works well when manually constructing the transaction as shown above.+--+-- It gets however quite complex when one wants to start folding or mapping on a+-- list of inputs or outputs where their numbers isn't known at compile-time!+-- Indeed, each call to 'addInput' does actually return a new type!+--+-- Let's say we want to write the following function:+--+-- @+-- addMany :: [Input dom] -> CoinSel dom n m -> CoinSel dom ? m+-- @+--+-- What should be the type of `?`. This can't possibly known at compile-time.
+ src/Data/UTxO/Transaction/Cardano/Byron.hs view
@@ -0,0 +1,360 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_HADDOCK prune #-}++module Data.UTxO.Transaction.Cardano.Byron+    (+    -- * Initialization+      mkInit+    , mainnetMagic+    , testnetMagic++    -- * Constructing Primitives+    , mkInput+    , mkOutput+    , mkSignKey++    -- * Converting From Bases+    , fromBase16+    , fromBase58+    , fromBase64++    -- Internal+    , Byron+    , encodeCoinSel+    , decodeCoinSel+    , encodeTx+    , decodeTx+    ) where++import Cardano.Binary+    ( FromCBOR (..), ToCBOR (..) )+import Cardano.Chain.Common+    ( mkAttributes, mkLovelace )+import Cardano.Chain.UTxO+    ( TxIn (..), TxInWitness (..), TxOut (..), TxSigData (..), mkTxAux )+import Cardano.Crypto.Hashing+    ( AbstractHash (..), hash )+import Cardano.Crypto.ProtocolMagic+    ( ProtocolMagicId (..) )+import Cardano.Crypto.Signing+    ( SignTag (..), Signature, SigningKey (..), VerificationKey (..) )+import Cardano.Crypto.Wallet+    ( toXPub, xprv )+import Codec.CBOR.Read+    ( deserialiseFromBytes )+import Crypto.Error+    ( eitherCryptoError )+import Crypto.Hash+    ( Blake2b_256, digestFromByteString )+import Data.ByteArray.Encoding+    ( Base (..), convertFromBase )+import Data.ByteString+    ( ByteString )+import Data.ByteString.Base58+    ( bitcoinAlphabet, decodeBase58 )+import Data.Either.Extra+    ( eitherToMaybe )+import Data.List.NonEmpty+    ( NonEmpty, nonEmpty )+import Data.Text+    ( Text )+import Data.UTxO.Transaction+    ( ErrMkPayment (..), MkPayment (..) )+import Data.Word+    ( Word32 )+import GHC.Exts+    ( IsList (fromList) )+import Numeric.Natural+    ( Natural )++import qualified Cardano.Chain.UTxO as CC+import qualified Cardano.Crypto.Signing as CC+import qualified Codec.CBOR.Decoding as CBOR+import qualified Codec.CBOR.Encoding as CBOR+import qualified Codec.CBOR.Write as CBOR+import qualified Crypto.ECC.Edwards25519 as Ed25519+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Data.Text.Encoding as T++-- | Construct a payment 'Init' for /Byron/ from primitive types.+--+-- __examples__:+--+-- >>> mkInit 764824073 == mainnetMagic+-- True+--+-- >>> mkInit 1097911063 == testnetMagic+-- True+--+-- @since 1.0.0+mkInit+    :: Word32+        -- ^ A protocol magic id+    -> Init Byron+mkInit =+    ProtocolMagicId++-- | Pre-defined 'Init' magic for /Byron/ MainNet.+--+-- @since 1.0.0+mainnetMagic :: Init Byron+mainnetMagic = mkInit 764824073++-- | Pre-defined 'Init' magic for /Byron/ TestNet.+--+-- @since 1.0.0+testnetMagic :: Init Byron+testnetMagic = mkInit 1097911063++-- | Construct a payment 'Input' for /Byron/ from primitive types.+--+-- __example__:+--+-- >>> mkInput 14 =<< fromBase16 "3b402651...aad1c0b7"+-- Just (Input ...)+--+-- @since 1.0.0+mkInput+    :: Word32+        -- ^ Input index.+    -> ByteString+        -- ^ Input transaction id. See also: 'fromBase16'.+    -> Maybe (Input Byron)+mkInput ix bytes =+    case digestFromByteString @Blake2b_256 bytes of+        Just txId -> Just $ TxInUtxo (AbstractHash txId) ix+        Nothing -> Nothing++-- | Construct a payment 'Output' for /Byron/ from primitive types.+--+-- __example__:+--+-- >>> mkOutput 42 =<< fromBase58 "Ae2tdPwU...DnXy319f"+-- Just (Output ...)+--+-- @since 1.0.0+mkOutput+    :: Natural+        -- ^ Output value, in Lovelace (1 Ada = 1e6 Lovelace).+    -> ByteString+        -- ^ Output Address. See also: 'fromBase58'.+    -> Maybe (Output Byron)+mkOutput n bytes =+    case (fromCBOR' bytes, mkLovelace (fromIntegral n)) of+        (Right addr, Right coin) -> Just $ TxOut addr coin+        _ -> Nothing+  where+    fromCBOR' = fmap snd . deserialiseFromBytes fromCBOR .  BL.fromStrict++-- | Construct a 'SignKey' for /Byron/ from primitive types.+--+-- __example__:+--+-- >>> mkSignKey =<< fromBase16 "3b402651...aad1c0b7"+-- Just (SignKey ...)+--+-- @since 1.0.0+mkSignKey+    :: ByteString+        -- ^ A extended address private key and its chain code.+        -- The key __must be 96 bytes__ long, internally made of two concatenated parts:+        --+        -- @+        -- BYTES = PRV | CC+        -- PRV   = 64OCTET  # a 64 bytes Ed25519 extended private key+        -- CC    = 32OCTET  # a 32 bytes chain code+        -- @+        --+        -- See also: 'fromBase16'.+    -> Maybe (SignKey Byron)+mkSignKey bytes+    | BS.length bytes /= 96 = Nothing+    | otherwise = do+        let (prv, cc) = BS.splitAt 64 bytes+        pub <- ed25519ScalarMult (BS.take 32 prv)+        fmap SigningKey $ eitherToMaybe $ xprv $ prv <> pub <> cc+  where+    ed25519ScalarMult :: ByteString -> Maybe ByteString+    ed25519ScalarMult bs = do+        scalar <- eitherToMaybe $ eitherCryptoError $ Ed25519.scalarDecodeLong bs+        pure $ Ed25519.pointEncode $ Ed25519.toPoint scalar++--+-- ByteString Decoding+--++-- | Convert a base16 encoded 'Text' into a raw 'ByteString'+--+-- @since 1.0.0+fromBase16 :: Text -> Maybe ByteString+fromBase16 = eitherToMaybe . convertFromBase Base16 . T.encodeUtf8++-- | Convert a base58 encoded 'Text' into a raw 'ByteString'+--+-- @since 1.0.0+fromBase58 :: Text -> Maybe ByteString+fromBase58 = decodeBase58 bitcoinAlphabet . T.encodeUtf8+++-- | Convert a base64 encoded 'Text' into a raw 'ByteString'+--+-- @since 1.0.0+fromBase64 :: Text -> Maybe ByteString+fromBase64 = eitherToMaybe . convertFromBase Base64 . T.encodeUtf8++--+-- MkPayment instance+--++-- Type-level constructor capturing types for 'Byron'.+data Byron++instance MkPayment Byron where+    type Init Byron = ProtocolMagicId++    type Input   Byron = TxIn+    type Output  Byron = TxOut+    type SignKey Byron = SigningKey++    type CoinSel Byron =+        (ProtocolMagicId, [TxIn], [TxOut])++    type Tx Byron = Either+        ErrMkPayment+        (ProtocolMagicId, NonEmpty TxIn, NonEmpty TxOut, TxSigData, [TxInWitness])++    empty :: ProtocolMagicId -> CoinSel Byron+    empty pm = (pm, mempty, mempty)++    addInput :: TxIn -> CoinSel Byron -> CoinSel Byron+    addInput inp (pm, inps, outs) = (pm, inp : inps, outs)++    addOutput :: TxOut -> CoinSel Byron -> CoinSel Byron+    addOutput out (pm, inps, outs) = (pm, inps, out : outs)++    lock :: CoinSel Byron -> Tx Byron+    lock (_pm, [], _outs) = Left MissingInput+    lock (_pm, _inps, []) = Left MissingOutput+    lock (pm, inps, outs) =+        Right (pm, neInps, neOuts, sigData, mempty)+      where+        sigData = TxSigData $ hash $ CC.UnsafeTx neInps neOuts (mkAttributes ())+        neInps  = NE.fromList $ reverse inps+        neOuts  = NE.fromList $ reverse outs++    signWith :: SigningKey -> Tx Byron -> Tx Byron+    signWith _ (Left e) = Left e+    signWith (SigningKey prv) (Right (pm, inps, outs, sigData, wits)) =+        Right (pm, inps, outs, sigData, VKWitness vk sig : wits)+      where+        vk :: VerificationKey+        vk = VerificationKey (toXPub prv)++        sig :: Signature TxSigData+        sig = CC.sign pm SignTx (SigningKey prv) sigData++    serialize :: Tx Byron -> Either ErrMkPayment ByteString+    serialize (Left e) = Left e+    serialize (Right (_pm, inps, outs, _sigData, wits))+        | NE.length inps /= length wits = Left MissingSignature+        | otherwise = Right $ CBOR.toStrictByteString $ toCBOR $ mkTxAux+            (CC.UnsafeTx inps outs (mkAttributes ()))+            (fromList $ reverse wits)+++-- Internal+--+-- For running the Payment DSL as a command-line, we need to be able to produce+-- a text output representing the internal state.+--+-- There's no point about obfuscating this more than necessary; this should+-- remain mainly invisible to users if used properly through pipes. Yet, it can+-- be useful to have a human-friendly representation that is base16 or base64+-- encoded. Three possible obvious choice:+--+-- - CBOR, since most data above can already be serialized to CBOR+-- - JSON, instances exists on most type and are derived generically from CBOR+-- - Show, although here most types don't have a corresponding 'Read' instance.+--+-- Since we also need to decode an encoded state, CBOR will be path of least+-- resistance.++-- __Internal__: Encode a 'CoinSel Byron' to CBOR.+encodeCoinSel :: CoinSel Byron -> CBOR.Encoding+encodeCoinSel (pm, inps, outs) = mconcat+    [ toCBOR pm+    , CBOR.encodeListLenIndef+    , mconcat (toCBOR <$> inps)+    , CBOR.encodeBreak+    , CBOR.encodeListLenIndef+    , mconcat (toCBOR <$> outs)+    , CBOR.encodeBreak+    ]++-- __Internal__: Decode a 'CoinSel Byron' from CBOR.+decodeCoinSel :: CBOR.Decoder s (CoinSel Byron)+decodeCoinSel = (,,)+    <$> fromCBOR+    <*> decodeListIndef fromCBOR+    <*> decodeListIndef fromCBOR++-- __Internal__: Encode a 'Tx Byron' to CBOR.+encodeTx :: Tx Byron -> CBOR.Encoding+encodeTx (Left e) = mconcat+    [ CBOR.encodeWord8 0+    , CBOR.encodeString $ T.pack $ show e+    ]+encodeTx (Right (pm, inps, outs, sigData, wits)) = mconcat+    [ CBOR.encodeWord8 1+    , encodeCoinSel (pm, NE.toList inps, NE.toList outs)+    , toCBOR sigData+    , CBOR.encodeListLenIndef+    , mconcat (toCBOR <$> wits)+    , CBOR.encodeBreak+    ]++-- __Internal__: Decode a 'Tx Byron' from CBOR.+decodeTx :: CBOR.Decoder s (Tx Byron)+decodeTx = do+    CBOR.decodeWord8 >>= \case+        0 -> fmap T.unpack CBOR.decodeString >>= \case+            str | str == show MissingInput     -> pure $ Left MissingInput+            str | str == show MissingOutput    -> pure $ Left MissingOutput+            str | str == show MissingSignature -> pure $ Left MissingSignature+            _ -> fail $+                "Invalid error constructor found in 'Tx Byron'. Constructor must \+                \be one of: " <> unwords (show <$>+                    [ MissingInput, MissingOutput, MissingSignature])+        1 -> do+            (pm, inps, outs) <- decodeCoinSel+            sigData <- fromCBOR+            wits <- decodeListIndef fromCBOR+            case (nonEmpty inps, nonEmpty outs) of+                (Nothing, _) -> fail+                    "Empty list of inputs found in 'Tx Byron'. This is impossible \+                    \unless the data has been modified by hand."+                (_, Nothing) -> fail+                    "Empty list of outputs found in 'Tx Byron'. This is impossible \+                    \unless the data has been modified by hand."+                (Just neInps, Just neOuts) ->+                    pure $ Right (pm, neInps, neOuts, sigData, wits)++        _ -> fail+            "'Tx Byron' has been modified  with and is now invalid. The first \+            \byte must be either 0 or 1, followed by respectively an error \+            \constructor or constituants of the intermediate transaction."++-- __Internal__ Decode an arbitrary long list 'sandwiched' by markers.+decodeListIndef :: forall s a. CBOR.Decoder s a -> CBOR.Decoder s [a]+decodeListIndef decodeOne = do+    _ <- CBOR.decodeListLenIndef+    CBOR.decodeSequenceLenIndef (flip (:)) [] reverse decodeOne
+ test/Data/UTxO/Transaction/Cardano/ByronSpec.hs view
@@ -0,0 +1,588 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module Data.UTxO.Transaction.Cardano.ByronSpec+    ( spec+    ) where++import Prelude++import Cardano.Chain.UTxO+    ( TxIn (..), TxInWitness (..), TxOut (..), TxSigData (..) )+import Cardano.Crypto.ProtocolMagic+    ( ProtocolMagicId (..) )+import Control.Monad+    ( foldM )+import Data.ByteString+    ( ByteString )+import Data.Function+    ( (&) )+import Data.List.NonEmpty+    ( NonEmpty )+import Data.Maybe+    ( fromJust, fromMaybe, isNothing )+import Data.Text+    ( Text )+import Data.UTxO.Transaction+    ( ErrMkPayment (..), MkPayment (..) )+import Data.UTxO.Transaction.Cardano.Byron+    ( Byron+    , decodeCoinSel+    , decodeTx+    , encodeCoinSel+    , encodeTx+    , fromBase16+    , fromBase58+    , fromBase64+    , mainnetMagic+    , mkInit+    , mkInput+    , mkOutput+    , mkSignKey+    , testnetMagic+    )+import Data.Word+    ( Word32, Word64 )+import Numeric.Natural+    ( Natural )+import System.Process+    ( readProcess )+import Test.Cardano.Chain.UTxO.Gen+    ( genTxIn, genTxInWitness, genTxOut, genTxSigData )+import Test.Hspec+    ( Spec, describe, expectationFailure, it, shouldBe )+import Test.Hspec.QuickCheck+    ( prop )+import Test.QuickCheck+    ( Arbitrary (..)+    , NonEmptyList (..)+    , Property+    , conjoin+    , counterexample+    , elements+    , forAll+    , listOf+    , oneof+    , property+    , quickCheck+    , suchThat+    , withMaxSuccess+    , (===)+    )+import Test.QuickCheck.Hedgehog+    ( hedgehog )++import qualified Codec.CBOR.Decoding as CBOR+import qualified Codec.CBOR.Encoding as CBOR+import qualified Codec.CBOR.Pretty as CBOR+import qualified Codec.CBOR.Read as CBOR+import qualified Codec.CBOR.Write as CBOR+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Data.UTxO.Transaction as Tx++{-# ANN spec ("HLint: ignore Use head" :: String) #-}++spec :: Spec+spec = do+    describe "CBOR roundtrips" $ do+        it "CoinSel Byron" $ withMaxSuccess 500 $ property $+            prop_RoundTripCBOR encodeCoinSel decodeCoinSel++        it "Tx Byron" $ withMaxSuccess 500 $ property $+            prop_RoundTripCBOR encodeTx decodeTx++    describe "(Mainnet) Golden Tests Transaction Construction" $ do+        it "1 input, 1 output (DSL)" $ do+            compareGolden goldenMainnet__1_1 $ Tx.empty mainnetMagic+                & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                & Tx.addOutput (unsafeMkOutput 42 (addrs !! 0))+                & Tx.lock+                & Tx.signWith (unsafeMkSignKey (keys !! 0))+                & Tx.serialize++        it "1 input, 1 output (CLI)" $ do+            state <- cardanoTx [ "empty", mainnetMagicT ] ""+                 >>= cardanoTx [ "add-input", "0", txids !! 0 ]+                 >>= cardanoTx [ "add-output", "42", addrs !! 0 ]+                 >>= cardanoTx [ "lock" ]+                 >>= cardanoTx [ "sign-with", keys !! 0 ]++            defaul <- fromBase64E <$> cardanoTx [ "serialize" ] state+            base16 <- fromBase16E <$> cardanoTx [ "serialize", "--base16" ] state+            base64 <- fromBase64E <$> cardanoTx [ "serialize", "--base64" ] state+            compareGolden goldenMainnet__1_1 base16+            compareGolden goldenMainnet__1_1 base64+            compareGolden goldenMainnet__1_1 defaul++        it "2 inputs, 2 outputs (DSL)" $ do+            compareGolden goldenMainnet__2_2 $ Tx.empty mainnetMagic+                & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                & Tx.addInput (unsafeMkInput 1 (txids !! 0))+                & Tx.addOutput (unsafeMkOutput 42 (addrs !! 0))+                & Tx.addOutput (unsafeMkOutput 14 (addrs !! 1))+                & Tx.lock+                & Tx.signWith (unsafeMkSignKey (keys !! 0))+                & Tx.signWith (unsafeMkSignKey (keys !! 1))+                & Tx.serialize++        it "2 inputs, 2 outputs (CLI)" $ do+            state <- cardanoTx [ "empty", mainnetMagicT ] ""+                 >>= cardanoTx [ "add-input", "0", txids !! 0 ]+                 >>= cardanoTx [ "add-input", "1", txids !! 0 ]+                 >>= cardanoTx [ "add-output", "42", addrs !! 0 ]+                 >>= cardanoTx [ "add-output", "14", addrs !! 1 ]+                 >>= cardanoTx [ "lock" ]+                 >>= cardanoTx [ "sign-with", keys !! 0 ]+                 >>= cardanoTx [ "sign-with", keys !! 1 ]++            defaul <- fromBase64E <$> cardanoTx [ "serialize" ] state+            base16 <- fromBase16E <$> cardanoTx [ "serialize", "--base16" ] state+            base64 <- fromBase64E <$> cardanoTx [ "serialize", "--base64" ] state+            compareGolden goldenMainnet__2_2 base16+            compareGolden goldenMainnet__2_2 base64+            compareGolden goldenMainnet__2_2 defaul++        it "1 input, 25 outputs (DSL)" $ do+            compareGolden goldenMainnet__25_1 $ Tx.empty mainnetMagic+                & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                & flip (foldr Tx.addOutput) (replicate 25 $ unsafeMkOutput 14 (addrs !! 0))+                & Tx.lock+                & Tx.signWith (unsafeMkSignKey (keys !! 0))+                & Tx.serialize++        it "1 input, 25 outputs (CLI)" $ do+            state <- cardanoTx [ "empty", mainnetMagicT ] ""+                 >>= cardanoTx [ "add-input", "0", txids !! 0 ]+                 >>= flip (foldM (&)) (replicate 25 $ cardanoTx [ "add-output", "14", addrs !! 0])+                 >>= cardanoTx [ "lock" ]+                 >>= cardanoTx [ "sign-with", keys !! 0 ]++            defaul <- fromBase64E <$> cardanoTx [ "serialize" ] state+            base16 <- fromBase16E <$> cardanoTx [ "serialize", "--base16" ] state+            base64 <- fromBase64E <$> cardanoTx [ "serialize", "--base64" ] state+            compareGolden goldenMainnet__25_1 base16+            compareGolden goldenMainnet__25_1 base64+            compareGolden goldenMainnet__25_1 defaul++    describe "(Testnet) Golden Tests Transaction Construction" $ do+        it "1 input, 1 output (DSL)" $ do+            compareGolden goldenTestnet__1_1 $ Tx.empty testnetMagic+                & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                & Tx.addOutput (unsafeMkOutput 42 (addrs !! 2))+                & Tx.lock+                & Tx.signWith (unsafeMkSignKey (keys !! 0))+                & Tx.serialize++        it "1 input, 1 output (CLI)" $ do+            state <- cardanoTx [ "empty", testnetMagicT ] ""+                 >>= cardanoTx [ "add-input", "0", txids !! 0 ]+                 >>= cardanoTx [ "add-output", "42", addrs !! 2 ]+                 >>= cardanoTx [ "lock" ]+                 >>= cardanoTx [ "sign-with", keys !! 0 ]++            defaul <- fromBase64E <$> cardanoTx [ "serialize" ] state+            base16 <- fromBase16E <$> cardanoTx [ "serialize", "--base16" ] state+            base64 <- fromBase64E <$> cardanoTx [ "serialize", "--base64" ] state+            compareGolden goldenTestnet__1_1 base16+            compareGolden goldenTestnet__1_1 base64+            compareGolden goldenTestnet__1_1 defaul+++        it "2 inputs, 2 outputs (DSL)" $ do+            compareGolden goldenTestnet__2_2 $ Tx.empty testnetMagic+                & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                & Tx.addInput (unsafeMkInput 1 (txids !! 0))+                & Tx.addOutput (unsafeMkOutput 42 (addrs !! 2))+                & Tx.addOutput (unsafeMkOutput 14 (addrs !! 3))+                & Tx.lock+                & Tx.signWith (unsafeMkSignKey (keys !! 0))+                & Tx.signWith (unsafeMkSignKey (keys !! 1))+                & Tx.serialize++        it "2 inputs, 2 outputs (CLI)" $ do+            state <- cardanoTx [ "empty", testnetMagicT ] ""+                 >>= cardanoTx [ "add-input", "0", txids !! 0 ]+                 >>= cardanoTx [ "add-input", "1", txids !! 0 ]+                 >>= cardanoTx [ "add-output", "42", addrs !! 2 ]+                 >>= cardanoTx [ "add-output", "14", addrs !! 3 ]+                 >>= cardanoTx [ "lock" ]+                 >>= cardanoTx [ "sign-with", keys !! 0 ]+                 >>= cardanoTx [ "sign-with", keys !! 1 ]++            defaul <- fromBase64E <$> cardanoTx [ "serialize" ] state+            base16 <- fromBase16E <$> cardanoTx [ "serialize", "--base16" ] state+            base64 <- fromBase64E <$> cardanoTx [ "serialize", "--base64" ] state+            compareGolden goldenTestnet__2_2 base16+            compareGolden goldenTestnet__2_2 base64+            compareGolden goldenTestnet__2_2 defaul++        it "1 input, 25 outputs (DSL)" $ do+            compareGolden goldenTestnet__25_1 $ Tx.empty testnetMagic+                & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                & flip (foldr Tx.addOutput) (replicate 25 $ unsafeMkOutput 14 (addrs !! 2))+                & Tx.lock+                & Tx.signWith (unsafeMkSignKey (keys !! 0))+                & Tx.serialize+++        it "1 input, 25 outputs (CLI)" $ do+            state <- cardanoTx [ "empty", testnetMagicT ] ""+                 >>= cardanoTx [ "add-input", "0", txids !! 0 ]+                 >>= flip (foldM (&)) (replicate 25 $ cardanoTx [ "add-output", "14", addrs !! 2])+                 >>= cardanoTx [ "lock" ]+                 >>= cardanoTx [ "sign-with", keys !! 0 ]++            defaul <- fromBase64E <$> cardanoTx [ "serialize" ] state+            base16 <- fromBase16E <$> cardanoTx [ "serialize", "--base16" ] state+            base64 <- fromBase64E <$> cardanoTx [ "serialize", "--base64" ] state+            compareGolden goldenTestnet__25_1 base16+            compareGolden goldenTestnet__25_1 base64+            compareGolden goldenTestnet__25_1 defaul+++    describe "Negative tests" $ do+        prop "Invalid Input" $ \ix -> do+            let len = 32+            let genInvalidId = oneof+                    [ pure ""+                    , pure $ BS.replicate (len - 1) 0+                    , pure $ BS.replicate (len + 1) 0+                    , (BS.pack <$> arbitrary) `suchThat` ((/= len) . BS.length)+                    ]+            forAll genInvalidId (isNothing . mkInput ix)++        prop "Invalid Output" $ \(coin :: Word64) -> do+            let genInvalidAddress = BS.pack <$> arbitrary+            forAll genInvalidAddress (isNothing . mkOutput (fromIntegral coin))++        prop "Invalid SignKey" $ do+            let len = 96+            let genInvalidSignKey =+                    (BS.pack <$> arbitrary) `suchThat` ((/= len) . BS.length)+            forAll genInvalidSignKey (isNothing . mkSignKey)++        it "Missing Input" $ do+            let result = Tx.empty mainnetMagic+                       & Tx.addOutput (unsafeMkOutput 14 (addrs !! 0))+                       & Tx.lock+                       & Tx.serialize+            result `shouldBe` Left MissingInput++        it "Missing Output" $ do+            let result = Tx.empty mainnetMagic+                       & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                       & Tx.lock+                       & Tx.serialize+            result `shouldBe` Left MissingOutput++        it "Missing Signature" $ do+            let result = Tx.empty mainnetMagic+                       & Tx.addInput (unsafeMkInput 0 (txids !! 0))+                       & Tx.addOutput (unsafeMkOutput 14 (addrs !! 0))+                       & Tx.lock+                       & Tx.serialize+            result `shouldBe` Left MissingSignature++--+-- Property: Roundtrip CBOR+--++prop_RoundTripCBOR+    :: (Show a, Eq a)+    => (a -> CBOR.Encoding)+    -> (forall s. CBOR.Decoder s a)+    -> a+    -> Property+prop_RoundTripCBOR encode decode a =+    result === Right (mempty, a)+  where+    bytes  = CBOR.toStrictByteString (encode a)+    result = CBOR.deserialiseFromBytes decode (BL.fromStrict bytes)++instance Arbitrary ProtocolMagicId where+    arbitrary = mkInit <$> arbitrary++instance Arbitrary TxIn where+    arbitrary = hedgehog genTxIn++instance Arbitrary TxOut where+    arbitrary = hedgehog genTxOut++instance Arbitrary TxSigData where+    arbitrary = hedgehog genTxSigData++instance Arbitrary ErrMkPayment where+    arbitrary = elements [ MissingInput, MissingOutput, MissingSignature ]++instance {-# OVERLAPS #-}+    ( Arbitrary a, Arbitrary b, Arbitrary c)+    => Arbitrary (ProtocolMagicId, NonEmpty a, NonEmpty b, c, [TxInWitness])+  where+    arbitrary = do+        pm   <- arbitrary+        wits <- listOf (hedgehog $ genTxInWitness pm)+        (,,,,)+            <$> pure pm+            <*> fmap (NE.fromList . getNonEmpty) arbitrary+            <*> fmap (NE.fromList . getNonEmpty) arbitrary+            <*> arbitrary+            <*> pure wits++--+-- Golden+--++compareGolden+    :: Show e+    => ByteString+        -- ^ An expected encoded result+    -> Either e ByteString+        -- ^ The actual string received+    -> IO ()+compareGolden _want (Left e)   = expectationFailure (show e)+compareGolden want (Right got) = quickCheck $ withMaxSuccess 1 $+    -- NOTE Using QuickCheck here simply for getting better counter examples+    -- than HSpec in case of failure...+    conjoin (uncurry (===) <$> zip (lines prettyWant) (lines prettyGot))+        & counterexample ("Got:  " <> prettyGot)+        & counterexample ("Want: " <> prettyWant)+  where+    prettyWant =+        CBOR.prettyHexEnc $ CBOR.encodePreEncoded want+    prettyGot  =+        CBOR.prettyHexEnc $ CBOR.encodePreEncoded got++--+-- Internal+--++unsafeMkInput :: Word32 -> Text -> Input Byron+unsafeMkInput ix str = fromJust $ mkInput ix (unsafeB16 str)++unsafeMkOutput :: Natural -> Text -> Output Byron+unsafeMkOutput n str = fromJust $ mkOutput n (unsafeB58 str)++unsafeMkSignKey :: Text -> SignKey Byron+unsafeMkSignKey str = fromJust $ mkSignKey (unsafeB16 str)++unsafeB16 :: Text -> ByteString+unsafeB16 = fromMaybe (error msg) . fromBase16+  where msg = "unable to decode base16 string."++unsafeB58 :: Text -> ByteString+unsafeB58 = fromMaybe (error msg) . fromBase58+  where msg = "unable to decode base58 string."++fromBase64E :: Text -> Either String ByteString+fromBase64E = maybe (Left msg) Right . fromBase64+  where msg = "unable to decode base64 string."++fromBase16E :: Text -> Either String ByteString+fromBase16E = maybe (Left msg) Right . fromBase16+  where msg = "unable to decode base16 string."++mainnetMagicT :: Text+mainnetMagicT = T.pack $ show $ unProtocolMagicId mainnetMagic++testnetMagicT :: Text+testnetMagicT = T.pack $ show $ unProtocolMagicId testnetMagic++cardanoTx+    :: [Text]+        -- ^ Arguments+    -> Text+        -- ^ stdin+    -> IO Text+        -- ^ stdout+cardanoTx args =+    fmap T.pack . readProcess "cardano-tx" (T.unpack <$> args) . T.unpack++--+-- Test Vectors+--++txids :: [Text]+txids =+    [ "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7"+    ]++addrs :: [Text]+addrs =+    -- Mainnet+    [ "Ae2tdPwUPEZETXfbQxKMkMJQY1MoHCBS7bkw6TmhLjRvi9LZh1uDnXy319f"+    , "Ae2tdPwUPEZ69HTPqLpnFFw2MAfwdEoV5cpVQP5Uy1bPijSEHQmMXUfT3q5"++    -- Testnet+    , "2cWKMJemoBajc46Wu4Z7e6bG48myZWfB7Z6pD77L6PrJQWt9HZ3Yv7o8CYZTBMqHTPTkv"+    , "2cWKMJemoBaiLiNB8QpHKjkQhnPdQSyxaLb8JJFUQYpiVzgEJE59fN7V7StqnyDuDjHYJ"+    ]++keys :: [Text]+keys =+    [ "e0860dab46f13e74ab834142e8877b80bf22044cae8ebab7a21ed1b8dc00c155\+      \f6b78eee2a5bbd453ce7e7711b2964abb6a36837e475271f18ff36ae5fc8af73\+      \e25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf834ee1fb"++    , "00bc08703e4bb4d9fe6edf4ad2ccb1ffc6fb8d555d96805d643e5d06afde605c\+      \e7d41d056def11417b17d3fb7415392b5f329877f372e55b0959c71b2bd2b447\+      \1bbf1c6081545b1ab140578d7b5c035bf904d05dd8e9b79b34d3160f86206bfc"+    ]++goldenMainnet__1_1 :: ByteString+goldenMainnet__1_1 = unsafeB16+    "82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace3\+    \2d79336579a1939b3aad1c0b700ff9f8282d818582183581cb0e693cbc97272\+    \bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c6182affa\+    \0818200d81858858258408e620235b2a427259cc1a1c63af9ef179cb3b6e396\+    \da182e01d65dd3ed7be94be25db39fb78e74d4b53fb51776d0f5eb360e62d09\+    \b853f3a87ac25bf834ee1fb58403684501a21346ba3c19a6dc3479180e80a14\+    \e5edc0ffb4b1469abe0332e7e1caf291d50cb8d2c47ee37f6b44b7fbfcc5a1b\+    \01bc262038e93969b9a728a2b3500"++goldenMainnet__2_2 :: ByteString+goldenMainnet__2_2 = unsafeB16+    "82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32\+    \d79336579a1939b3aad1c0b7008200d81858248258203b40265111d8bb3c3c60\+    \8d95b3a0bf83461ace32d79336579a1939b3aad1c0b701ff9f8282d818582183\+    \581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a000\+    \1a497c15c6182a8282d818582183581c5d9254c4b41dbdc10f9c3b4517630497\+    \6e5e210decc38792ab2b50c9a0001ab537acfc0effa0828200d8185885825840\+    \8e620235b2a427259cc1a1c63af9ef179cb3b6e396da182e01d65dd3ed7be94b\+    \e25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf834ee1fb\+    \5840a214bef8191aca0b41c109101c420deb131f5eff63394285dc18fc39726f\+    \b24a7bc34adce5a6e234360aae398b431a47d9b9d9a71cdcea6bd21bac7ed816\+    \53068200d81858858258402cf36a269802da7cc0408308bda9a0015e7e819121\+    \17013c93edeefa9b62bbb61bbf1c6081545b1ab140578d7b5c035bf904d05dd8\+    \e9b79b34d3160f86206bfc58403cb09514f2d27f5dab76480ec010b4e89c0c50\+    \f583c00bca487e0a45643e4c2854842c57d87f93961efad7037be8de5100d0fc\+    \e2383e8a11e20cb9762731da0c"++goldenMainnet__25_1 :: ByteString+goldenMainnet__25_1 = unsafeB16+    "82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32\+    \d79336579a1939b3aad1c0b700ff9f8282d818582183581cb0e693cbc97272bd\+    \42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282d818\+    \582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a388\+    \83a0001a497c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b443e\+    \cac68b96b4fa20159186a4a38883a0001a497c15c60e8282d818582183581cb0\+    \e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c\+    \15c60e8282d818582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa\+    \20159186a4a38883a0001a497c15c60e8282d818582183581cb0e693cbc97272\+    \bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282d8\+    \18582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a3\+    \8883a0001a497c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b44\+    \3ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282d818582183581c\+    \b0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a49\+    \7c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4\+    \fa20159186a4a38883a0001a497c15c60e8282d818582183581cb0e693cbc972\+    \72bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282\+    \d818582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4\+    \a38883a0001a497c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b\+    \443ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282d81858218358\+    \1cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a\+    \497c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b443ecac68b96\+    \b4fa20159186a4a38883a0001a497c15c60e8282d818582183581cb0e693cbc9\+    \7272bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e82\+    \82d818582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186\+    \a4a38883a0001a497c15c60e8282d818582183581cb0e693cbc97272bd42aacf\+    \3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282d818582183\+    \581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a000\+    \1a497c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b443ecac68b\+    \96b4fa20159186a4a38883a0001a497c15c60e8282d818582183581cb0e693cb\+    \c97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e\+    \8282d818582183581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa201591\+    \86a4a38883a0001a497c15c60e8282d818582183581cb0e693cbc97272bd42aa\+    \cf3b443ecac68b96b4fa20159186a4a38883a0001a497c15c60e8282d8185821\+    \83581cb0e693cbc97272bd42aacf3b443ecac68b96b4fa20159186a4a38883a0\+    \001a497c15c60e8282d818582183581cb0e693cbc97272bd42aacf3b443ecac6\+    \8b96b4fa20159186a4a38883a0001a497c15c60effa0818200d8185885825840\+    \8e620235b2a427259cc1a1c63af9ef179cb3b6e396da182e01d65dd3ed7be94b\+    \e25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf834ee1fb\+    \58402b01895a50681f18418da48bf6cf9d70ad465018bb9276f6f0aa9b58ff2d\+    \2f273d584e4f27e228cd57a6f96009d5c27525fd92647db708185e488713d40d\+    \d402"++goldenTestnet__1_1 :: ByteString+goldenTestnet__1_1 = unsafeB16+    "82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32d7\+    \9336579a1939b3aad1c0b700ff9f8282d818582883581c946480eb45fa900e84d1\+    \f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab18\+    \2affa0818200d81858858258408e620235b2a427259cc1a1c63af9ef179cb3b6e3\+    \96da182e01d65dd3ed7be94be25db39fb78e74d4b53fb51776d0f5eb360e62d09b\+    \853f3a87ac25bf834ee1fb584081b55eff4aa66b01c339ba2ec04dbad9d14bd049\+    \f6e8ae46211a2870c839966bfc721ca80c3b71d3719e468dcd16c877ff68a3cff8\+    \887ac9f3546da99e6bba00"++goldenTestnet__2_2 :: ByteString+goldenTestnet__2_2 = unsafeB16+    "82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32d7\+    \9336579a1939b3aad1c0b7008200d81858248258203b40265111d8bb3c3c608d95\+    \b3a0bf83461ace32d79336579a1939b3aad1c0b701ff9f8282d818582883581c94\+    \6480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170\+    \cb17001a16bbe0ab182a8282d818582883581c48a08edee8a9f586263d5f4e2288\+    \ac1a6e33656bfa2570fb8618c5c4a102451a4170cb17001ab30d9d270effa08282\+    \00d81858858258408e620235b2a427259cc1a1c63af9ef179cb3b6e396da182e01\+    \d65dd3ed7be94be25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac\+    \25bf834ee1fb5840fb812923b3b5a15a9d7005ef0b2ef714ed188c48f0ec16a7ef\+    \5e3661f92aebb203d3717fafbb10b4de6bf4fa6ad064b5ad65434a449983741182\+    \1af434d0280f8200d81858858258402cf36a269802da7cc0408308bda9a0015e7e\+    \81912117013c93edeefa9b62bbb61bbf1c6081545b1ab140578d7b5c035bf904d0\+    \5dd8e9b79b34d3160f86206bfc5840a57a996c2a1185f117db5e21aed47c54eccf\+    \263bf1e978114b1a2731086b4e8d189700c918b1ce04cd77316a15fb22f3a2cbed\+    \7de16fb4fc83d4cc4b11a3860b"++goldenTestnet__25_1 :: ByteString+goldenTestnet__25_1 = unsafeB16+    "82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32d7\+    \9336579a1939b3aad1c0b700ff9f8282d818582883581c946480eb45fa900e84d1\+    \f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e\+    \8282d818582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3ab\+    \ec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883581c946480eb45\+    \fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a\+    \16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f25141589468ca1d38\+    \bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883581c\+    \946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a41\+    \70cb17001a16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f2514158\+    \9468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818\+    \582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0\+    \a102451a4170cb17001a16bbe0ab0e8282d818582883581c946480eb45fa900e84\+    \d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab\+    \0e8282d818582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3\+    \abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883581c946480eb\+    \45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb1700\+    \1a16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f25141589468ca1d\+    \38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d81858288358\+    \1c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a\+    \4170cb17001a16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f25141\+    \589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d8\+    \18582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1e\+    \a0a102451a4170cb17001a16bbe0ab0e8282d818582883581c946480eb45fa900e\+    \84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0\+    \ab0e8282d818582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6\+    \c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883581c946480\+    \eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17\+    \001a16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f25141589468ca\+    \1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883\+    \581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a10245\+    \1a4170cb17001a16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f251\+    \41589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282\+    \d818582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d\+    \1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883581c946480eb45fa90\+    \0e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bb\+    \e0ab0e8282d818582883581c946480eb45fa900e84d1f25141589468ca1d38bbb4\+    \e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0e8282d818582883581c9464\+    \80eb45fa900e84d1f25141589468ca1d38bbb4e6c3abec7d1ea0a102451a4170cb\+    \17001a16bbe0ab0e8282d818582883581c946480eb45fa900e84d1f25141589468\+    \ca1d38bbb4e6c3abec7d1ea0a102451a4170cb17001a16bbe0ab0effa0818200d8\+    \1858858258408e620235b2a427259cc1a1c63af9ef179cb3b6e396da182e01d65d\+    \d3ed7be94be25db39fb78e74d4b53fb51776d0f5eb360e62d09b853f3a87ac25bf\+    \834ee1fb5840e0f9591bfd7b67738db6b689d514bba1fe8c730491f9bdad7fbe05\+    \58974e57c1ef62c331da2ad528ff829b5406638d384a0b4d0b40b378f4063acb73\+    \851d3906"
+ test/Main.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}