Implementation of the Bitcoin network protocol messages
Project Status: Experimental
haskoin-protocol is a component of haskoin, an ecosystem of haskell libraries implementing the various parts of the Bitcoin protocol. This library provides an implementation of the network types and messages used in the Bitcoin protocol. It also provides the binary serialization and de-serialization routines required for sending and reading the Bitcoin protocol messages from the network.
import Data.Binary (encode, decodeOrFail)
import Haskoin.Protocol
import Haskoin.Util (stringToBS)
main :: IO ()
main = do
-- Create a NetworkAddress type
let networkAddress = NetworkAddress
{ naServices = 1
, naAddress = (0xffff00, 0x000000)
, naPort = 0
}
-- Create a VarString type
userAgent = VarString $ stringToBS "/haskoin:0.0.1/"
-- Create a Version type
version = Version
{ version = 70000
, services = 1
, timestamp = 0
, addrRecv = networkAddress
, addrSend = networkAddress
, verNonce = 0
, userAgent = userAgent
, startHeight = 0
, relay = True
}
-- Create a Version message
-- Will contain a message header when serialized
versionMessage = MVersion version
-- Create a VerAck message
-- Will contain a message header when serialized
verackMessage = MVerAck
-- Serialize the version and verack messages
-- This produces a Data.ByteString.Lazy
-- Check the Data.Binary package for more details
versionMessageBS = encode versionMessage
verackMessageBS = encode verackMessage
print $ "Serialized version message: " ++ (show versionMessageBS)
print $ "Serialized verAck message: " ++ (show verackMessageBS)
-- Deserialize the version and verack messages from bytestrings
-- Check the Data.Binary package for more details
let originalVersion = case decodeOrFail versionMessageBS of
(Left (_, _, err)) -> error err
(Right (_, _, msg)) -> msg :: Message
originalVerack = case decodeOrFail verackMessageBS of
(Left (_, _, err)) -> error err
(Right (_, _, msg)) -> msg :: Message
print $ "De-serialized version message: " ++ (show originalVersion)
print $ "De-serialized verack message: " ++ (show originalVerack)
All the types and functions in this section are exported by Haskoin.Protocol
import Haskoin.Protocol
All types in this library are instances of Data.Binary
and can be serialized
and de-serialized easily. Here is an example using the VarInt
type:
import Data.Binary (encode, decodeOrFail)
import Haskoin.Protocol
main :: IO ()
main = do
-- Create a type from this library
let varint = VarInt 10
-- Serialize a bitcoin protocol type
-- See Data.Binary for more details
bs = encode varint
-- De-serialize a bitcoin protocol type
-- See Data.Binary for more details
original = case decodeOrFail bs of
(Left (_, _, err)) -> error err
(Right (_, _, res)) -> res :: VarInt
return ()
Bitcoin protocol messages are exchanged between Bitcoin nodes. The definition of these messages is given here.
data MessageCommand =
MCVersion |
MCVerAck |
MCAddr |
MCInv |
MCGetData |
MCNotFound |
MCGetBlocks |
MCGetHeaders |
MCTx |
MCBlock |
MCHeaders |
MCGetAddr |
MCPing |
MCPong |
MCAlert
data MessageHeader = MessageHeader {
headMagic :: Word32,
headCmd :: MessageCommand,
headPayloadSize :: Word32,
headChecksum :: CheckSum32
}
When serializing a Message
, the MessageHeader
is automatically computed and
added to the resulting ByteString
.
-- Algebraic data type describing a Bitcoin protocol message
data Message =
MVersion Version |
MVerAck |
MAddr Addr |
MInv Inv |
MGetData GetData |
MNotFound NotFound |
MGetBlocks GetBlocks |
MGetHeaders GetHeaders |
MTx Tx |
MBlock Block |
MHeaders Headers |
MGetAddr |
MPing Ping |
MPong Pong |
MAlert Alert
newtype VarInt = VarInt { getVarInt :: Word64 }
newtype VarString = VarString { getVarString :: BS.ByteString }
data NetworkAddress = NetworkAddress {
naServices :: Word64,
naAddress :: (Word64, Word64),
naPort :: Word16
}
data ScriptOp =
-- Pushing Data
OP_PUSHDATA BS.ByteString |
OP_FALSE |
OP_1NEGATE |
OP_TRUE |
OP_2 | OP_3 | OP_4 | OP_5 | OP_6 |
OP_7 | OP_8 | OP_9 | OP_10 | OP_11 |
OP_12 | OP_13 | OP_14 | OP_15 | OP_16 |
-- Flow control
OP_VERIFY |
-- Stack operations
OP_DUP |
-- Bitwise logic
OP_EQUAL |
OP_EQUALVERIFY |
-- Crypto
OP_HASH160 |
OP_CHECKSIG |
OP_CHECKMULTISIG |
-- Other
OP_PUBKEY PubKey |
OP_INVALIDOPCODE Word8
newtype Script = Script { runScript :: [ScriptOp] }
data Tx = Tx {
txVersion :: Word32,
txIn :: [TxIn],
txOut :: [TxOut],
txLockTime :: Word32
}
data CoinbaseTx = CoinbaseTx {
cbVersion :: Word32,
cbData :: BS.ByteString,
cbOut :: [TxOut],
cbLockTime :: Word32
}
data TxIn = TxIn {
prevOutput :: OutPoint,
sigScript :: Script,
txInSequence :: Word32
}
data TxOut = TxOut {
outValue :: Word64,
scriptPubKey :: Script
}
data OutPoint = OutPoint {
outPointHash :: Hash256,
outPointIndex :: Word32
}
data BlockHeader = BlockHeader {
blockVersion :: Word32,
prevBlock :: Hash256,
merkleRoot :: Hash256,
blockTimestamp :: Word32,
blockBits :: Word32,
bhNonce :: Word32
}
data Block = Block {
blockHeader :: BlockHeader,
blockCoinbaseTx :: CoinbaseTx,
blockTxns :: [Tx]
}
type BlockLocator = [Hash256]
data GetBlocks = GetBlocks {
getBlocksVersion :: Word32,
getBlocksLocator :: BlockLocator,
getBlocksHashStop :: Hash256
}
data GetHeaders = GetHeaders {
getHeadersVersion :: Word32,
getHeadersBL :: BlockLocator,
getHeadersHashStop :: Hash256
}
data InvType = InvError | InvTx | InvBlock
data InvVector = InvVector {
invType :: InvType,
invHash :: Hash256
}
data GetData = GetData {
getDataList :: [InvVector]
}
data Inv = Inv {
invList :: [InvVector]
}
type BlockHeaderCount = (BlockHeader, VarInt)
data Headers = Headers {
headersList :: [BlockHeaderCount]
}
data Version = Version {
version :: Word32,
services :: Word64,
timestamp :: Word64,
addrRecv :: NetworkAddress,
addrSend :: NetworkAddress,
verNonce :: Word64,
userAgent :: VarString,
startHeight :: Word32,
relay :: Bool
}
type NetworkAddressTime = (Word32, NetworkAddress)
data Addr = Addr {
addrList :: [NetworkAddressTime]
}
newtype Ping = Ping { pingNonce :: Word64 }
newtype Pong = Pong { pongNonce :: Word64 }
data NotFound = NotFound {
notFoundList :: [InvVector]
}
data Alert = Alert {
alertPayload :: VarString,
alertSignature :: VarString
}
- Cabal package manager
# in Ubuntu
apt-get install cabal-install
- haskoin-util
# haskoin-util is not on Hackage (yet)
git clone https://github.com/plaprade/haskoin-util.git
cd haskoin-util
cabal install
- haskoin-crypto
# haskoin-crypto is not on Hackage (yet)
git clone https://github.com/plaprade/haskoin-crypto.git
cd haskoin-crypto
cabal install
# haskoin-protocol is not on Hackage (yet)
git clone https://github.com/plaprade/haskoin-protocol.git
cd haskoin-protocol
cabal install
If you are missing the test dependencies:
cabal install --enable-tests
cabal test
If you have the test dependencies, you can build without installing:
cabal configure --enable-tests
cabal build
cabal test
The tests can take a few minutes to run.
Please report any bugs in the projects bug tracker:
github.com/plaprade/haskoin-protocol/issues
We're glad you want to contribute! It's simple:
- Fork haskoin-protocol
- Create a branch
git checkout -b my_branch
- Commit your changes
git commit -am 'comments'
- Push the branch
git push origin my_branch
- Open a pull request
Code guidelines:
- 80 columns.
- 4 space indentation. No tabs.
- Follow the general style of the code, whenever it makes sense.
You can support the project by donating in Bitcoins to:
176CwMCWMq1y9CxFZWk7Vfoka5PoaNzxRq