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 +22/−0
- README.md +11/−0
- Setup.hs +3/−0
- bitcoin-block.cabal +65/−0
- src/Data/Bitcoin/Block.hs +35/−0
- src/Data/Bitcoin/Block/Types.hs +106/−0
- test/Data/Bitcoin/BlockSpec.hs +26/−0
- test/Main.hs +8/−0
- test/Spec.hs +1/−0
+ 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 +================== + +[](https://travis-ci.org/solatis/haskell-bitcoin-tx) +[](https://coveralls.io/r/solatis/haskell-bitcoin-tx?branch=master) +[](http://en.wikipedia.org/wiki/MIT_License) +[](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 #-}