bitcoin-payment-channel 0.2.1.0 → 0.2.2.0
raw patch · 11 files changed
+94/−52 lines, 11 files
Files
- bitcoin-payment-channel.cabal +3/−1
- src/Data/Bitcoin/PaymentChannel/Internal/BitcoinAmount.hs +60/−0
- src/Data/Bitcoin/PaymentChannel/Internal/Error.hs +1/−1
- src/Data/Bitcoin/PaymentChannel/Internal/Payment.hs +1/−1
- src/Data/Bitcoin/PaymentChannel/Internal/Refund.hs +5/−3
- src/Data/Bitcoin/PaymentChannel/Internal/Serialization.hs +5/−5
- src/Data/Bitcoin/PaymentChannel/Internal/Settlement.hs +1/−1
- src/Data/Bitcoin/PaymentChannel/Internal/State.hs +2/−2
- src/Data/Bitcoin/PaymentChannel/Internal/Types.hs +10/−6
- src/Data/Bitcoin/PaymentChannel/Internal/Util.hs +2/−28
- src/Data/Bitcoin/PaymentChannel/Types.hs +4/−4
bitcoin-payment-channel.cabal view
@@ -1,5 +1,5 @@ name: bitcoin-payment-channel-version: 0.2.1.0+version: 0.2.2.0 synopsis: Library for working with Bitcoin payment channels description: A Bitcoin payment channel allows secure and instant transfer of bitcoins from one@@ -37,6 +37,7 @@ other-modules: Data.Bitcoin.PaymentChannel.Internal.Error+ Data.Bitcoin.PaymentChannel.Internal.BitcoinAmount, Data.Bitcoin.PaymentChannel.Internal.Payment, Data.Bitcoin.PaymentChannel.Internal.Refund, Data.Bitcoin.PaymentChannel.Internal.Script,@@ -46,6 +47,7 @@ Data.Bitcoin.PaymentChannel.Internal.Serialization, Data.Bitcoin.PaymentChannel.Internal.Util, Data.Bitcoin.PaymentChannel.Internal.Version+ ghc-options: -W
+ src/Data/Bitcoin/PaymentChannel/Internal/BitcoinAmount.hs view
@@ -0,0 +1,60 @@+module Data.Bitcoin.PaymentChannel.Internal.BitcoinAmount where++import qualified Data.Serialize as Ser+import qualified Data.Serialize.Put as SerPut+import qualified Data.Serialize.Get as SerGet+import qualified Data.Binary as Bin+import qualified Data.Binary.Put as BinPut+import qualified Data.Binary.Get as BinGet+import Data.Word+++-- |Represents a bitcoin amount as number of satoshis.+-- 1 satoshi = 1e-8 bitcoin.+-- Only natural numbers (>= 0) can be represented ('fromInteger' caps to a 'Word64').+-- It is thus not possible to construct a negative BitcoinAmount which, when added to+-- another BitcoinAmount, subtracts from its value. So we avoid the problem of being+-- able to invert subtraction/addition by supplying a negative value as one of the+-- arguments.+newtype BitcoinAmount = BitcoinAmount Integer+ deriving (Eq, Ord)+instance Show BitcoinAmount where+ show amount = show (toInteger amount) ++ " satoshi"++instance Num BitcoinAmount where+ -- We leave multiplication of two money amounts as undefined+ (BitcoinAmount _) * (BitcoinAmount _) = undefined+ (BitcoinAmount a1) + (BitcoinAmount a2) = BitcoinAmount (fromIntegral . capToWord64 $ a1+a2)+ (BitcoinAmount a1) - (BitcoinAmount a2) = BitcoinAmount (fromIntegral . capToWord64 $ a1-a2)+ abs = id -- Always positive+ signum (BitcoinAmount 0) = BitcoinAmount 0+ signum (BitcoinAmount _) = BitcoinAmount 1+ fromInteger = BitcoinAmount . fromIntegral . capToWord64++instance Enum BitcoinAmount where+ toEnum = BitcoinAmount . fromIntegral . capToWord64 . fromIntegral+ fromEnum (BitcoinAmount amount) = fromIntegral amount++instance Real BitcoinAmount where+ toRational (BitcoinAmount amount) = toRational amount++instance Integral BitcoinAmount where+ toInteger (BitcoinAmount int) = int+ -- Dividing one money amounts by another doesn't make sense either+ quotRem (BitcoinAmount _) (BitcoinAmount _) = undefined++-- | Convert to 'Word64', with zero as floor, (maxBound :: Word64) as ceiling+capToWord64 :: Integer -> Word64+capToWord64 i = fromIntegral $+ max 0 cappedValue+ where+ cappedValue = min i $ fromIntegral (maxBound :: Word64)++instance Bin.Binary BitcoinAmount where+ put = BinPut.putWord64le . fromIntegral . toInteger+ get = BitcoinAmount . fromIntegral <$> BinGet.getWord64le++instance Ser.Serialize BitcoinAmount where+ put = SerPut.putWord64le . fromIntegral . toInteger+ get = BitcoinAmount . fromIntegral <$> SerGet.getWord64le+
src/Data/Bitcoin/PaymentChannel/Internal/Error.hs view
@@ -1,6 +1,6 @@ module Data.Bitcoin.PaymentChannel.Internal.Error where -import Data.Bitcoin.PaymentChannel.Internal.Util (BitcoinAmount)+import Data.Bitcoin.PaymentChannel.Internal.Types (BitcoinAmount) data PayChanError = BadSignature |
src/Data/Bitcoin/PaymentChannel/Internal/Payment.hs view
@@ -43,7 +43,7 @@ getPaymentTxForSigning st@(CPaymentChannelState _ fti (CPaymentTxConfig sendAddr) chanValLeft _) newValueLeft = paymentTxAddOutput senderOut $ buildEmptyPaymentTx fti- where senderOut = HT.TxOut (toWord64 newValueLeft) (addressToScriptPubKeyBS sendAddr)+ where senderOut = HT.TxOut (fromIntegral . toInteger $ newValueLeft) (addressToScriptPubKeyBS sendAddr) getPaymentTxHashForSigning :: PaymentChannelState
src/Data/Bitcoin/PaymentChannel/Internal/Refund.hs view
@@ -14,13 +14,15 @@ getUnsignedRefundTx st txFee = -- @CPaymentChannelState = let (baseTx,_) = getPaymentTxForSigning st 0 --create empty payment tx, which redeems funding tx- refundOut = HT.TxOut (toWord64 $ pcsChannelTotalValue st - txFee) (pcsClientChange st)+ refundOut = HT.TxOut+ (fromIntegral . toInteger $ pcsChannelTotalValue st - txFee)+ (pcsClientChangeScriptPubKey st) txInput0 = head $ HT.txIn baseTx in baseTx { HT.txOut = [refundOut],- -- lockTime of refund tx must be equal to or greater than lockTime- -- in channel redeemScript+ -- lockTime of refund tx must be greater than or equal to the lockTime+ -- in the channel redeemScript HT.txLockTime = toWord32 (pcsLockTime st), -- if the sequence field equals maxBound (0xffffffff), -- lockTime features are disabled, so we set it to that minus one
src/Data/Bitcoin/PaymentChannel/Internal/Serialization.hs view
@@ -6,7 +6,7 @@ import Data.Bitcoin.PaymentChannel.Internal.Types import Data.Bitcoin.PaymentChannel.Internal.Util- (BitcoinAmount(..), toWord64, deserEither, toHexString,+ (deserEither, toHexString, parseBitcoinLocktime, BitcoinLockTime(..), toWord32) import Data.Aeson (Value(Number), FromJSON(..), ToJSON(..),@@ -50,7 +50,7 @@ instance ToJSON BitcoinAmount where toJSON amt = Number $ scientific- (fromIntegral $ toWord64 amt) 0+ (fromIntegral $ toInteger amt) 0 instance FromJSON BitcoinAmount where parseJSON = withScientific "BitcoinAmount" $@@ -113,8 +113,8 @@ -- Needed to convert from Scientific instance Bounded BitcoinAmount where- minBound = CMoneyAmount 0- maxBound = CMoneyAmount $ round $ 21e6 * 1e8+ minBound = BitcoinAmount 0+ maxBound = BitcoinAmount $ round $ 21e6 * 1e8 --- Util@@ -138,7 +138,7 @@ parseJSONInt :: Scientific -> Parser Integer parseJSONInt s = case toBoundedInteger s of- Just (CMoneyAmount i) -> return i+ Just (BitcoinAmount i) -> return i Nothing -> fail $ "failed to decode JSON number to integer. data: " ++ show s parseJSONWord :: Scientific -> Parser Word64
src/Data/Bitcoin/PaymentChannel/Internal/Settlement.hs view
@@ -24,7 +24,7 @@ (baseTx,_) = getPaymentTxForSigning st senderVal adjTx = if sigHash == HS.SigNone True then removeOutputs baseTx else baseTx receiverAmount = channelTotalValue - senderVal - txFee -- may be less than zero- recvOut = HT.TxOut (toWord64 receiverAmount) (addressToScriptPubKeyBS recvAddr)+ recvOut = HT.TxOut (fromIntegral . toInteger $ receiverAmount) (addressToScriptPubKeyBS recvAddr) in paymentTxAddOutput recvOut adjTx getSettlementTxForSigning _ _ _ = error "no payment sig available"
src/Data/Bitcoin/PaymentChannel/Internal/State.hs view
@@ -4,7 +4,7 @@ import Data.Bitcoin.PaymentChannel.Internal.Types import Data.Bitcoin.PaymentChannel.Internal.Error-import Data.Bitcoin.PaymentChannel.Internal.Util (addressToScriptPubKeyBS, BitcoinAmount)+import Data.Bitcoin.PaymentChannel.Internal.Util (addressToScriptPubKeyBS) import qualified Network.Haskoin.Transaction as HT import qualified Network.Haskoin.Crypto as HC@@ -17,7 +17,7 @@ pcsServerPubKey = cpReceiverPubKey . pcsParameters pcsExpirationDate = cpLockTime . pcsParameters pcsClientChangeAddress = ptcSenderChangeAddress . pcsPaymentConfig-pcsClientChange = addressToScriptPubKeyBS . pcsClientChangeAddress+pcsClientChangeScriptPubKey = addressToScriptPubKeyBS . pcsClientChangeAddress pcsLockTime = cpLockTime . pcsParameters pcsChannelID :: PaymentChannelState -> HT.OutPoint
src/Data/Bitcoin/PaymentChannel/Internal/Types.hs view
@@ -1,19 +1,23 @@ -- {-# LANGUAGE DeriveDataTypeable #-} -module Data.Bitcoin.PaymentChannel.Internal.Types where+module Data.Bitcoin.PaymentChannel.Internal.Types+(+ module Data.Bitcoin.PaymentChannel.Internal.Types+ , module Data.Bitcoin.PaymentChannel.Internal.BitcoinAmount+) +where+ import Data.Bitcoin.PaymentChannel.Internal.Util import Data.Bitcoin.PaymentChannel.Internal.Version+import Data.Bitcoin.PaymentChannel.Internal.BitcoinAmount import qualified Network.Haskoin.Transaction as HT import qualified Network.Haskoin.Crypto as HC import qualified Network.Haskoin.Script as HS-import qualified Data.ByteString as B-import Data.Word (Word32, Word64, Word8)-import qualified Data.Binary as Bin-import qualified Data.Binary.Put as BinPut-import qualified Data.Binary.Get as BinGet import Data.Typeable+import Data.Word+ dUST_LIMIT = 700 :: BitcoinAmount mIN_CHANNEL_SIZE = dUST_LIMIT * 2
src/Data/Bitcoin/PaymentChannel/Internal/Util.hs view
@@ -4,7 +4,7 @@ module Data.Bitcoin.PaymentChannel.Internal.Util where import Data.String (fromString)-import qualified Data.Serialize as Ser (Serialize, encode, get, put)+import qualified Data.Serialize as Ser import qualified Data.Serialize.Put as SerPut import qualified Data.Serialize.Get as SerGet import qualified Data.Binary as Bin@@ -44,32 +44,6 @@ (bs,e) -> if B.length e /= 0 then B.empty else bs ---- |Represents a bitcoin amount as number of satoshis.--- 1 satoshi = 1e-8 bitcoins.--- Integer operations will never over- or underflow with this type.--- Convert to a Word64 using 'toWord64', which caps the final amount.-newtype BitcoinAmount = CMoneyAmount Integer- deriving (Eq, Ord, Num, Enum, Real, Integral)-instance Show BitcoinAmount where- show ma = show (fromIntegral $ toWord64 ma) ++ " satoshi"---- | Convert to 'Word64', with zero as floor, UINT64_MAX as ceiling-toWord64 :: BitcoinAmount -> Word64-toWord64 (CMoneyAmount i) = fromIntegral $- max 0 cappedValue- where- cappedValue = min i $ fromIntegral (maxBound :: Word64)--instance Bin.Binary BitcoinAmount where- put = BinPut.putWord64le . toWord64- get = CMoneyAmount . fromIntegral <$> BinGet.getWord64le--instance Ser.Serialize BitcoinAmount where- put = SerPut.putWord64le . toWord64- get = CMoneyAmount . fromIntegral <$> SerGet.getWord64le-- -- | Converts a pay-to-pubkey-hash address string to Script. -- | Eg. \"1PDGytNA7EJ5XrJdTGKv11VAUFxKnsfwke\" into -- | \"Script [OP_DUP, OP_HASH160, OP_PUSHDATA f3a5194fbf4b3556838e0f773b613a876737a2c6,@@ -122,7 +96,7 @@ replaceScriptInput scriptIn (HT.Tx v (txIn:_) txOut lt) = HT.Tx v [newTxIn] txOut lt where newTxIn = txIn { HT.scriptInput = scriptIn }-replaceScriptInput scriptIn (HT.Tx _ [] _ _) =+replaceScriptInput _ (HT.Tx _ [] _ _) = error "cannot replace scriptInput without any inputs" removeOutputs :: HT.Tx -> HT.Tx
src/Data/Bitcoin/PaymentChannel/Types.hs view
@@ -21,7 +21,7 @@ PayChanError(..), PaymentChannelState, SendPubKey(..),RecvPubKey(..),IsPubKey(..),-BitcoinAmount, toWord64,+BitcoinAmount, BitcoinLockTime(..), fromDate, usesBlockHeight, @@ -34,15 +34,15 @@ where import Data.Bitcoin.PaymentChannel.Internal.Types- (PaymentChannelState(..), Payment(..)+ (BitcoinAmount(..),+ PaymentChannelState(..), Payment(..) ,FundingTxInfo(..), ChannelParameters(..), PaymentSignature(..), SendPubKey(..), RecvPubKey(..), IsPubKey(..), dUST_LIMIT, mIN_CHANNEL_SIZE) import Data.Bitcoin.PaymentChannel.Internal.Serialization -- () import Data.Bitcoin.PaymentChannel.Internal.Util- (BitcoinAmount(..), toWord64,- BitcoinLockTime(..), fromDate, usesBlockHeight)+ (BitcoinLockTime(..), fromDate, usesBlockHeight) import qualified Data.Bitcoin.PaymentChannel.Internal.State as S import Data.Bitcoin.PaymentChannel.Internal.Error (PayChanError(..)) -- import Data.Bitcoin.PaymentChannel.Internal.Types ()