module Data.Blockchain.Mining.Block
( MineBlockException(..)
, mineBlock
, mineEmptyBlock
, mineGenesisBlock
) where
import qualified Data.ByteString.Char8 as Char8
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Time.Clock as Time
import qualified Data.Word as Word
import qualified Data.Blockchain.Core.Blockchain as Blockchain
import qualified Data.Blockchain.Core.Crypto as Crypto
import qualified Data.Blockchain.Core.Types as Blockchain
data MineBlockException
= InvalidTransactionList
deriving (Eq, Show)
mineBlock
:: Crypto.PublicKey
-> [Blockchain.Transaction]
-> Blockchain.Blockchain Blockchain.Validated
-> IO (Either MineBlockException Blockchain.Block)
mineBlock pubKey txs blockchain =
case Blockchain.validateTransactions blockchain txs of
Left _e -> return (Left InvalidTransactionList)
Right () -> Right <$> mineBlockInternal pubKey reward diff1 difficulty prevBlockHeaderHash txs
where
diff1 = Blockchain.difficulty1Target config
reward = Blockchain.targetReward config (fromIntegral $ length prevBlocks + 1)
config = Blockchain.blockchainConfig blockchain
difficulty = Blockchain.targetDifficulty config $ NonEmpty.toList prevBlocks
prevBlocks = Blockchain.longestChain blockchain
prevBlock = NonEmpty.last prevBlocks
prevBlockHeaderHash = Crypto.hash (Blockchain.blockHeader prevBlock)
mineEmptyBlock
:: Crypto.PublicKey
-> Blockchain.Blockchain Blockchain.Validated
-> IO (Either MineBlockException Blockchain.Block)
mineEmptyBlock pubKey = mineBlock pubKey mempty
mineGenesisBlock :: Blockchain.BlockchainConfig -> IO Blockchain.Block
mineGenesisBlock config = do
(Crypto.KeyPair pubKey _privKey) <- Crypto.generate
mineBlockInternal pubKey reward diff1 difficulty prevBlockHeaderHash mempty
where
diff1 = Blockchain.difficulty1Target config
reward = Blockchain.initialMiningReward config
difficulty = Blockchain.initialDifficulty config
prevBlockHeaderHash = Crypto.unsafeFromByteString $ Char8.replicate 64 '0'
mineBlockInternal
:: Crypto.PublicKey -> Word.Word -> Blockchain.Hex256 -> Blockchain.Difficulty
-> Crypto.Hash Blockchain.BlockHeader -> [Blockchain.Transaction]
-> IO Blockchain.Block
mineBlockInternal pubKey reward diff1 difficulty prevBlockHeaderHash txs = do
header <- mineHeader
prevBlockHeaderHash
(Crypto.hash coinbaseTx)
(Crypto.hashTreeRoot txs)
diff1
difficulty
return (Blockchain.Block header coinbaseTx txs)
where
coinbaseTx = Blockchain.CoinbaseTransaction $ pure (Blockchain.TransactionOut reward pubKey)
mineHeader
:: Crypto.Hash Blockchain.BlockHeader
-> Crypto.Hash Blockchain.CoinbaseTransaction
-> Crypto.HashTreeRoot Blockchain.Transaction
-> Blockchain.Hex256
-> Blockchain.Difficulty
-> IO Blockchain.BlockHeader
mineHeader prevBlockHeaderHash coinbaseTransactionHash transactionHashTreeRoot diff1 difficulty = do
time <- Time.getCurrentTime
let version = 0
nonce = 0
initialHeader = Blockchain.BlockHeader{..}
mineHeaderInternal initialHeader
where
mineHeaderInternal header =
if Blockchain.blockHeaderHashDifficulty diff1 header >= difficulty
then return header
else mineHeaderInternal (incNonce header)
incNonce :: Blockchain.BlockHeader -> Blockchain.BlockHeader
incNonce header = header { Blockchain.nonce = Blockchain.nonce header + 1 }