bitcoin-hs-0.0.1: Bitcoin/Script/Integer.hs
-- | Numbers in Bitcoin scripts
{-# LANGUAGE PatternGuards #-}
module Bitcoin.Script.Integer where
--------------------------------------------------------------------------------
import Data.Int
import Data.Word
import Data.Bits
import Data.List ( unfoldr , splitAt , mapAccumL )
import Data.Maybe
import Control.Monad
import Control.Applicative
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Bitcoin.Misc.BigInt
import Bitcoin.Misc.OctetStream
--------------------------------------------------------------------------------
-- * signs
-- | Positive actually means non-negative here, but it looks better (and is easier to read) this way
data Sign = Positive | Negative
signOf :: Integer -> Sign
signOf n = if n<0 then Negative else Positive
toSignAbs :: Integer -> (Sign,Integer)
toSignAbs n = (signOf n, abs n)
fromSignAbs :: (Sign,Integer) -> Integer
fromSignAbs (sign,absn) = case sign of
Positive -> absn
Negative -> negate absn
--------------------------------------------------------------------------------
-- * Bitcoin's special ByteString <-> Integer conversion
-- | Byte vectors are interpreted as little-endian variable-length integers
-- with the most significant bit determining the sign of the integer. Thus
-- 0x81 represents -1. 0x80 is another representation of zero (so called
-- negative 0). Byte vectors are interpreted as Booleans where False is
-- represented by any representation of zero, and True is represented by
-- any representation of non-zero.
asInteger :: B.ByteString -> Integer
asInteger bs =
case sign of
Positive -> absn
Negative -> negate absn
where
absn = littleEndianRollInteger ws
(sign,ws) = decodeSign (B.unpack bs)
asByteString :: Integer -> B.ByteString
asByteString 0 = B.empty
asByteString n = B.pack $ encodeSign (signOf n) $ littleEndianUnrollInteger (abs n)
encodeSign :: Sign -> [Word8] -> [Word8]
encodeSign sign = go where
go ws = case ws of
(x:rest@(y:_)) -> x : go rest
[last] -> if last < 0x80
then case sign of { Positive -> [last] ; Negative -> [last + 0x80] }
else last : go []
[] -> [ case sign of { Positive -> 0 ; Negative -> 0x80 } ]
decodeSign :: [Word8] -> (Sign,[Word8])
decodeSign = go where
go ws = case ws of
(x:rest@(y:_)) -> let (sign,xs) = go rest in (sign,x:xs)
[last] -> if last < 0x80
then (Positive, [last ] )
else (Negative, [last-0x80] )
[] -> ( Positive , [] )
--------------------------------------------------------------------------------