haskoin-node-0.7.0: test/Haskoin/NodeSpec.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Haskoin.NodeSpec
( spec
) where
import Control.Monad
import Control.Monad.Logger
import Control.Monad.Trans
import qualified Database.RocksDB as R
import Haskoin
import Haskoin.Node
import Network.Socket (SockAddr (..))
import NQE
import Test.Hspec
import UnliftIO
data TestNode = TestNode
{ testMgr :: Manager
, testChain :: Chain
, nodeEvents :: Inbox NodeEvent
}
spec :: Spec
spec = do
let net = btcTest
describe "peer manager on test network" $ do
it "connects to a peer" $
withTestNode net "connect-one-peer" $ \TestNode {..} -> do
p <- waitForPeer nodeEvents
Just OnlinePeer {onlinePeerVersion = Just Version {version = ver}} <-
managerGetPeer p testMgr
ver `shouldSatisfy` (>= 70002)
it "downloads some blocks" $
withTestNode net "get-blocks" $ \TestNode {..} -> do
let h1 =
"000000000babf10e26f6cba54d9c282983f1d1ce7061f7e875b58f8ca47db932"
h2 =
"00000000851f278a8b2c466717184aae859af5b83c6f850666afbc349cf61577"
p <- waitForPeer nodeEvents
Just [b1, b2] <- peerGetBlocks net 30 p [h1, h2]
headerHash (blockHeader b1) `shouldBe` h1
headerHash (blockHeader b2) `shouldBe` h2
let testMerkle b =
merkleRoot (blockHeader b) `shouldBe`
buildMerkleRoot (map txHash (blockTxns b))
testMerkle b1
testMerkle b2
it "connects to two peers" $
withTestNode net "connect-peers" $ \TestNode {..} ->
replicateM_ 2 (waitForPeer nodeEvents) `shouldReturn` ()
it "attempts to get inexistent things" $
withTestNode net "download-fail" $ \TestNode {..} -> do
let hs =
[ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
]
p <- waitForPeer nodeEvents
peerGetTxs net 30 p hs `shouldReturn` Nothing
describe "chain on test network" $ do
it "syncs some headers" $
withTestNode net "connect-sync" $ \TestNode {..} -> do
let h =
"000000009ec921df4bb16aedd11567e27ede3c0b63835b257475d64a059f102b"
hs =
[ headerHash (getGenesisHeader net)
, "0000000005bdbddb59a3cd33b69db94fa67669c41d9d32751512b5d7b68c71cf"
, "00000000185b36fa6e406626a722793bea80531515e0b2a99ff05b73738901f1"
, "000000001ab69b12b73ccdf46c9fbb4489e144b54f1565e42e481c8405077bdd"
]
bns <-
replicateM 4 . receiveMatch nodeEvents $ \case
ChainEvent (ChainBestBlock bn) -> Just bn
_ -> Nothing
bb <- chainGetBest testChain
nodeHeight bb `shouldSatisfy` (>= 6000)
an <-
maybe (throwString "No ancestor found") return =<<
chainGetAncestor 2357 (last bns) testChain
map (headerHash . nodeHeader) bns `shouldBe` hs
headerHash (nodeHeader an) `shouldBe` h
it "downloads some block parents" $
withTestNode net "parents" $ \TestNode {..} -> do
let hs =
[ "00000000c74a24e1b1f2c04923c514ed88fc785cf68f52ed0ccffd3c6fe3fbd9"
, "000000007e5c5f40e495186ac4122f2e4ee25788cc36984a5760c55ecb376cb1"
, "00000000a6299059b2bff3479bc569019792e75f3c0f39b10a0bc85eac1b1615"
]
[_, bn] <-
replicateM 2 $
receiveMatch nodeEvents $ \case
ChainEvent (ChainBestBlock bn) -> Just bn
_ -> Nothing
nodeHeight bn `shouldBe` 2000
ps <- chainGetParents 1997 bn testChain
length ps `shouldBe` 3
forM_ (zip ps hs) $ \(p, h) ->
headerHash (nodeHeader p) `shouldBe` h
waitForPeer :: MonadIO m => Inbox NodeEvent -> m Peer
waitForPeer inbox =
receiveMatch inbox $ \case
PeerEvent (PeerConnected p _) -> Just p
_ -> Nothing
withTestNode ::
MonadUnliftIO m
=> Network
-> String
-> (TestNode -> m a)
-> m a
withTestNode net str f =
runNoLoggingT $
withSystemTempDirectory ("haskoin-node-test-" <> str <> "-") $ \w ->
withPublisher $ \pub ->
withSubscription pub $ \node_inbox -> do
db <-
R.open
w
R.defaultOptions
{ R.createIfMissing = True
, R.compression = R.SnappyCompression
}
let cfg =
NodeConfig
{ nodeConfMaxPeers = 20
, nodeConfDB = db
, nodeConfPeers = []
, nodeConfDiscover = True
, nodeConfNetAddr =
NetworkAddress 0 (SockAddrInet 0 0)
, nodeConfNet = net
, nodeConfEvents = pub
, nodeConfTimeout = 10
}
withNode cfg $ \(mgr, ch) ->
lift $
f
TestNode
{ testMgr = mgr
, testChain = ch
, nodeEvents = node_inbox
}