packages feed

bitcoin-tx (empty) → 0.9.0

raw patch · 9 files changed

+343/−0 lines, 9 filesdep +basedep +base16-bytestringdep +binarysetup-changed

Dependencies added: base, base16-bytestring, binary, bitcoin-script, bitcoin-tx, bytestring, hspec

Files

+ LICENSE view
@@ -0,0 +1,22 @@+The MIT License (MIT)
+
+Copyright (c) 2015 Leon Mergen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+ README.md view
@@ -0,0 +1,11 @@+haskell-bitcoin-tx
+==================
+
+[![Build Status](https://travis-ci.org/solatis/haskell-bitcoin-tx.png?branch=master)](https://travis-ci.org/solatis/haskell-bitcoin-tx)
+[![Coverage Status](https://coveralls.io/repos/solatis/haskell-bitcoin-tx/badge.svg?branch=master)](https://coveralls.io/r/solatis/haskell-bitcoin-tx?branch=master)
+[![MIT](http://b.repl.ca/v1/license-MIT-blue.png)](http://en.wikipedia.org/wiki/MIT_License)
+[![Haskell](http://b.repl.ca/v1/language-haskell-lightgrey.png)](http://haskell.org)
+
+This library provides the same functionality as the bitcoin-tx command line
+utility, which was introduced in Bitcoin Core v0.10. These functions are
+pure and require no communication with a bitcoin daemon.
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple
+
+main = defaultMain
+ bitcoin-tx.cabal view
@@ -0,0 +1,60 @@+name: bitcoin-tx
+category: Network, Finance
+version: 0.9.0
+license: MIT
+license-file: LICENSE
+copyright: (c) 2015 Leon Mergen
+author: Leon Mergen
+maintainer: leon@solatis.com
+homepage: http://www.leonmergen.com/opensource.html
+bug-reports: http://github.com/solatis/haskell-bitcoin-tx/issues
+stability: experimental
+synopsis: Utility functions for manipulating bitcoin transactions
+description:
+    This library provides the same functionality as the bitcoin-tx command line
+    utility, which was introduced in Bitcoin Core v0.10. These functions are
+    pure and require no communication with a bitcoin daemon.
+            
+build-type: Simple
+data-files: LICENSE, README.md
+cabal-version: >= 1.10
+tested-with: GHC == 7.6, GHC == 7.8, GHC == 7.10
+
+library
+  hs-source-dirs:      src
+  ghc-options:         -Wall -ferror-spans
+  default-language:    Haskell2010
+
+  exposed-modules:     Bitcoin.Transaction
+                       Bitcoin.Transaction.Types
+
+  build-depends:       base                     >= 4.3          && < 5
+                     , bytestring
+                     , base16-bytestring
+                     , binary
+
+                     , bitcoin-script
+
+test-suite test-suite
+  type:                exitcode-stdio-1.0
+  ghc-options:         -Wall -ferror-spans -threaded -auto-all -caf-all -fno-warn-type-defaults
+  default-language:    Haskell2010
+  hs-source-dirs:      test
+  main-is:             Main.hs
+
+  other-modules:       Bitcoin.TransactionSpec
+                       Spec
+                       Main
+
+  build-depends:       base                     >= 4.3          && < 5
+                     , hspec
+
+                     , bytestring
+
+                     , bitcoin-tx
+                     , bitcoin-script
+
+source-repository head
+  type: git
+  location: git://github.com/solatis/haskell-bitcoin-tx.git
+  branch: master
+ src/Bitcoin/Transaction.hs view
@@ -0,0 +1,19 @@+module Bitcoin.Transaction where
+
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.ByteString.Base16.Lazy as BS16L
+
+import qualified Data.Binary     as B ( decode
+                                      , encode )
+
+import Bitcoin.Transaction.Types
+
+-- | Decodes a hex representation of a transaction into a 'Transaction' object.
+decode :: BSL.ByteString -> Transaction
+decode =
+  B.decode . fst . BS16L.decode
+
+-- | Encodes a 'Transaction' object into a hex representation.
+encode :: Transaction -> BSL.ByteString
+encode =
+  BS16L.encode . B.encode
+ src/Bitcoin/Transaction/Types.hs view
@@ -0,0 +1,196 @@+{-# LANGUAGE FlexibleInstances  #-}
+
+module Bitcoin.Transaction.Types where
+
+import Control.Applicative ((<$>),(<*>))
+import Control.Monad (liftM2, replicateM, forM_)
+
+import Data.Word ( Word32
+                 , Word64 )
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+
+import Data.Bits (shiftL, shiftR)
+import Data.Binary ( Binary, get, put, encode, decode )
+
+import Data.Binary.Get ( getByteString
+                       , getWord8
+                       , getWord16le
+                       , getWord32le
+                       , getWord64le
+                       , getWord64be )
+
+import Data.Binary.Put ( putByteString
+                       , putWord8
+                       , putWord16le
+                       , putWord32le
+                       , putWord64le
+                       , putWord64be )
+
+import qualified Bitcoin.Script as Btc ( Script (..) )
+
+-- | Data type representing a variable length integer. The 'VarInt' type
+-- usually precedes an array or a string that can vary in length.
+newtype VarInt = VarInt { getVarInt :: Word64 }
+    deriving (Eq, Show, Read)
+
+instance Binary VarInt where
+
+    get = VarInt <$> ( getWord8 >>= go )
+      where
+        go 0xff = getWord64le
+        go 0xfe = fromIntegral <$> getWord32le
+        go 0xfd = fromIntegral <$> getWord16le
+        go x    = fromIntegral <$> return x
+
+    put (VarInt x)
+        | x < 0xfd =
+            putWord8 $ fromIntegral x
+        | x <= 0xffff = do
+            putWord8 0xfd
+            putWord16le $ fromIntegral x
+        | x <= 0xffffffff = do
+            putWord8 0xfe
+            putWord32le $ fromIntegral x
+        | otherwise = do
+            putWord8 0xff
+            putWord64le x
+
+data TxnOutputType = TxnPubKey     -- ^ JSON of "pubkey" received.
+                   | TxnPubKeyHash -- ^ JSON of "pubkeyhash" received.
+                   | TxnScriptHash -- ^ JSON of "scripthash" received.
+                   | TxnMultisig   -- ^ JSON of "multisig" received.
+    deriving ( Show, Read, Ord, Eq )
+
+
+data TransactionHash = TransactionHash Integer
+    deriving ( Show, Read, Eq )
+
+instance Binary TransactionHash where
+    get = do
+      a <- fromIntegral <$> getWord64be
+      b <- fromIntegral <$> getWord64be
+      c <- fromIntegral <$> getWord64be
+      d <- fromIntegral <$> getWord64be
+
+      return $ TransactionHash ((a `shiftL` 192) + (b `shiftL` 128) + (c `shiftL` 64) + d)
+
+    put (TransactionHash i) = do
+      putWord64be $ fromIntegral (i `shiftR` 192)
+      putWord64be $ fromIntegral (i `shiftR` 128)
+      putWord64be $ fromIntegral (i `shiftR` 64)
+      putWord64be $ fromIntegral i
+
+-- | The OutPoint is used inside a transaction input to reference the previous
+-- transaction output that it is spending.
+data OutPoint = OutPoint {
+  -- | The hash of the referenced transaction.
+  outPointHash  :: TransactionHash,
+
+  -- | The position of the specific output in the transaction.
+  -- The first output position is 0.
+  outPointIndex :: !Word32
+  } deriving (Read, Show, Eq)
+
+instance Binary OutPoint where
+  get = do
+    (h,i) <- liftM2 (,) get getWord32le
+    return $ OutPoint h i
+
+  put (OutPoint h i) = put h >> putWord32le i
+
+-- | Data type representing a transaction input.
+data TransactionIn =  TransactionIn {
+  -- | Reference the previous transaction output (hash + position)
+  prevOutput   :: OutPoint,
+
+  -- | Script providing the requirements of the previous transaction
+  -- output to spend those coins.
+  scriptInput  :: Btc.Script,
+
+  -- | Transaction version as defined by the sender of the
+  -- transaction. The intended use is for replacing transactions with
+  -- new information before the transaction is included in a block.
+  txInSequence :: Word32
+
+  } deriving (Eq, Show, Read)
+
+instance Binary TransactionIn where
+    get = do
+      o <- get
+      (VarInt len) <- get
+      scriptBs <- getByteString (fromIntegral len)
+      s <- getWord32le
+
+      let i = decode $ BSL.fromStrict scriptBs
+
+      return $ TransactionIn o i s
+
+    put (TransactionIn o i s) = do
+      let scriptBs = BSL.toStrict $ encode i
+
+      put o
+      put $ VarInt $ fromIntegral $ BS.length scriptBs
+      putByteString scriptBs
+      putWord32le s
+
+
+-- | Data type representing a transaction output.
+data TransactionOut = TransactionOut {
+  -- | Transaction output value.
+  outValue     :: Word64,
+
+  -- | Script specifying the conditions to spend this output.
+  scriptOutput :: Btc.Script
+
+  } deriving (Eq, Show, Read)
+
+instance Binary TransactionOut where
+    get = do
+        val <- getWord64le
+        (VarInt len) <- get
+
+        scriptBs <- getByteString (fromIntegral len)
+        let s = decode $ BSL.fromStrict scriptBs
+
+        return $ TransactionOut val s
+
+    put (TransactionOut o s) = do
+      let scriptBs = BSL.toStrict $ encode s
+
+      putWord64le o
+      put $ VarInt $ fromIntegral $ BS.length scriptBs
+      putByteString scriptBs
+
+-- | Data type representing a bitcoin transaction
+data Transaction = Transaction {
+  -- | Transaction data format version
+  txVersion  :: Word32,
+
+  -- | List of transaction inputs
+  txIn       :: [TransactionIn],
+
+  -- | List of transaction outputs
+  txOut      :: [TransactionOut],
+
+  -- | The block number of timestamp at which this transaction is locked
+  txLockTime :: Word32
+
+  } deriving (Eq, Show, Read)
+
+instance Binary Transaction where
+    get = Transaction <$> getWord32le
+                      <*> (replicateList =<< get)
+                      <*> (replicateList =<< get)
+                      <*> getWord32le
+      where
+        replicateList (VarInt c) = replicateM (fromIntegral c) get
+
+    put (Transaction v is os l) = do
+        putWord32le v
+        put $ VarInt $ fromIntegral $ length is
+        forM_ is put
+        put $ VarInt $ fromIntegral $ length os
+        forM_ os put
+        putWord32le l
+ test/Bitcoin/TransactionSpec.hs view
@@ -0,0 +1,23 @@+module Bitcoin.TransactionSpec where
+
+import           Bitcoin.Script (Script (..))
+import           Bitcoin.Transaction        (decode, encode)
+import           Bitcoin.Transaction.Types
+import qualified Data.ByteString.Lazy.Char8 as BSL8 (pack)
+
+import           Test.Hspec
+
+spec :: Spec
+spec = do
+  describe "when parsing a specific transaction" $ do
+    let hex = BSL8.pack "0100000002f327e86da3e66bd20e1129b1fb36d07056f0b9a117199e759396526b8f3a20780000000049483045022100fce442ec52aa2792efc27fd3ad0eaf7fa69f097fdcefab017ea56d1799b10b2102207a6ae3eb61e11ffaba0453f173d1792f1b7bb8e7422ea945101d68535c4b474801fffffffff0ede03d75050f20801d50358829ae02c058e8677d2cc74df51f738285013c26000000006b483045022100b77f935ff366a6f3c2fdeb83589c790265d43b3d2cf5e5f0047da56c36de75f40220707ceda75d8dcf2ccaebc506f7293c3dcb910554560763d7659fb202f8ec324b012102240d7d3c7aad57b68aa0178f4c56f997d1bfab2ded3c2f9427686017c603a6d6ffffffff02f028d6dc010000001976a914ffb035781c3c69e076d48b60c3d38592e7ce06a788ac00ca9a3b000000001976a914fa5139067622fd7e1e722a05c17c2bb7d5fd6df088ac00000000"
+
+    it "encoding a decoding a transaction results in the original hex" $
+      (encode . decode) hex `shouldBe` hex
+
+    it "succesfully parses a transaction into a meaningful object" $ do
+      let decoded = decode hex
+
+      case decoded of
+       (Transaction 1 [(TransactionIn _ _ 4294967295), (TransactionIn _ _ 4294967295)] [(TransactionOut 7999990000 (Script _)), (TransactionOut 1000000000 (Script _))] 0) -> return ()
+       _                               -> expectationFailure ("Result does not match expected: " ++ show decoded)
+ test/Main.hs view
@@ -0,0 +1,8 @@+module Main where
+
+import Test.Hspec.Runner
+import qualified Spec
+
+main :: IO ()
+main =
+  hspecWith defaultConfig Spec.spec
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}