bitcoin-hs-0.0.1: Bitcoin/BlockChain/Unspent.hs
-- | 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)
--------------------------------------------------------------------------------