module Blockchain.BlockSynchronizer (
handleNewBlockHashes,
handleNewBlocks
) where
import Control.Monad.IO.Class
import Control.Monad.State
import qualified Data.Binary as Bin
import qualified Data.ByteString.Lazy as BL
import Data.Function
import Data.List
import Data.Maybe
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
import Network.Simple.TCP
import Blockchain.Data.Block
import Blockchain.BlockChain
import Blockchain.Communication
import Blockchain.Context
import Blockchain.ExtDBs
import Blockchain.SHA
import Blockchain.Data.Wire
--import Debug.Trace
data GetBlockHashesResult = NeedMore SHA | NeededHashes [SHA] deriving (Show)
findFirstHashAlreadyInDB::[SHA]->ContextM (Maybe SHA)
findFirstHashAlreadyInDB hashes = do
items <- filterM (fmap (not . isNothing) . blockDBGet . BL.toStrict . Bin.encode) hashes
return $ safeHead items
where
safeHead::[a]->Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x
handleNewBlockHashes::Socket->[SHA]->ContextM ()
--handleNewBlockHashes _ list | trace ("########### handleNewBlockHashes: " ++ show list) $ False = undefined
handleNewBlockHashes _ [] = error "handleNewBlockHashes called with empty list"
handleNewBlockHashes socket blockHashes = do
result <- findFirstHashAlreadyInDB blockHashes
case result of
Nothing -> do
--liftIO $ putStrLn "Requesting more block hashes"
cxt <- get
put cxt{neededBlockHashes=reverse blockHashes ++ neededBlockHashes cxt}
sendMessage socket $ GetBlockHashes [last blockHashes] 0x500
Just hashInDB -> do
liftIO $ putStrLn $ "Found a serverblock already in our database: " ++ show (pretty hashInDB)
cxt <- get
--liftIO $ putStrLn $ show (pretty blockHashes)
put cxt{neededBlockHashes=reverse (takeWhile (/= hashInDB) blockHashes) ++ neededBlockHashes cxt}
askForSomeBlocks socket
askForSomeBlocks::Socket->ContextM ()
askForSomeBlocks socket = do
cxt <- get
if null (neededBlockHashes cxt)
then return ()
else do
let (firstBlocks, lastBlocks) = splitAt 0x20 (neededBlockHashes cxt)
put cxt{neededBlockHashes=lastBlocks}
sendMessage socket $ GetBlocks firstBlocks
handleNewBlocks::Socket->[Block]->ContextM ()
handleNewBlocks socket blocks = do
liftIO $ putStrLn "Submitting new blocks"
addBlocks $ sortBy (compare `on` number . blockData) blocks
liftIO $ putStrLn $ show (length blocks) ++ " blocks have been submitted"
askForSomeBlocks socket