-- | Build a table of unspent transactions outputs (the transactions are assumed to be valid!) {-# LANGUAGE CPP, BangPatterns #-} module Bitcoin.BlockChain.Unspent where -------------------------------------------------------------------------------- import Control.Monad import Data.Array import Data.Maybe import Data.Word import Data.Int import Data.Set (Set) ; import qualified Data.Set as Set import Data.Map (Map) ; import qualified Data.Map as Map import Data.IORef import Bitcoin.Protocol import Bitcoin.BlockChain.Base import Bitcoin.BlockChain.Load import Bitcoin.BlockChain.Tx import Bitcoin.BlockChain.Cache import Bitcoin.BlockChain.Chain import Bitcoin.BlockChain.TxLookup import Bitcoin.Script.Base -------------------------------------------------------------------------------- -- | An unspent transaction output data UnspentOutput = UnspentOutput { _unspentTxId :: !Hash256 -- ^ the transaction id , _unspentTxOut :: {-# UNPACK #-} !Word32 -- ^ which output of that transaction } deriving (Eq,Ord,Show) {- data Unspent , _unspentAmount :: !Amount -- ^ the amount of bitcoins unspent , _unspentAddress :: !(Maybe Address) -- ^ if it is on output to an address, we also store the address } -} -------------------------------------------------------------------------------- -- | Builds a table of unspent transactions outputs. Note that all transactions are assumed to be valid! -- So you have to check them before, to be safe. -- buildUnspentTable :: ChainTable -> TxLookup -> IO (Map UnspentOutput Amount) buildUnspentTable chTable txTable = do let txLkp :: Hash256 -> IO (Maybe (Tx RawScript RawScript)) txLkp = txLookup_ chTable txTable let (a,b) = bounds (_tableLongest chTable) unspent <- newIORef Map.empty forM_ [a..b] $ \(!blkIdx) -> do block <- loadBlockAt $! _chainLocation (_tableLongest chTable ! blkIdx) -- As far as I know, it is required that a transaction which spends another transaction -- in the same block comes later within the block, thus this is safe to do: forM_ (_blockTxs block) $ \(!tx) -> do let !txhash = _txHash tx forM_ (_txInputs tx) $ \(TxInput !prevhash !prevout _ _) -> myModifyIORef' unspent $ Map.delete (UnspentOutput prevhash prevout) forM_ (zip [0..] (_txOutputs tx)) $ \(outidx, TxOutput !amount _) -> myModifyIORef' unspent $ Map.insert (UnspentOutput txhash outidx ) (Amount $ fromIntegral amount) readIORef unspent where -- it is only present in newer versions of the base library... myModifyIORef' :: IORef a -> (a -> a) -> IO () myModifyIORef' ref f = do !old <- readIORef ref writeIORef ref $! (f old) --------------------------------------------------------------------------------