packages feed

bitcoin-block (empty) → 0.9.0

raw patch · 9 files changed

+277/−0 lines, 9 filesdep +basedep +binarydep +bitcoin-blocksetup-changed

Dependencies added: base, binary, bitcoin-block, bitcoin-tx, bitcoin-types, bytestring, cryptohash, hexstring, hspec, largeword

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-block.cabal view
@@ -0,0 +1,65 @@+name: bitcoin-block
+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-block/issues
+stability: experimental
+synopsis: Utility functions for manipulating bitcoin blocks
+description:
+            This library provides functionality for parsing, inspecting,
+            hashing and serialization of bitcoin blocks. 
+            
+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:     Data.Bitcoin.Block
+  
+  other-modules:       Data.Bitcoin.Block.Types
+
+  build-depends:       base                     >= 4.3          && < 5
+                     , bytestring
+                     , binary
+                     , hexstring
+                     , cryptohash
+                     , largeword
+                     
+                     , bitcoin-tx
+                     , bitcoin-types
+
+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:       Data.Bitcoin.BlockSpec
+                       Spec
+                       Main
+
+  build-depends:       base                     >= 4.3          && < 5
+                     , hspec
+
+                     , bytestring
+                     , hexstring
+
+                     , bitcoin-tx
+                     , bitcoin-types
+                     , bitcoin-block                     
+
+source-repository head
+  type: git
+  location: git://github.com/solatis/haskell-bitcoin-block.git
+  branch: master
+ src/Data/Bitcoin/Block.hs view
@@ -0,0 +1,35 @@+module Data.Bitcoin.Block ( decode
+                          , encode
+                          , headerHash
+                          , Block (..)
+                          , BlockHeader (..) ) where
+
+import qualified Data.Binary                    as B (encode)
+
+import qualified Data.ByteString                as BS (reverse)
+import qualified Data.ByteString.Lazy           as BSL (toStrict)
+
+import qualified Crypto.Hash.SHA256             as Sha256
+import qualified Data.HexString                 as HS
+
+import           Data.Bitcoin.Block.Types
+
+-- | Decodes a hex representation of a transaction into a 'Block' object.
+decode :: HS.HexString -- ^ The hexadecimal representation of the transaction
+       -> Block        -- ^ The decoded 'Transaction' object
+decode = HS.toBinary
+
+-- | Encodes a 'Block' object into a hex representation.
+encode :: Block        -- ^ The 'Transaction' we would like to encode to hex
+       -> HS.HexString -- ^ The hexadecimal representation of the transaction
+encode = HS.fromBinary
+
+-- | Calculates the transaction id of a 'Transaction' as a 'HS.HexString' so it
+--   can be used in RPC interfaces.
+headerHash :: Block -> HS.HexString
+headerHash block =
+      -- Bitcoin uses a "double sha256", also known as sha256d as its hash algo
+  let sha256d = Sha256.hash . Sha256.hash
+      bytes   = BSL.toStrict . B.encode
+
+  in HS.fromBytes . BS.reverse . sha256d . bytes $ blockHeader block
+ src/Data/Bitcoin/Block/Types.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE FlexibleInstances  #-}
+
+module Data.Bitcoin.Block.Types where
+
+import Data.Binary ( Binary, get, put )
+import Data.Binary.Get ( getByteString
+                       , getWord32le )
+
+import Data.Binary.Put ( putByteString
+                       , putWord32le )
+
+import Control.Monad ( forM_
+                     , replicateM )
+
+import qualified Data.HexString as HS
+import qualified Data.ByteString as BS
+
+import Data.Word ( Word32 )
+import Data.LargeWord ( Word256 )
+
+import Data.Bitcoin.Types ( VarInt (..)
+                          , BlockHash )
+import qualified Data.Bitcoin.Transaction as Btc ( Transaction (..)
+                                                 , Coinbase )
+
+
+-- | Data type describing a block in the bitcoin protocol.
+data Block = Block {
+  -- | Header information for this block.
+  blockHeader     :: BlockHeader,
+
+  -- | Coinbase transaction of this block.
+  blockCoinbaseTx :: Btc.Coinbase,
+
+  -- | List of transactions pertaining to this block.
+  blockTxns       :: [Btc.Transaction]
+
+  } deriving (Eq, Show)
+
+instance Binary Block where
+
+    get = do
+        header     <- get
+        (VarInt c) <- get
+        cb         <- get
+        txs        <- replicateM (fromIntegral (c-1)) get
+        return $ Block header cb txs
+
+    put (Block h cb txs) = do
+        put h
+        put $ VarInt $ fromIntegral $ length txs + 1
+        put cb
+        forM_ txs put
+
+-- | Data type recording information on a 'Block'. The hash of a block is
+--   defined as the hash of this data structure. The block mining process
+--   involves finding a partial hash collision by varying the nonce in the
+--   'BlockHeader' and/or additional randomness in the 'Btc.Coinbase' of this
+--   'Block'. Variations in the 'Btc.Coinbase' will result in different merkle
+--   roots in the 'BlockHeader'.
+
+data BlockHeader = BlockHeader {
+
+  -- | Block version information, based on the version of the
+  --   software creating this block.
+  blockVersion   :: Word32,
+
+  -- | Hash of the previous block (parent) referenced by this
+  --   block.
+  prevBlock      :: BlockHash,
+
+  -- | Root of the merkle tree of all transactions pertaining
+  --   to this block.
+  merkleRoot     :: Word256,
+
+  -- | Unix timestamp recording when this block was created
+  blockTimestamp :: Word32,
+
+  -- | The difficulty target being used for this block
+  blockBits      :: Word32,
+
+  -- | A random nonce used to generate this block. Additional
+  --   randomness is included in the coinbase transaction of
+  --   this block.
+  bhNonce        :: Word32
+
+  } deriving (Eq, Show)
+
+instance Binary BlockHeader where
+
+    get = do
+      v  <- getWord32le
+      p  <- getByteString 32 -- A BlockHash is exactly 32 bytes
+      m  <- get
+      bt <- getWord32le
+      bb <- getWord32le
+      n  <- getWord32le
+      return $ BlockHeader v ((HS.fromBytes . BS.reverse) p) m bt bb n
+
+    put (BlockHeader v p m bt bb n) = do
+      putWord32le   v
+      putByteString ((BS.reverse . HS.toBytes) p)
+      put           m
+      putWord32le   bt
+      putWord32le   bb
+      putWord32le   n
+ test/Data/Bitcoin/BlockSpec.hs view
@@ -0,0 +1,26 @@+module Data.Bitcoin.BlockSpec where
+
+import qualified Data.HexString as HS
+
+import           Data.Bitcoin.Block
+import qualified Data.ByteString.Char8          as BS8 (pack)
+
+import           Test.Hspec
+
+spec :: Spec
+spec = do
+  describe "when parsing a specific block" $ do
+    let hex = HS.hexString $ BS8.pack "03000000f4c7237cbb549c2354cc62c9a668af38f6fefd40ad47f1e4c74ad582ae9b1f58f928b445a7129277f55c69cbe27dc0f624f652c4d2f31f1840e2bae2fdcb4489d30a3e55ffff7f20020000000201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0b01690101062f503253482fffffffff011019062a01000000232102ef5396e5e464f62fbe1a865b8f8469b2d43036dbbf781e5318dfc04bbdcae8fbac0000000001000000038f3021ca346d0a2404c33b6ea3aa5a0dd0acd21507c411a92467f919851a7b1e0000000049483045022100ac8b2c8645b7ad344a94ed1a4b8b5cd32fec2c27d75719852b6459f25b939a9b022056e7d49efe036fb1fc93773587972cbc8e8bf06463f6d170ce128226b025228c01ffffffff8eec8c01a8fe1bb3a265363d4a3863326f79d98d0fb05def21e0f13c4cb70853000000006b483045022100d9124cd8ec46bbda359e41fa92c9a5ad142ce42239e1ac12cc22ae191609ec7702205974c9c9ff7578e86928693c65fd817fcfbcd6bfcce2424695b9fff5f2c810ed012102c36b6125cfbb9e50bf5c390f948640765e3b0fb980c313c9307692d2d2b8b62dffffffff57f74f9754e5c9814e928cd52bfffd74e7a29a2b35bf0444ddbe69f3de9268dc0000000049483045022100f539d6202f1025b1abbf38343d9e6a2570db19684b50351ba5e87f534419721302207b45ef08f07095c19012969e1335e12c18b1efff3e0deb83b1010c110c49c2bb01ffffffff01089d17a8040000001976a914c09c37f829a24e0c61f809f1c3c73f78f71aa2ae88ac00000000"
+
+    it "encoding a decoding a block results in the original hex" $
+      (encode . decode) hex `shouldBe` hex
+
+    it "succesfully parses a block into a meaningful object" $ do
+      let decoded = decode hex
+
+      case decoded of
+       (Block (BlockHeader 3 _ _ 1430129363 _ 2) _ _) -> return ()
+       _                                              -> expectationFailure ("Result does not match expected: " ++ show decoded)
+
+    it "succesfully generates hash of parsed object" $ do
+      headerHash (decode hex) `shouldBe` (HS.hexString $ BS8.pack "269c6a72b865805c74260c9d99b74cf2f20d9fccd6c3749e607427102dd1edda")
+ 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 #-}