haskoin-core 0.19.0 → 0.20.0
raw patch · 33 files changed
+1715/−757 lines, 33 filesdep +base16dep +binarydep +bytesdep −base16-bytestringdep ~cerealPVP ok
version bump matches the API change (PVP)
Dependencies added: base16, binary, bytes
Dependencies removed: base16-bytestring
Dependency ranges changed: cereal
API changes (from Hackage documentation)
+ Haskoin.Address: instance Data.Binary.Class.Binary Haskoin.Address.Address
+ Haskoin.Address: instance Data.Bytes.Serial.Serial Haskoin.Address.Address
+ Haskoin.Block.Common: instance Data.Binary.Class.Binary Haskoin.Block.Common.Block
+ Haskoin.Block.Common: instance Data.Binary.Class.Binary Haskoin.Block.Common.BlockHash
+ Haskoin.Block.Common: instance Data.Binary.Class.Binary Haskoin.Block.Common.BlockHeader
+ Haskoin.Block.Common: instance Data.Binary.Class.Binary Haskoin.Block.Common.GetHeaders
+ Haskoin.Block.Common: instance Data.Binary.Class.Binary Haskoin.Block.Common.Headers
+ Haskoin.Block.Common: instance Data.Bytes.Serial.Serial Haskoin.Block.Common.Block
+ Haskoin.Block.Common: instance Data.Bytes.Serial.Serial Haskoin.Block.Common.BlockHash
+ Haskoin.Block.Common: instance Data.Bytes.Serial.Serial Haskoin.Block.Common.BlockHeader
+ Haskoin.Block.Common: instance Data.Bytes.Serial.Serial Haskoin.Block.Common.GetBlocks
+ Haskoin.Block.Common: instance Data.Bytes.Serial.Serial Haskoin.Block.Common.GetHeaders
+ Haskoin.Block.Common: instance Data.Bytes.Serial.Serial Haskoin.Block.Common.Headers
+ Haskoin.Block.Headers: instance Data.Binary.Class.Binary Haskoin.Block.Headers.BlockNode
+ Haskoin.Block.Headers: instance Data.Bytes.Serial.Serial Haskoin.Block.Headers.BlockNode
+ Haskoin.Block.Merkle: instance Data.Binary.Class.Binary Haskoin.Block.Merkle.MerkleBlock
+ Haskoin.Block.Merkle: instance Data.Bytes.Serial.Serial Haskoin.Block.Merkle.MerkleBlock
+ Haskoin.Constants: instance Data.Binary.Class.Binary Haskoin.Constants.Network
+ Haskoin.Constants: instance Data.Bytes.Serial.Serial Haskoin.Constants.Network
+ Haskoin.Crypto.Hash: instance Data.Binary.Class.Binary Haskoin.Crypto.Hash.CheckSum32
+ Haskoin.Crypto.Hash: instance Data.Binary.Class.Binary Haskoin.Crypto.Hash.Hash160
+ Haskoin.Crypto.Hash: instance Data.Binary.Class.Binary Haskoin.Crypto.Hash.Hash256
+ Haskoin.Crypto.Hash: instance Data.Binary.Class.Binary Haskoin.Crypto.Hash.Hash512
+ Haskoin.Crypto.Hash: instance Data.Bytes.Serial.Serial Haskoin.Crypto.Hash.CheckSum32
+ Haskoin.Crypto.Hash: instance Data.Bytes.Serial.Serial Haskoin.Crypto.Hash.Hash160
+ Haskoin.Crypto.Hash: instance Data.Bytes.Serial.Serial Haskoin.Crypto.Hash.Hash256
+ Haskoin.Crypto.Hash: instance Data.Bytes.Serial.Serial Haskoin.Crypto.Hash.Hash512
+ Haskoin.Keys.Common: instance Data.Binary.Class.Binary Haskoin.Keys.Common.PubKeyI
+ Haskoin.Keys.Common: instance Data.Bytes.Serial.Serial Haskoin.Keys.Common.PubKeyI
+ Haskoin.Keys.Extended: instance Data.Binary.Class.Binary Haskoin.Keys.Extended.DerivPath
+ Haskoin.Keys.Extended: instance Data.Binary.Class.Binary Haskoin.Keys.Extended.HardPath
+ Haskoin.Keys.Extended: instance Data.Binary.Class.Binary Haskoin.Keys.Extended.SoftPath
+ Haskoin.Keys.Extended: instance Data.Binary.Class.Binary Haskoin.Keys.Extended.XPrvKey
+ Haskoin.Keys.Extended: instance Data.Binary.Class.Binary Haskoin.Keys.Extended.XPubKey
+ Haskoin.Keys.Extended: instance Data.Bytes.Serial.Serial Haskoin.Keys.Extended.DerivPath
+ Haskoin.Keys.Extended: instance Data.Bytes.Serial.Serial Haskoin.Keys.Extended.HardPath
+ Haskoin.Keys.Extended: instance Data.Bytes.Serial.Serial Haskoin.Keys.Extended.SoftPath
+ Haskoin.Keys.Extended: instance Data.Bytes.Serial.Serial Haskoin.Keys.Extended.XPrvKey
+ Haskoin.Keys.Extended: instance Data.Bytes.Serial.Serial Haskoin.Keys.Extended.XPubKey
+ Haskoin.Network.Bloom: instance Data.Binary.Class.Binary Haskoin.Network.Bloom.BloomFilter
+ Haskoin.Network.Bloom: instance Data.Binary.Class.Binary Haskoin.Network.Bloom.BloomFlags
+ Haskoin.Network.Bloom: instance Data.Binary.Class.Binary Haskoin.Network.Bloom.FilterAdd
+ Haskoin.Network.Bloom: instance Data.Binary.Class.Binary Haskoin.Network.Bloom.FilterLoad
+ Haskoin.Network.Bloom: instance Data.Bytes.Serial.Serial Haskoin.Network.Bloom.BloomFilter
+ Haskoin.Network.Bloom: instance Data.Bytes.Serial.Serial Haskoin.Network.Bloom.BloomFlags
+ Haskoin.Network.Bloom: instance Data.Bytes.Serial.Serial Haskoin.Network.Bloom.FilterAdd
+ Haskoin.Network.Bloom: instance Data.Bytes.Serial.Serial Haskoin.Network.Bloom.FilterLoad
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Addr
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Alert
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.GetData
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.HostAddress
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Inv
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.InvType
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.InvVector
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.MessageCommand
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.NetworkAddress
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.NotFound
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Ping
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Pong
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Reject
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.RejectCode
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.VarInt
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.VarString
+ Haskoin.Network.Common: instance Data.Binary.Class.Binary Haskoin.Network.Common.Version
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Addr
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Alert
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.GetData
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.HostAddress
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Inv
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.InvType
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.InvVector
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.MessageCommand
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.NetworkAddress
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.NotFound
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Ping
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Pong
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Reject
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.RejectCode
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.VarInt
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.VarString
+ Haskoin.Network.Common: instance Data.Bytes.Serial.Serial Haskoin.Network.Common.Version
+ Haskoin.Network.Message: instance Data.Binary.Class.Binary Haskoin.Network.Message.MessageHeader
+ Haskoin.Network.Message: instance Data.Bytes.Serial.Serial Haskoin.Network.Message.MessageHeader
+ Haskoin.Script.Common: instance Data.Binary.Class.Binary Haskoin.Script.Common.Script
+ Haskoin.Script.Common: instance Data.Binary.Class.Binary Haskoin.Script.Common.ScriptOp
+ Haskoin.Script.Common: instance Data.Bytes.Serial.Serial Haskoin.Script.Common.Script
+ Haskoin.Script.Common: instance Data.Bytes.Serial.Serial Haskoin.Script.Common.ScriptOp
+ Haskoin.Transaction.Common: instance Data.Binary.Class.Binary Haskoin.Transaction.Common.OutPoint
+ Haskoin.Transaction.Common: instance Data.Binary.Class.Binary Haskoin.Transaction.Common.Tx
+ Haskoin.Transaction.Common: instance Data.Binary.Class.Binary Haskoin.Transaction.Common.TxHash
+ Haskoin.Transaction.Common: instance Data.Binary.Class.Binary Haskoin.Transaction.Common.TxIn
+ Haskoin.Transaction.Common: instance Data.Binary.Class.Binary Haskoin.Transaction.Common.TxOut
+ Haskoin.Transaction.Common: instance Data.Bytes.Serial.Serial Haskoin.Transaction.Common.OutPoint
+ Haskoin.Transaction.Common: instance Data.Bytes.Serial.Serial Haskoin.Transaction.Common.Tx
+ Haskoin.Transaction.Common: instance Data.Bytes.Serial.Serial Haskoin.Transaction.Common.TxHash
+ Haskoin.Transaction.Common: instance Data.Bytes.Serial.Serial Haskoin.Transaction.Common.TxIn
+ Haskoin.Transaction.Common: instance Data.Bytes.Serial.Serial Haskoin.Transaction.Common.TxOut
+ Haskoin.Util: decodeHexLazy :: Text -> Maybe ByteString
+ Haskoin.Util: encodeHexLazy :: ByteString -> Text
+ Haskoin.Util: getInt32be :: MonadGet m => m Int32
+ Haskoin.Util: getInt64be :: MonadGet m => m Int64
+ Haskoin.Util: getIntMap :: MonadGet m => m Int -> m a -> m (IntMap a)
+ Haskoin.Util: getInteger :: MonadGet m => m Integer
+ Haskoin.Util: getLengthBytes :: MonadGet m => m ByteString
+ Haskoin.Util: getList :: MonadGet m => m a -> m [a]
+ Haskoin.Util: getMaybe :: MonadGet m => m a -> m (Maybe a)
+ Haskoin.Util: getTwo :: MonadGet m => m a -> m b -> m (a, b)
+ Haskoin.Util: putInt32be :: MonadPut m => Int32 -> m ()
+ Haskoin.Util: putInt64be :: MonadPut m => Int64 -> m ()
+ Haskoin.Util: putIntMap :: MonadPut m => (Int -> m ()) -> (a -> m ()) -> IntMap a -> m ()
+ Haskoin.Util: putInteger :: MonadPut m => Integer -> m ()
+ Haskoin.Util: putLengthBytes :: MonadPut m => ByteString -> m ()
+ Haskoin.Util: putList :: MonadPut m => (a -> m ()) -> [a] -> m ()
+ Haskoin.Util: putMaybe :: MonadPut m => (a -> m ()) -> Maybe a -> m ()
+ Haskoin.Util: putTwo :: MonadPut m => (a -> m ()) -> (b -> m ()) -> (a, b) -> m ()
- Haskoin.Crypto.Signature: getSig :: Get Sig
+ Haskoin.Crypto.Signature: getSig :: MonadGet m => m Sig
- Haskoin.Crypto.Signature: putSig :: Putter Sig
+ Haskoin.Crypto.Signature: putSig :: MonadPut m => Sig -> m ()
- Haskoin.Keys.Extended: getXPrvKey :: Network -> Get XPrvKey
+ Haskoin.Keys.Extended: getXPrvKey :: MonadGet m => Network -> m XPrvKey
- Haskoin.Keys.Extended: getXPubKey :: Network -> Get XPubKey
+ Haskoin.Keys.Extended: getXPubKey :: MonadGet m => Network -> m XPubKey
- Haskoin.Keys.Extended: putXPrvKey :: Network -> Putter XPrvKey
+ Haskoin.Keys.Extended: putXPrvKey :: MonadPut m => Network -> XPrvKey -> m ()
- Haskoin.Keys.Extended: putXPubKey :: Network -> Putter XPubKey
+ Haskoin.Keys.Extended: putXPubKey :: MonadPut m => Network -> XPubKey -> m ()
- Haskoin.Network.Common: putVarInt :: Integral a => a -> Put
+ Haskoin.Network.Common: putVarInt :: (MonadPut m, Integral a) => a -> m ()
- Haskoin.Network.Message: getMessage :: Network -> Get Message
+ Haskoin.Network.Message: getMessage :: MonadGet m => Network -> m Message
- Haskoin.Network.Message: putMessage :: Network -> Putter Message
+ Haskoin.Network.Message: putMessage :: MonadPut m => Network -> Message -> m ()
- Haskoin.Util.Arbitrary.Util: testSerial :: (Eq a, Show a, Typeable a, Serialize a) => Gen a -> Spec
+ Haskoin.Util.Arbitrary.Util: testSerial :: (Eq a, Show a, Typeable a, Serial a) => Gen a -> Spec
Files
- CHANGELOG.md +5/−0
- haskoin-core.cabal +11/−7
- src/Haskoin/Address.hs +83/−33
- src/Haskoin/Address/Base58.hs +5/−3
- src/Haskoin/Block/Common.hs +105/−43
- src/Haskoin/Block/Headers.hs +31/−21
- src/Haskoin/Block/Merkle.hs +23/−14
- src/Haskoin/Constants.hs +16/−4
- src/Haskoin/Crypto/Hash.hs +45/−11
- src/Haskoin/Crypto/Signature.hs +8/−4
- src/Haskoin/Keys/Common.hs +39/−18
- src/Haskoin/Keys/Extended.hs +106/−49
- src/Haskoin/Network/Bloom.hs +60/−25
- src/Haskoin/Network/Common.hs +225/−81
- src/Haskoin/Network/Message.hs +74/−62
- src/Haskoin/Script/Common.hs +39/−14
- src/Haskoin/Script/SigHash.hs +19/−17
- src/Haskoin/Script/Standard.hs +29/−25
- src/Haskoin/Transaction/Builder.hs +24/−8
- src/Haskoin/Transaction/Builder/Sign.hs +4/−2
- src/Haskoin/Transaction/Common.hs +107/−45
- src/Haskoin/Transaction/Partial.hs +357/−150
- src/Haskoin/Transaction/Segwit.hs +9/−7
- src/Haskoin/Util.hs +169/−24
- src/Haskoin/Util/Arbitrary/Script.hs +1/−1
- src/Haskoin/Util/Arbitrary/Util.hs +23/−21
- test/Haskoin/Crypto/HashSpec.hs +7/−6
- test/Haskoin/Keys/ExtendedSpec.hs +14/−12
- test/Haskoin/KeysSpec.hs +15/−11
- test/Haskoin/NetworkSpec.hs +9/−7
- test/Haskoin/ScriptSpec.hs +25/−18
- test/Haskoin/Transaction/PartialSpec.hs +13/−4
- test/Haskoin/TransactionSpec.hs +15/−10
CHANGELOG.md view
@@ -4,6 +4,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ++## 0.20.0+### Chaged+- Use bytes instead of binary or cereal.+ ## 0.19.0 ### Added - Hashable instances for extended keys.
haskoin-core.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: 90de304467308666144472c303ad361258687f7e36e27e916ba836799ea6833c+-- hash: f90b7b9353df059b5744c2454f8f514133d3e989a66c8137a87030db88a73a2a name: haskoin-core-version: 0.19.0+version: 0.20.0 synopsis: Bitcoin & Bitcoin Cash library for Haskell description: Please see the README on GitHub at <https://github.com/haskoin/haskoin-core#readme> category: Bitcoin, Finance, Network@@ -94,9 +94,11 @@ , aeson >=1.4.6.0 , array >=0.5.4.0 , base >=4.9 && <5- , base16-bytestring >=0.1.1.6+ , base16 >=0.3.0.1+ , binary >=0.8.8+ , bytes >=0.17 , bytestring >=0.10.10.0- , cereal >=0.5.8.1+ , cereal >=0.5.8 , conduit >=1.3.1.2 , containers >=0.6.2.1 , cryptonite >=0.26@@ -147,9 +149,11 @@ , aeson >=1.4.6.0 , array >=0.5.4.0 , base >=4.9 && <5- , base16-bytestring >=0.1.1.6+ , base16 >=0.3.0.1+ , binary >=0.8.8+ , bytes >=0.17 , bytestring >=0.10.10.0- , cereal >=0.5.8.1+ , cereal >=0.5.8 , conduit >=1.3.1.2 , containers >=0.6.2.1 , cryptonite >=0.26
src/Haskoin/Address.hs view
@@ -58,12 +58,17 @@ import Data.Aeson as A import Data.Aeson.Encoding as A import Data.Aeson.Types+import Data.Binary (Binary (..)) import Data.ByteString (ByteString) import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Hashable import Data.Maybe-import Data.Serialize as S+import Data.Serialize (Serialize (..)) import Data.Text (Text)+import qualified Data.Text as T import Data.Word (Word8) import GHC.Generics (Generic) import Haskoin.Address.Base58@@ -102,8 +107,48 @@ { getAddrVersion :: !Word8 , getAddrData :: !ByteString }- deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData)+ deriving+ (Eq, Ord, Generic, Show, Read, Hashable, NFData) +instance Serial Address where+ serialize (PubKeyAddress k) = do+ putWord8 0x00+ serialize k+ serialize (ScriptAddress s) = do+ putWord8 0x01+ serialize s+ serialize (WitnessPubKeyAddress h) = do+ putWord8 0x02+ serialize h+ serialize (WitnessScriptAddress s) = do+ putWord8 0x03+ serialize s+ serialize (WitnessAddress v d) = do+ putWord8 0x04+ putWord8 v+ putWord64be (fromIntegral (B.length d))+ putByteString d++ deserialize =+ getWord8 >>= \case+ 0x00 -> PubKeyAddress <$> deserialize+ 0x01 -> ScriptAddress <$> deserialize+ 0x02 -> WitnessPubKeyAddress <$> deserialize+ 0x03 -> WitnessScriptAddress <$> deserialize+ 0x04 -> WitnessAddress <$> getWord8 <*>+ (getByteString . fromIntegral =<< getWord64be)+ b -> fail . T.unpack $+ "Could not decode address type byte: " <>+ encodeHex (B.singleton b)++instance Serialize Address where+ put = serialize+ get = deserialize++instance Binary Address where+ put = serialize+ get = deserialize+ -- | 'Address' pays to a public key hash. isPubKeyAddress :: Address -> Bool isPubKeyAddress PubKeyAddress {} = True@@ -148,19 +193,19 @@ addrToText :: Network -> Address -> Maybe Text addrToText net a@PubKeyAddress {getAddrHash160 = h} | isNothing (getCashAddrPrefix net) =- Just . encodeBase58Check . runPut $ base58put net a- | otherwise = cashAddrEncode net 0 (S.encode h)+ Just . encodeBase58Check . runPutS $ base58put net a+ | otherwise = cashAddrEncode net 0 (runPutS $ serialize h) addrToText net a@ScriptAddress {getAddrHash160 = h} | isNothing (getCashAddrPrefix net) =- Just . encodeBase58Check . runPut $ base58put net a+ Just . encodeBase58Check . runPutS $ base58put net a | otherwise =- cashAddrEncode net 1 (S.encode h)+ cashAddrEncode net 1 (runPutS $ serialize h) addrToText net WitnessPubKeyAddress {getAddrHash160 = h} = do hrp <- getBech32Prefix net- segwitEncode hrp 0 (B.unpack (S.encode h))+ segwitEncode hrp 0 (B.unpack (runPutS $ serialize h)) addrToText net WitnessScriptAddress {getAddrHash256 = h} = do hrp <- getBech32Prefix net- segwitEncode hrp 0 (B.unpack (S.encode h))+ segwitEncode hrp 0 (B.unpack (runPutS $ serialize h)) addrToText net WitnessAddress {getAddrVersion = v, getAddrData = d} = do hrp <- getBech32Prefix net segwitEncode hrp v (B.unpack d)@@ -174,8 +219,8 @@ cashToAddr net txt = do (ver, bs) <- cashAddrDecode net txt case ver of- 0 -> PubKeyAddress <$> eitherToMaybe (S.decode bs)- 1 -> ScriptAddress <$> eitherToMaybe (S.decode bs)+ 0 -> PubKeyAddress <$> eitherToMaybe (runGetS deserialize bs)+ 1 -> ScriptAddress <$> eitherToMaybe (runGetS deserialize bs) _ -> Nothing bech32ToAddr :: Network -> Text -> Maybe Address@@ -184,19 +229,19 @@ (ver, bs) <- second B.pack <$> segwitDecode hrp txt case ver of 0 -> case B.length bs of- 20 -> WitnessPubKeyAddress <$> eitherToMaybe (S.decode bs)- 32 -> WitnessScriptAddress <$> eitherToMaybe (S.decode bs)+ 20 -> WitnessPubKeyAddress <$> eitherToMaybe (runGetS deserialize bs)+ 32 -> WitnessScriptAddress <$> eitherToMaybe (runGetS deserialize bs) _ -> Nothing _ -> Just $ WitnessAddress ver bs base58ToAddr :: Network -> Text -> Maybe Address base58ToAddr net txt =- eitherToMaybe . runGet (base58get net) =<< decodeBase58Check txt+ eitherToMaybe . runGetS (base58get net) =<< decodeBase58Check txt -base58get :: Network -> Get Address+base58get :: MonadGet m => Network -> m Address base58get net = do pfx <- getWord8- addr <- S.get+ addr <- deserialize f pfx addr where f x a@@ -204,18 +249,18 @@ | x == getScriptPrefix net = return $ ScriptAddress a | otherwise = fail "Does not recognize address prefix" -base58put :: Network -> Putter Address+base58put :: MonadPut m => Network -> Address -> m () base58put net (PubKeyAddress h) = do putWord8 (getAddrPrefix net)- put h+ serialize h base58put net (ScriptAddress h) = do putWord8 (getScriptPrefix net)- put h+ serialize h base58put _ _ = error "Cannot serialize this address as Base58" -- | Obtain a standard pay-to-public-key-hash address from a public key. pubKeyAddr :: PubKeyI -> Address-pubKeyAddr = PubKeyAddress . addressHash . S.encode+pubKeyAddr = PubKeyAddress . addressHash . runPutS . serialize -- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'. p2pkhAddr :: Hash160 -> Address@@ -224,13 +269,18 @@ -- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a -- public key. pubKeyWitnessAddr :: PubKeyI -> Address-pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . S.encode+pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . runPutS . serialize -- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key. pubKeyCompatWitnessAddr :: PubKeyI -> Address pubKeyCompatWitnessAddr = p2shAddr .- addressHash . encodeOutputBS . PayWitnessPKHash . addressHash . S.encode+ addressHash .+ encodeOutputBS .+ PayWitnessPKHash .+ addressHash .+ runPutS .+ serialize -- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a -- 'Hash160'.@@ -264,11 +314,11 @@ addressToOutput :: Address -> ScriptOutput addressToOutput = \case- PubKeyAddress h -> PayPKHash h- ScriptAddress h -> PayScriptHash h+ PubKeyAddress h -> PayPKHash h+ ScriptAddress h -> PayScriptHash h WitnessPubKeyAddress h -> PayWitnessPKHash h WitnessScriptAddress h -> PayWitnessScriptHash h- WitnessAddress v d -> PayWitness v d+ WitnessAddress v d -> PayWitness v d -- | Get output script AST for an 'Address'. addressToScript :: Address -> Script@@ -276,7 +326,7 @@ -- | Encode address as output script in 'ByteString' form. addressToScriptBS :: Address -> ByteString-addressToScriptBS = S.encode . addressToScript+addressToScriptBS = runPutS . serialize . addressToScript -- | Decode an output script into an 'Address' if it has such representation. scriptToAddress :: Script -> Either String Address@@ -292,18 +342,18 @@ outputAddress :: ScriptOutput -> Maybe Address outputAddress = \case- PayPKHash h -> Just $ PubKeyAddress h- PayScriptHash h -> Just $ ScriptAddress h- PayPK k -> Just $ pubKeyAddr k- PayWitnessPKHash h -> Just $ WitnessPubKeyAddress h+ PayPKHash h -> Just $ PubKeyAddress h+ PayScriptHash h -> Just $ ScriptAddress h+ PayPK k -> Just $ pubKeyAddr k+ PayWitnessPKHash h -> Just $ WitnessPubKeyAddress h PayWitnessScriptHash h -> Just $ WitnessScriptAddress h- PayWitness v d -> Just $ WitnessAddress v d- _ -> Nothing+ PayWitness v d -> Just $ WitnessAddress v d+ _ -> Nothing -- | Infer the 'Address' of a 'ScriptInput'. inputAddress :: ScriptInput -> Maybe Address inputAddress = \case (RegularInput (SpendPKHash _ key)) -> Just $ pubKeyAddr key- (ScriptHashInput _ rdm) -> Just $ payToScriptAddress rdm- _ -> Nothing+ (ScriptHashInput _ rdm) -> Just $ payToScriptAddress rdm+ _ -> Nothing
src/Haskoin/Address/Base58.hs view
@@ -23,8 +23,10 @@ import Data.ByteString (ByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Maybe (fromMaybe, isJust, listToMaybe)-import Data.Serialize as S import Data.String.Conversions (cs) import Data.Text (Text) import qualified Data.Text as T@@ -90,7 +92,7 @@ -- the checksum as 'Base58'. encodeBase58Check :: ByteString -> Base58 encodeBase58Check bs =- encodeBase58 $ BS.append bs $ encode $ checkSum32 bs+ encodeBase58 $ BS.append bs $ runPutS $ serialize $ checkSum32 bs -- | Decode a 'Base58'-encoded string that contains a checksum. This function -- returns 'Nothing' if the input string contains invalid 'Base58' characters or@@ -99,5 +101,5 @@ decodeBase58Check bs = do rs <- decodeBase58 bs let (res, chk) = BS.splitAt (BS.length rs - 4) rs- guard $ chk == encode (checkSum32 res)+ guard $ chk == runPutS (serialize (checkSum32 res)) return res
src/Haskoin/Block/Common.hs view
@@ -36,15 +36,19 @@ Value (..), object, toJSON, withObject, withText, (.:), (.=)) import Data.Aeson.Encoding (pairs, unsafeToEncoding)+import Data.Binary (Binary (..)) import Data.Bits (shiftL, shiftR, (.&.), (.|.)) import qualified Data.ByteString as B import Data.ByteString.Builder (char7)+import qualified Data.ByteString.Lazy as BL+import Data.Bytes.Get (MonadGet, getWord32le, runGetL,+ runGetS)+import Data.Bytes.Put (MonadPut, putWord32le, runPutL,+ runPutS)+import Data.Bytes.Serial (Serial (..)) import Data.Hashable (Hashable) import Data.Maybe (fromMaybe)-import Data.Serialize (Serialize, decode, encode, get,- put)-import Data.Serialize.Get (getWord32le)-import Data.Serialize.Put (Put, putWord32le)+import Data.Serialize (Serialize (..)) import Data.String (IsString, fromString) import Data.String.Conversions (cs) import Data.Text (Text)@@ -69,17 +73,25 @@ } deriving (Eq, Show, Read, Generic, Hashable, NFData) -instance Serialize Block where- get = do- header <- get- (VarInt c) <- get- txs <- replicateM (fromIntegral c) get+instance Serial Block where+ deserialize = do+ header <- deserialize+ (VarInt c) <- deserialize+ txs <- replicateM (fromIntegral c) deserialize return $ Block header txs- put (Block h txs) = do- put h+ serialize (Block h txs) = do+ serialize h putVarInt $ length txs- forM_ txs put+ forM_ txs serialize +instance Serialize Block where+ get = deserialize+ put = serialize++instance Binary Block where+ get = deserialize+ put = serialize+ instance ToJSON Block where toJSON (Block h t) = object ["header" .= h, "transactions" .= t] toEncoding (Block h t) = pairs $ "header" .= h <> "transactions" .= t@@ -92,8 +104,16 @@ -- | Block header hash. To be serialized reversed for display purposes. newtype BlockHash = BlockHash { getBlockHash :: Hash256- } deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)+ } deriving (Eq, Ord, Generic, Hashable, Serial, NFData) +instance Serialize BlockHash where+ put = serialize+ get = deserialize++instance Binary BlockHash where+ put = serialize+ get = deserialize+ instance Show BlockHash where showsPrec _ = shows . blockHashToHex @@ -115,19 +135,21 @@ toJSON = String . blockHashToHex toEncoding h = unsafeToEncoding $- char7 '"' <> hexBuilder (B.reverse (encode h)) <> char7 '"'+ char7 '"' <>+ hexBuilder (BL.reverse (runPutL (serialize h))) <>+ char7 '"' -- | Block hashes are reversed with respect to the in-memory byte order in a -- block hash when displayed. blockHashToHex :: BlockHash -> Text-blockHashToHex (BlockHash h) = encodeHex (B.reverse (encode h))+blockHashToHex (BlockHash h) = encodeHex (B.reverse (runPutS (serialize h))) -- | Convert a human-readable hex block hash into a 'BlockHash'. Bytes are -- reversed as normal. hexToBlockHash :: Text -> Maybe BlockHash hexToBlockHash hex = do bs <- B.reverse <$> decodeHex hex- h <- eitherToMaybe (decode bs)+ h <- eitherToMaybe (runGetS deserialize bs) return $ BlockHash h -- | Data type recording information of a 'Block'. The hash of a block is@@ -157,7 +179,7 @@ object [ "version" .= v , "prevblock" .= p- , "merkleroot" .= encodeHex (encode m)+ , "merkleroot" .= encodeHex (runPutS (serialize m)) , "timestamp" .= t , "bits" .= b , "nonce" .= n@@ -166,7 +188,7 @@ pairs ( "version" .= v <> "prevblock" .= p- <> "merkleroot" .= encodeHex (encode m)+ <> "merkleroot" .= encodeHex (runPutS (serialize m)) <> "timestamp" .= t <> "bits" .= b <> "nonce" .= n@@ -182,13 +204,17 @@ <*> o .: "bits" <*> o .: "nonce" where- f = maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex+ f = maybe+ mzero+ return .+ (eitherToMaybe . runGetS deserialize =<<) .+ decodeHex -instance Serialize BlockHeader where- get = do+instance Serial BlockHeader where+ deserialize = do v <- getWord32le- p <- get- m <- get+ p <- deserialize+ m <- deserialize t <- getWord32le b <- getWord32le n <- getWord32le@@ -201,17 +227,25 @@ , blockBits = b , bhNonce = n }- put (BlockHeader v p m bt bb n) = do+ serialize (BlockHeader v p m bt bb n) = do putWord32le v- put p- put m+ serialize p+ serialize m putWord32le bt putWord32le bb putWord32le n +instance Binary BlockHeader where+ put = serialize+ get = deserialize++instance Serialize BlockHeader where+ put = serialize+ get = deserialize+ -- | Compute hash of 'BlockHeader'. headerHash :: BlockHeader -> BlockHash-headerHash = BlockHash . doubleSHA256 . encode+headerHash = BlockHash . doubleSHA256 . runPutS . serialize -- | A block locator is a set of block headers, denser towards the best block -- and sparser towards the genesis block. It starts at the highest block known.@@ -236,18 +270,26 @@ } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize GetBlocks where- get = GetBlocks <$> getWord32le <*> (repList =<< get) <*> get+instance Serial GetBlocks where+ deserialize =+ GetBlocks+ <$> getWord32le+ <*> (repList =<< deserialize)+ <*> deserialize where- repList (VarInt c) = replicateM (fromIntegral c) get- put (GetBlocks v xs h) = putGetBlockMsg v xs h+ repList (VarInt c) = replicateM (fromIntegral c) deserialize+ serialize (GetBlocks v xs h) = putGetBlockMsg v xs h -putGetBlockMsg :: Word32 -> BlockLocator -> BlockHash -> Put+instance Serialize GetBlocks where+ put = serialize+ get = deserialize++putGetBlockMsg :: MonadPut m => Word32 -> BlockLocator -> BlockHash -> m () putGetBlockMsg v xs h = do putWord32le v putVarInt $ length xs- forM_ xs put- put h+ forM_ xs serialize+ serialize h -- | Similar to the 'GetBlocks' message type but for retrieving block headers -- only. The response to a 'GetHeaders' request is a 'Headers' message@@ -263,12 +305,24 @@ } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize GetHeaders where- get = GetHeaders <$> getWord32le <*> (repList =<< get) <*> get+instance Serial GetHeaders where+ deserialize =+ GetHeaders+ <$> getWord32le+ <*> (repList =<< deserialize)+ <*> deserialize where- repList (VarInt c) = replicateM (fromIntegral c) get- put (GetHeaders v xs h) = putGetBlockMsg v xs h+ repList (VarInt c) = replicateM (fromIntegral c) deserialize+ serialize (GetHeaders v xs h) = putGetBlockMsg v xs h +instance Serialize GetHeaders where+ put = serialize+ get = deserialize++instance Binary GetHeaders where+ put = serialize+ get = deserialize+ -- | 'BlockHeader' type with a transaction count as 'VarInt' type BlockHeaderCount = (BlockHeader, VarInt) @@ -279,14 +333,22 @@ headersList :: [BlockHeaderCount] } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize Headers where- get = Headers <$> (repList =<< get)+instance Serial Headers where+ deserialize = Headers <$> (repList =<< deserialize) where repList (VarInt c) = replicateM (fromIntegral c) action- action = liftM2 (,) get get- put (Headers xs) = do+ action = liftM2 (,) deserialize deserialize+ serialize (Headers xs) = do putVarInt $ length xs- forM_ xs $ \(a, b) -> put a >> put b+ forM_ xs $ \(a, b) -> serialize a >> serialize b++instance Serialize Headers where+ put = serialize+ get = deserialize++instance Binary Headers where+ put = serialize+ get = deserialize -- | Decode the compact number used in the difficulty target of a block. --
src/Haskoin/Block/Headers.hs view
@@ -75,20 +75,21 @@ import Control.Monad.State.Strict as State (StateT, get, gets, lift, modify) import Control.Monad.Trans.Maybe+import Data.Binary (Binary (..)) import Data.Bits (shiftL, shiftR, (.&.)) import qualified Data.ByteString as B import Data.ByteString.Short (ShortByteString, fromShort, toShort)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Function (on)-import Data.Hashable import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap+import Data.Hashable import Data.List (sort, sortBy) import Data.Maybe (fromMaybe, listToMaybe)-import Data.Serialize as S (Serialize (..), decode,- encode, get, put)-import Data.Serialize.Get as S-import Data.Serialize.Put as S+import Data.Serialize (Serialize (..)) import Data.Typeable (Typeable) import Data.Word (Word32, Word64) import GHC.Generics (Generic)@@ -123,26 +124,34 @@ } deriving (Show, Read, Generic, Hashable, NFData) -instance Serialize BlockNode where- get = do- nodeHeader <- S.get+instance Serial BlockNode where+ deserialize = do+ nodeHeader <- deserialize nodeHeight <- getWord32le- nodeWork <- S.get+ nodeWork <- getInteger if nodeHeight == 0 then do let nodeSkip = headerHash nodeHeader return BlockNode {..} else do- nodeSkip <- S.get+ nodeSkip <- deserialize return BlockNode {..}- put bn = do- put $ nodeHeader bn+ serialize bn = do+ serialize $ nodeHeader bn putWord32le $ nodeHeight bn- put $ nodeWork bn+ putInteger $ nodeWork bn case nodeHeight bn of 0 -> return ()- _ -> put $ nodeSkip bn+ _ -> serialize $ nodeSkip bn +instance Serialize BlockNode where+ put = serialize+ get = deserialize++instance Binary BlockNode where+ put = serialize+ get = deserialize+ instance Eq BlockNode where (==) = (==) `on` nodeHeader @@ -187,7 +196,7 @@ genesisMap net = HashMap.singleton (shortBlockHash (headerHash (getGenesisHeader net)))- (toShort (encode (genesisNode net)))+ (toShort (runPutS (serialize (genesisNode net)))) -- | Add block header to memory block map. addBlockHeaderMemory :: BlockNode -> HeaderMemory -> HeaderMemory@@ -199,20 +208,21 @@ getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode getBlockHeaderMemory bh HeaderMemory {..} = do bs <- shortBlockHash bh `HashMap.lookup` memoryHeaderMap- eitherToMaybe . decode $ fromShort bs+ eitherToMaybe . runGetS deserialize $ fromShort bs -- | Calculate short block hash taking eight non-zero bytes from the 16-byte -- hash. This function will take the bytes that are not on the zero-side of the -- hash, making colissions between short block hashes difficult. shortBlockHash :: BlockHash -> ShortBlockHash-shortBlockHash = either error id . decode . B.take 8 . encode+shortBlockHash =+ either error id . runGetS deserialize . B.take 8 . runPutS . serialize -- | Add a block to memory-based block map. addBlockToMap :: BlockNode -> BlockMap -> BlockMap addBlockToMap node = HashMap.insert (shortBlockHash $ headerHash $ nodeHeader node)- (toShort $ encode node)+ (toShort $ runPutS $ serialize node) -- | Get the ancestor of the provided 'BlockNode' at the specified -- 'BlockHeight'.@@ -635,7 +645,7 @@ middleBlock a b = getAncestor h b >>= \case Nothing -> error "You fell into a pit full of mud and snakes"- Just x -> return x+ Just x -> return x where h = middleOf (nodeHeight a) (nodeHeight b) @@ -797,7 +807,7 @@ -- | Returns the proof of work of a block header hash as an 'Integer' number. blockPOW :: BlockHash -> Integer-blockPOW = bsToInteger . B.reverse . encode+blockPOW = bsToInteger . B.reverse . runPutS . serialize -- | Returns the work represented by this block. Work is defined as the number -- of tries needed to solve a block in the average case with respect to the@@ -867,7 +877,7 @@ bh' = mineBlock net seed bh { prevBlock = headerHash bh -- Just to make it different in every header- , merkleRoot = sha256 $ encode seed+ , merkleRoot = sha256 $ runPutS $ serialize seed } -- | Find the last common block ancestor between provided block headers.
src/Haskoin/Block/Merkle.hs view
@@ -35,14 +35,16 @@ import Control.DeepSeq import Control.Monad (forM_, replicateM, when)+import Data.Binary (Binary (..)) import Data.Bits import qualified Data.ByteString as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either (isRight) import Data.Hashable import Data.Maybe-import Data.Serialize (Serialize, encode, get, put)-import Data.Serialize.Get (getWord32le, getWord8)-import Data.Serialize.Put (putWord32le, putWord8)+import Data.Serialize (Serialize (..)) import Data.Word (Word32, Word8) import GHC.Generics import Haskoin.Block.Common@@ -74,26 +76,33 @@ , mFlags :: !FlagBits } deriving (Eq, Show, Read, Generic, Hashable, NFData) -instance Serialize MerkleBlock where-- get = do- header <- get+instance Serial MerkleBlock where+ deserialize = do+ header <- deserialize ntx <- getWord32le- (VarInt matchLen) <- get- hashes <- replicateM (fromIntegral matchLen) get- (VarInt flagLen) <- get+ (VarInt matchLen) <- deserialize+ hashes <- replicateM (fromIntegral matchLen) deserialize+ (VarInt flagLen) <- deserialize ws <- replicateM (fromIntegral flagLen) getWord8 return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws) - put (MerkleBlock h ntx hashes flags) = do- put h+ serialize (MerkleBlock h ntx hashes flags) = do+ serialize h putWord32le ntx putVarInt $ length hashes- forM_ hashes put+ forM_ hashes serialize let ws = encodeMerkleFlags flags putVarInt $ length ws forM_ ws putWord8 +instance Binary MerkleBlock where+ put = serialize+ get = deserialize++instance Serialize MerkleBlock where+ put = serialize+ get = deserialize+ -- | Unpack Merkle flags into 'FlagBits' structure. decodeMerkleFlags :: [Word8] -> FlagBits decodeMerkleFlags ws =@@ -126,7 +135,7 @@ -- | Concatenate and compute double SHA256. hash2 :: Hash256 -> Hash256 -> Hash256-hash2 a b = doubleSHA256 $ encode a `BS.append` encode b+hash2 a b = doubleSHA256 $ runPutS (serialize a) <> runPutS (serialize b) -- | Computes the hash of a specific node in a Merkle tree. calcHash :: Int -- ^ height of the node
src/Haskoin/Constants.hs view
@@ -28,10 +28,14 @@ ) where import Control.DeepSeq+import Data.Binary (Binary (..)) import Data.ByteString (ByteString)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.List import Data.Maybe-import Data.Serialize+import Data.Serialize (Serialize (..)) import Data.String import Data.Text (Text) import Data.Word (Word32, Word64, Word8)@@ -120,14 +124,22 @@ , getHalvingInterval :: !Word32 } deriving (Eq, Generic, NFData) -instance Serialize Network where- put net =+instance Serial Network where+ serialize net = putWord32be $ getNetworkMagic net- get = do+ deserialize = do magic <- getWord32be case find ((== magic) . getNetworkMagic) allNets of Nothing -> fail $ "Network magic unknown: " <> show magic Just net -> return net++instance Binary Network where+ put = serialize+ get = deserialize++instance Serialize Network where+ put = serialize+ get = deserialize instance Show Network where show = getNetworkIdent
src/Haskoin/Crypto/Hash.hs view
@@ -34,17 +34,19 @@ import Crypto.Hash (RIPEMD160 (..), SHA1 (..), SHA256 (..), SHA512 (..), hashWith) import Crypto.MAC.HMAC (HMAC, hmac)+import Data.Binary (Binary (..)) import Data.ByteArray (ByteArrayAccess) import qualified Data.ByteArray as BA import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.ByteString.Short (ShortByteString) import qualified Data.ByteString.Short as BSS+import qualified Data.Bytes.Get as Get+import qualified Data.Bytes.Put as Put+import Data.Bytes.Serial (Serial (..)) import Data.Either (fromRight) import Data.Hashable (Hashable)-import Data.Serialize (Serialize (..), decode)-import qualified Data.Serialize.Get as Get-import qualified Data.Serialize.Put as Put+import Data.Serialize (Serialize (..)) import Data.String (IsString, fromString) import Data.String.Conversions (cs) import Data.Word (Word32)@@ -55,8 +57,16 @@ -- | 'Word32' wrapped for type-safe 32-bit checksums. newtype CheckSum32 = CheckSum32 { getCheckSum32 :: Word32- } deriving (Eq, Ord, Serialize, Show, Read, Hashable, Generic, NFData)+ } deriving (Eq, Ord, Serial, Show, Read, Hashable, Generic, NFData) +instance Serialize CheckSum32 where+ put = serialize+ get = deserialize++instance Binary CheckSum32 where+ put = serialize+ get = deserialize+ -- | Type for 512-bit hashes. newtype Hash512 = Hash512 { getHash512 :: ShortByteString } deriving (Eq, Ord, Hashable, Generic, NFData)@@ -104,10 +114,18 @@ where e = error "Could not decode hash from hex string" +instance Serial Hash512 where+ deserialize = Hash512 . BSS.toShort <$> Get.getByteString 64+ serialize = Put.putByteString . BSS.fromShort . getHash512+ instance Serialize Hash512 where- get = Hash512 <$> Get.getShortByteString 64- put = Put.putShortByteString . getHash512+ put = serialize+ get = deserialize +instance Binary Hash512 where+ put = serialize+ get = deserialize+ instance IsString Hash256 where fromString str = case decodeHex $ cs str of@@ -119,10 +137,18 @@ where e = error "Could not decode hash from hex string" +instance Serial Hash256 where+ deserialize = Hash256 . BSS.toShort <$> Get.getByteString 32+ serialize = Put.putByteString . BSS.fromShort . getHash256+ instance Serialize Hash256 where- get = Hash256 <$> Get.getShortByteString 32- put = Put.putShortByteString . getHash256+ put = serialize+ get = deserialize +instance Binary Hash256 where+ put = serialize+ get = deserialize+ instance IsString Hash160 where fromString str = case decodeHex $ cs str of@@ -134,10 +160,18 @@ where e = error "Could not decode hash from hex string" +instance Serial Hash160 where+ deserialize = Hash160 . BSS.toShort <$> Get.getByteString 20+ serialize = Put.putByteString . BSS.fromShort . getHash160+ instance Serialize Hash160 where- get = Hash160 <$> Get.getShortByteString 20- put = Put.putShortByteString . getHash160+ put = serialize+ get = deserialize +instance Binary Hash160 where+ put = serialize+ get = deserialize+ -- | Calculate SHA512 hash. sha512 :: ByteArrayAccess b => b -> Hash512 sha512 = Hash512 . BSS.toShort . BA.convert . hashWith SHA512@@ -169,7 +203,7 @@ -- | Computes a 32 bit checksum. checkSum32 :: ByteArrayAccess b => b -> CheckSum32 checkSum32 = fromRight (error "Could not decode bytes as CheckSum32")- . decode+ . Get.runGetS deserialize . BS.take 4 . BA.convert . hashWith SHA256
src/Haskoin/Crypto/Signature.hs view
@@ -23,17 +23,21 @@ import Control.Monad (guard, unless, when) import Crypto.Secp256k1+import Data.Binary (Binary (..)) import Data.ByteString (ByteString) import qualified Data.ByteString as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Maybe (fromMaybe, isNothing)-import Data.Serialize as S+import Data.Serialize (Serialize (..)) import Haskoin.Crypto.Hash import Numeric (showHex) -- | Convert 256-bit hash into a 'Msg' for signing or verification. hashToMsg :: Hash256 -> Msg hashToMsg =- fromMaybe e . msg . encode+ fromMaybe e . msg . runPutS . serialize where e = error "Could not convert 32-byte hash to secp256k1 message" @@ -48,7 +52,7 @@ norm = fromMaybe s (normalizeSig s) -- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.-getSig :: Get Sig+getSig :: MonadGet m => m Sig getSig = do l <- lookAhead $ do@@ -67,7 +71,7 @@ Nothing -> fail "Invalid signature" -- | Serialize an ECDSA signature for Bitcoin use.-putSig :: Putter Sig+putSig :: MonadPut m => Sig -> m () putSig s = putByteString $ exportSig s -- | Is canonical half order.
src/Haskoin/Keys/Common.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-|@@ -40,14 +41,16 @@ import Data.Aeson (FromJSON, ToJSON (..), Value (String), parseJSON, withText) import Data.Aeson.Encoding (unsafeToEncoding)+import Data.Binary (Binary (..)) import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.ByteString.Builder (char7)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Hashable import Data.Maybe (fromMaybe)-import Data.Serialize (Serialize, decode, encode, get, put)-import Data.Serialize.Get (getByteString)-import Data.Serialize.Put (putByteString)+import Data.Serialize (Serialize (..)) import Data.String (IsString, fromString) import Data.String.Conversions (cs) import GHC.Generics (Generic)@@ -64,32 +67,50 @@ instance IsString PubKeyI where fromString str =- fromMaybe e $ eitherToMaybe . decode <=< decodeHex $ cs str+ fromMaybe e $ eitherToMaybe . runGetS deserialize <=< decodeHex $ cs str where e = error "Could not decode public key" instance ToJSON PubKeyI where- toJSON = String . encodeHex . encode- toEncoding s = unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'+ toJSON = String . encodeHex . runPutS . serialize+ toEncoding s = unsafeToEncoding $+ char7 '"' <>+ hexBuilder (runPutL (serialize s)) <>+ char7 '"' instance FromJSON PubKeyI where parseJSON = withText "PubKeyI" $- maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex+ maybe mzero return . (eitherToMaybe . runGetS deserialize =<<) . decodeHex -instance Serialize PubKeyI where- get = c <|> u+instance Serial PubKeyI where+ deserialize = s >>= \case+ True -> c+ False -> u where+ s = lookAhead $ getWord8 >>= \case+ 0x02 -> return True+ 0x03 -> return True+ 0x04 -> return False+ _ -> fail "Not a public key" c = do bs <- getByteString 33- guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]- maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure True+ maybe (fail "Could not decode public key") return $+ PubKeyI <$> importPubKey bs <*> pure True u = do bs <- getByteString 65- guard $ BS.head bs == 0x04- maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure False+ maybe (fail "Could not decode public key") return $+ PubKeyI <$> importPubKey bs <*> pure False - put pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)+ serialize pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk) +instance Serialize PubKeyI where+ put = serialize+ get = deserialize++instance Binary PubKeyI where+ put = serialize+ get = deserialize+ -- | Wrap a public key from secp256k1 library adding information about compression. wrapPubKey :: Bool -> PubKey -> PubKeyI wrapPubKey c p = PubKeyI p c@@ -101,7 +122,7 @@ -- | Tweak a public key. tweakPubKey :: PubKey -> Hash256 -> Maybe PubKey-tweakPubKey p h = tweakAddPubKey p =<< tweak (encode h)+tweakPubKey p h = tweakAddPubKey p =<< tweak (runPutS (serialize h)) -- | Elliptic curve private key type with expected public key compression -- information. Compression information is stored in private key WIF formats and@@ -118,15 +139,15 @@ -- | Tweak a private key. tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey-tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)+tweakSecKey key h = tweakAddSecKey key =<< tweak (runPutS (serialize h)) -- | Decode Casascius mini private keys (22 or 30 characters). fromMiniKey :: ByteString -> Maybe SecKeyI fromMiniKey bs = do guard checkShortKey- wrapSecKey False <$> secKey (encode (sha256 bs))+ wrapSecKey False <$> secKey (runPutS (serialize (sha256 bs))) where- checkHash = encode $ sha256 $ bs `BS.append` "?"+ checkHash = runPutS $ serialize $ sha256 $ bs `BS.append` "?" checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00 -- | Decode private key from WIF (wallet import format) string.
src/Haskoin/Keys/Extended.hs view
@@ -111,18 +111,20 @@ toJSON, withText) import Data.Aeson.Encoding (Encoding, text) import Data.Aeson.Types (Parser)+import Data.Binary (Binary (get, put)) import Data.Bits (clearBit, setBit, testBit) import Data.ByteString (ByteString) import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either (fromRight) import Data.Hashable import Data.List (foldl') import Data.List.Split (splitOn) import Data.Maybe (fromMaybe)-import Data.Serialize as S (Serialize, decode, encode, get,- put)-import Data.Serialize.Get (Get, getWord32be, getWord8, runGet)-import Data.Serialize.Put (Putter, putWord32be, putWord8, runPut)+import Data.Serialize (Serialize (..))+import qualified Data.Serialize as S import Data.String (IsString, fromString) import Data.String.Conversions (cs) import Data.Typeable (Typeable)@@ -165,20 +167,28 @@ , xPrvKey :: !SecKey -- ^ private key of this node } deriving (Generic, Eq, Show, Read, NFData, Hashable) -instance Serialize XPrvKey where- put k = do+instance Serial XPrvKey where+ serialize k = do putWord8 $ xPrvDepth k putWord32be $ xPrvParent k putWord32be $ xPrvIndex k- put $ xPrvChain k+ serialize $ xPrvChain k putPadPrvKey $ xPrvKey k- get =+ deserialize = XPrvKey <$> getWord8 <*> getWord32be <*> getWord32be- <*> S.get+ <*> deserialize <*> getPadPrvKey +instance Binary XPrvKey where+ put = serialize+ get = deserialize++instance Serialize XPrvKey where+ put = serialize+ get = deserialize+ xPrvToJSON :: Network -> XPrvKey -> Value xPrvToJSON net = A.String . xPrvExport net @@ -202,20 +212,28 @@ , xPubKey :: !PubKey -- ^ public key of this node } deriving (Generic, Eq, Show, Read, NFData, Hashable) -instance Serialize XPubKey where- put k = do+instance Serial XPubKey where+ serialize k = do putWord8 $ xPubDepth k putWord32be $ xPubParent k putWord32be $ xPubIndex k- put $ xPubChain k- put $ wrapPubKey True (xPubKey k)- get =+ serialize $ xPubChain k+ serialize $ wrapPubKey True (xPubKey k)+ deserialize = XPubKey <$> getWord8 <*> getWord32be <*> getWord32be- <*> S.get- <*> (pubKeyPoint <$> S.get)+ <*> deserialize+ <*> (pubKeyPoint <$> deserialize) +instance Serialize XPubKey where+ put = serialize+ get = deserialize++instance Binary XPubKey where+ put = serialize+ get = deserialize+ -- | Decode an extended public key from a JSON string xPubFromJSON :: Network -> Value -> Parser XPubKey xPubFromJSON net =@@ -238,7 +256,7 @@ XPrvKey 0 0 0 c k where (p, c) = split512 $ hmac512 "Bitcoin seed" bs- k = fromMaybe err (secKey (encode p))+ k = fromMaybe err (secKey (runPutS (serialize p))) err = throw $ DerivationException "Invalid seed" -- | Derive an extended public key from an extended private key. This function@@ -265,8 +283,8 @@ | otherwise = error "Invalid child derivation index" where pK = xPubKey $ deriveXPubKey xkey- m = B.append (exportPubKey True pK) (encode child)- (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m+ m = B.append (exportPubKey True pK) (runPutS (serialize child))+ (a, c) = split512 $ hmac512 (runPutS $ serialize $ xPrvChain xkey) m k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a err = throw $ DerivationException "Invalid prvSubKey derivation" @@ -280,8 +298,8 @@ XPubKey (xPubDepth xKey + 1) (xPubFP xKey) child c pK | otherwise = error "Invalid child derivation index" where- m = B.append (exportPubKey True (xPubKey xKey)) (encode child)- (a, c) = split512 $ hmac512 (encode $ xPubChain xKey) m+ m = B.append (exportPubKey True (xPubKey xKey)) (runPutS $ serialize child)+ (a, c) = split512 $ hmac512 (runPutS $ serialize $ xPubChain xKey) m pK = fromMaybe err $ tweakPubKey (xPubKey xKey) a err = throw $ DerivationException "Invalid pubSubKey derivation" @@ -300,8 +318,8 @@ | otherwise = error "Invalid child derivation index" where i = setBit child 31- m = B.append (bsPadPrvKey $ xPrvKey xkey) (encode i)- (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m+ m = B.append (bsPadPrvKey $ xPrvKey xkey) (runPutS $ serialize i)+ (a, c) = split512 $ hmac512 (runPutS $ serialize $ xPrvChain xkey) m k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a err = throw $ DerivationException "Invalid hardSubKey derivation" @@ -331,19 +349,19 @@ -- | Computes the key identifier of an extended public key. xPubID :: XPubKey -> Hash160-xPubID = ripemd160 . encode . sha256 . exportPubKey True . xPubKey+xPubID = ripemd160 . runPutS . serialize . sha256 . exportPubKey True . xPubKey -- | Computes the key fingerprint of an extended private key. xPrvFP :: XPrvKey -> Fingerprint xPrvFP =- fromRight err . decode . B.take 4 . encode . xPrvID+ fromRight err . runGetS deserialize . B.take 4 . runPutS . serialize . xPrvID where err = error "Could not decode xPrvFP" -- | Computes the key fingerprint of an extended public key. xPubFP :: XPubKey -> Fingerprint xPubFP =- fromRight err . decode . B.take 4 . encode . xPubID+ fromRight err . runGetS deserialize . B.take 4 . runPutS . serialize . xPubID where err = error "Could not decode xPubFP" @@ -363,53 +381,53 @@ -- | Exports an extended private key to the BIP32 key export format ('Base58'). xPrvExport :: Network -> XPrvKey -> Base58-xPrvExport net = encodeBase58Check . runPut . putXPrvKey net+xPrvExport net = encodeBase58Check . runPutS . putXPrvKey net -- | Exports an extended public key to the BIP32 key export format ('Base58'). xPubExport :: Network -> XPubKey -> Base58-xPubExport net = encodeBase58Check . runPut . putXPubKey net+xPubExport net = encodeBase58Check . runPutS . putXPubKey net -- | Decodes a BIP32 encoded extended private key. This function will fail if -- invalid base 58 characters are detected or if the checksum fails. xPrvImport :: Network -> Base58 -> Maybe XPrvKey-xPrvImport net = eitherToMaybe . runGet (getXPrvKey net) <=< decodeBase58Check+xPrvImport net = eitherToMaybe . runGetS (getXPrvKey net) <=< decodeBase58Check -- | Decodes a BIP32 encoded extended public key. This function will fail if -- invalid base 58 characters are detected or if the checksum fails. xPubImport :: Network -> Base58 -> Maybe XPubKey-xPubImport net = eitherToMaybe . runGet (getXPubKey net) <=< decodeBase58Check+xPubImport net = eitherToMaybe . runGetS (getXPubKey net) <=< decodeBase58Check -- | Export an extended private key to WIF (Wallet Import Format). xPrvWif :: Network -> XPrvKey -> Base58 xPrvWif net xkey = toWif net (wrapSecKey True (xPrvKey xkey)) -- | Parse a binary extended private key.-getXPrvKey :: Network -> Get XPrvKey+getXPrvKey :: MonadGet m => Network -> m XPrvKey getXPrvKey net = do ver <- getWord32be unless (ver == getExtSecretPrefix net) $ fail "Get: Invalid version for extended private key"- S.get+ deserialize -- | Serialize an extended private key.-putXPrvKey :: Network -> Putter XPrvKey+putXPrvKey :: MonadPut m => Network -> XPrvKey -> m () putXPrvKey net k = do putWord32be $ getExtSecretPrefix net- put k+ serialize k -- | Parse a binary extended public key.-getXPubKey :: Network -> Get XPubKey+getXPubKey :: MonadGet m => Network -> m XPubKey getXPubKey net = do ver <- getWord32be unless (ver == getExtPubKeyPrefix net) $ fail "Get: Invalid version for extended public key"- S.get+ deserialize -- | Serialize an extended public key.-putXPubKey :: Network -> Putter XPubKey+putXPubKey :: MonadPut m => Network -> XPubKey -> m () putXPubKey net k = do putWord32be $ getExtPubKeyPrefix net- put k+ serialize k {- Derivation helpers -} @@ -580,18 +598,54 @@ Deriv `compare` _ = LT _ `compare` Deriv = GT +instance Serial DerivPath where+ deserialize = listToPath <$> getList getWord32be+ serialize = putList putWord32be . pathToList+ instance Serialize DerivPath where- get = listToPath <$> S.get- put = put . pathToList+ put = serialize+ get = deserialize +instance Binary DerivPath where+ put = serialize+ get = deserialize++instance Serial HardPath where+ deserialize =+ maybe+ (fail "Could not decode hard path")+ return .+ toHard .+ listToPath =<<+ getList getWord32be+ serialize = putList putWord32be . pathToList+ instance Serialize HardPath where- get = maybe mzero return . toHard . listToPath =<< S.get- put = put . pathToList+ put = serialize+ get = deserialize +instance Binary HardPath where+ put = serialize+ get = deserialize++instance Serial SoftPath where+ deserialize =+ maybe+ (fail "Could not decode soft path")+ return .+ toSoft .+ listToPath =<<+ getList getWord32be+ serialize = putList putWord32be . pathToList+ instance Serialize SoftPath where- get = maybe mzero return . toSoft . listToPath =<< S.get- put = put . pathToList+ put = serialize+ get = deserialize +instance Binary SoftPath where+ put = serialize+ get = deserialize+ -- | Get a list of derivation indices from a derivation path. pathToList :: DerivPathI t -> [KeyIndex] pathToList =@@ -927,15 +981,18 @@ {- Utilities for extended keys -} -- | De-serialize HDW-specific private key.-getPadPrvKey :: Get SecKey+getPadPrvKey :: MonadGet m => m SecKey getPadPrvKey = do pad <- getWord8 unless (pad == 0x00) $ fail "Private key must be padded with 0x00"- S.get+ bs <- getByteString 32+ case runGetS S.get bs of+ Left e -> fail e+ Right x -> return x -- | Serialize HDW-specific private key.-putPadPrvKey :: Putter SecKey-putPadPrvKey p = putWord8 0x00 >> S.put p+putPadPrvKey :: MonadPut m => SecKey -> m ()+putPadPrvKey p = putWord8 0x00 >> putByteString (runPutS (S.put p)) bsPadPrvKey :: SecKey -> ByteString-bsPadPrvKey = runPut . putPadPrvKey+bsPadPrvKey = runPutS . putPadPrvKey
src/Haskoin/Network/Bloom.hs view
@@ -31,18 +31,18 @@ import Control.DeepSeq import Control.Monad (forM_, replicateM)+import Data.Binary (Binary (..)) import Data.Bits import Data.ByteString (ByteString) import qualified Data.ByteString as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import qualified Data.Foldable as F import Data.Hash.Murmur (murmur3) import Data.List (foldl') import qualified Data.Sequence as S-import Data.Serialize (Serialize, encode, get, put)-import Data.Serialize.Get (getByteString, getWord32le,- getWord8)-import Data.Serialize.Put (putByteString, putWord32le,- putWord8)+import Data.Serialize (Serialize (..)) import Data.Word import GHC.Generics (Generic) import Haskoin.Network.Common@@ -74,19 +74,27 @@ -- ^ auto-update on pay-to-pubkey or pay-to-multisig (default) deriving (Eq, Show, Read, Generic, NFData) -instance Serialize BloomFlags where- get = go =<< getWord8+instance Serial BloomFlags where+ deserialize = go =<< getWord8 where go 0 = return BloomUpdateNone go 1 = return BloomUpdateAll go 2 = return BloomUpdateP2PubKeyOnly go _ = fail "BloomFlags get: Invalid bloom flag" - put f = putWord8 $ case f of+ serialize f = putWord8 $ case f of BloomUpdateNone -> 0 BloomUpdateAll -> 1 BloomUpdateP2PubKeyOnly -> 2 +instance Binary BloomFlags where+ get = deserialize+ put = serialize++instance Serialize BloomFlags where+ get = deserialize+ put = serialize+ -- | A bloom filter is a probabilistic data structure that SPV clients send to -- other peers to filter the set of transactions received from them. Bloom -- filters can have false positives but not false negatives. Some transactions@@ -105,45 +113,72 @@ } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize BloomFilter where+instance Serial BloomFilter where - get = BloomFilter <$> (S.fromList <$> (readDat =<< get))- <*> getWord32le <*> getWord32le- <*> get+ deserialize =+ BloomFilter+ <$> (S.fromList <$> (readDat =<< deserialize))+ <*> getWord32le+ <*> getWord32le+ <*> deserialize where readDat (VarInt len) = replicateM (fromIntegral len) getWord8 - put (BloomFilter dat hashFuncs tweak flags) = do+ serialize (BloomFilter dat hashFuncs tweak flags) = do putVarInt $ S.length dat forM_ (F.toList dat) putWord8 putWord32le hashFuncs putWord32le tweak- put flags+ serialize flags +instance Binary BloomFilter where+ put = serialize+ get = deserialize++instance Serialize BloomFilter where+ put = serialize+ get = deserialize+ -- | Set a new bloom filter on the peer connection. newtype FilterLoad = FilterLoad { filterLoadBloomFilter :: BloomFilter } deriving (Eq, Show, Read, Generic, NFData) +instance Serial FilterLoad where+ deserialize = FilterLoad <$> deserialize+ serialize (FilterLoad f) = serialize f++instance Binary FilterLoad where+ put = serialize+ get = deserialize+ instance Serialize FilterLoad where- get = FilterLoad <$> get- put (FilterLoad f) = put f+ put = serialize+ get = deserialize -- | Add the given data element to the connections current filter without -- requiring a completely new one to be set. newtype FilterAdd = FilterAdd { getFilterData :: ByteString } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize FilterAdd where- get = do- (VarInt len) <- get+instance Serial FilterAdd where+ deserialize = do+ (VarInt len) <- deserialize dat <- getByteString $ fromIntegral len return $ FilterAdd dat - put (FilterAdd bs) = do+ serialize (FilterAdd bs) = do putVarInt $ BS.length bs putByteString bs +instance Binary FilterAdd where+ put = serialize+ get = deserialize +instance Serialize FilterAdd where+ put = serialize+ get = deserialize++ -- | Build a bloom filter that will provide the given false positive rate when -- the given number of elements have been inserted. bloomCreate :: Int -- ^ number of elements@@ -235,14 +270,14 @@ (BloomUpdateAll, _) -> bloomInsert bf outpoint _ -> error "Error Updating Bloom Filter with relevant outpoint" where- outpoint = encode $ OutPoint {outPointHash = h, outPointIndex = id'}+ outpoint = runPutS $ serialize $ OutPoint {outPointHash = h, outPointIndex = id'} scriptType = (\s -> isPayPK s || isPayMulSig s) scriptOut -- Encodes a scriptOutput so it can be checked agains the Bloom Filter encodeScriptOut :: ScriptOutput -> ByteString- encodeScriptOut (PayMulSig outputMuSig _) = encode outputMuSig- encodeScriptOut (PayWitnessScriptHash scriptHash) = encode scriptHash- encodeScriptOut (DataCarrier getOutputDat) = encode getOutputDat- encodeScriptOut outputHash = (encode . getOutputHash) outputHash+ encodeScriptOut (PayMulSig outputMuSig _) = runPutS $ serialize outputMuSig+ encodeScriptOut (PayWitnessScriptHash scriptHash) = runPutS $ serialize scriptHash+ encodeScriptOut (DataCarrier getOutputDat) = runPutS $ serialize getOutputDat+ encodeScriptOut outputHash = (runPutS . serialize . getOutputHash) outputHash -- | Returns True if the filter is empty (all bytes set to 0x00) isBloomEmpty :: BloomFilter -> Bool
src/Haskoin/Network/Common.hs view
@@ -47,11 +47,15 @@ import Control.DeepSeq import Control.Monad (forM_, liftM2, replicateM, unless)+import Data.Binary (Binary (..)) import Data.Bits (shiftL) import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.ByteString.Char8 as C (replicate)-import Data.Serialize as S+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..)) import Data.String import Data.String.Conversions (cs) import Data.Word (Word32, Word64)@@ -71,17 +75,25 @@ } deriving (Eq, Show, Generic, NFData) -instance Serialize Addr where+instance Serial Addr where - get = Addr <$> (repList =<< S.get)+ deserialize = Addr <$> (repList =<< deserialize) where repList (VarInt c) = replicateM (fromIntegral c) action- action = liftM2 (,) getWord32le S.get+ action = liftM2 (,) getWord32le deserialize - put (Addr xs) = do+ serialize (Addr xs) = do putVarInt $ length xs- forM_ xs $ \(a,b) -> putWord32le a >> put b+ forM_ xs $ \(a,b) -> putWord32le a >> serialize b +instance Binary Addr where+ get = deserialize+ put = serialize++instance Serialize Addr where+ get = deserialize+ put = serialize+ -- | Data type describing signed messages that can be sent between bitcoin -- nodes to display important notifications to end users about the health of -- the network.@@ -93,9 +105,17 @@ , alertSignature :: !VarString } deriving (Eq, Show, Read, Generic, NFData) +instance Serial Alert where+ deserialize = Alert <$> deserialize <*> deserialize+ serialize (Alert p s) = serialize p >> serialize s++instance Binary Alert where+ put = serialize+ get = deserialize+ instance Serialize Alert where- get = Alert <$> S.get <*> S.get- put (Alert p s) = put p >> put s+ put = serialize+ get = deserialize -- | The 'GetData' type is used to retrieve information on a specific object -- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'@@ -109,16 +129,24 @@ getDataList :: [InvVector] } deriving (Eq, Show, Generic, NFData) -instance Serialize GetData where+instance Serial GetData where - get = GetData <$> (repList =<< S.get)+ deserialize = GetData <$> (repList =<< deserialize) where- repList (VarInt c) = replicateM (fromIntegral c) S.get+ repList (VarInt c) = replicateM (fromIntegral c) deserialize - put (GetData xs) = do+ serialize (GetData xs) = do putVarInt $ length xs- forM_ xs put+ forM_ xs serialize +instance Binary GetData where+ get = deserialize+ put = serialize++instance Serialize GetData where+ get = deserialize+ put = serialize+ -- | 'Inv' messages are used by nodes to advertise their knowledge of new -- objects by publishing a list of hashes to a peer. 'Inv' messages can be sent -- unsolicited or in response to a 'GetBlocks' message.@@ -128,16 +156,24 @@ invList :: [InvVector] } deriving (Eq, Show, Generic, NFData) -instance Serialize Inv where+instance Serial Inv where - get = Inv <$> (repList =<< S.get)+ deserialize = Inv <$> (repList =<< deserialize) where- repList (VarInt c) = replicateM (fromIntegral c) S.get+ repList (VarInt c) = replicateM (fromIntegral c) deserialize - put (Inv xs) = do+ serialize (Inv xs) = do putVarInt $ length xs- forM_ xs put+ forM_ xs serialize +instance Binary Inv where+ get = deserialize+ put = serialize++instance Serialize Inv where+ get = deserialize+ put = serialize+ -- | Data type identifying the type of an inventory vector. SegWit types are -- only used in 'GetData' messages, not 'Inv'. data InvType@@ -150,8 +186,8 @@ | InvWitnessMerkleBlock -- ^ segwit filtere block deriving (Eq, Show, Read, Generic, NFData) -instance Serialize InvType where- get = go =<< getWord32le+instance Serial InvType where+ deserialize = go =<< getWord32le where go x = case x of@@ -164,7 +200,7 @@ | x == 1 `shiftL` 30 + 2 -> return InvWitnessBlock | x == 1 `shiftL` 30 + 3 -> return InvWitnessMerkleBlock | otherwise -> fail "bitcoinGet InvType: Invalid Type"- put x =+ serialize x = putWord32le $ case x of InvError -> 0@@ -175,6 +211,14 @@ InvWitnessBlock -> 1 `shiftL` 30 + 2 InvWitnessMerkleBlock -> 1 `shiftL` 30 + 3 +instance Binary InvType where+ get = deserialize+ put = serialize++instance Serialize InvType where+ get = deserialize+ put = serialize+ -- | Invectory vectors represent hashes identifying objects such as a 'Block' or -- a 'Tx'. They notify other peers about new data or data they have otherwise -- requested.@@ -186,17 +230,33 @@ , invHash :: !Hash256 } deriving (Eq, Show, Generic, NFData) +instance Serial InvVector where+ deserialize = InvVector <$> deserialize <*> deserialize+ serialize (InvVector t h) = serialize t >> serialize h++instance Binary InvVector where+ get = deserialize+ put = serialize+ instance Serialize InvVector where- get = InvVector <$> S.get <*> S.get- put (InvVector t h) = put t >> put h+ get = deserialize+ put = serialize newtype HostAddress = HostAddress ByteString deriving (Eq, Show, Ord, Generic, NFData) +instance Serial HostAddress where+ serialize (HostAddress bs) = putByteString bs+ deserialize = HostAddress <$> getByteString 18++instance Binary HostAddress where+ get = deserialize+ put = serialize+ instance Serialize HostAddress where- put (HostAddress bs) = putByteString bs- get = HostAddress <$> getByteString 18+ get = deserialize+ put = serialize -- | Data type describing a bitcoin network address. Addresses are stored in -- IPv6 format. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6@@ -210,14 +270,14 @@ hostToSockAddr :: HostAddress -> SockAddr hostToSockAddr (HostAddress bs) =- case runGet getSockAddr bs of+ case runGetS getSockAddr bs of Left e -> error e Right x -> x sockToHostAddress :: SockAddr -> HostAddress-sockToHostAddress = HostAddress . runPut . putSockAddr+sockToHostAddress = HostAddress . runPutS . putSockAddr -putSockAddr :: SockAddr -> Put+putSockAddr :: MonadPut m => SockAddr -> m () putSockAddr (SockAddrInet6 p _ (a, b, c, d) _) = do putWord32be a putWord32be b@@ -234,7 +294,7 @@ putSockAddr _ = error "Invalid address type" -getSockAddr :: Get SockAddr+getSockAddr :: MonadGet m => m SockAddr getSockAddr = do a <- getWord32be b <- getWord32be@@ -249,9 +309,17 @@ p <- getWord16be return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0 +instance Serial NetworkAddress where+ deserialize = NetworkAddress <$> getWord64le <*> deserialize+ serialize (NetworkAddress s a) = putWord64le s >> serialize a++instance Binary NetworkAddress where+ get = deserialize+ put = serialize+ instance Serialize NetworkAddress where- get = NetworkAddress <$> getWord64le <*> S.get- put (NetworkAddress s a) = putWord64le s >> put a+ get = deserialize+ put = serialize -- | A 'NotFound' message is returned as a response to a 'GetData' message -- whe one of the requested objects could not be retrieved. This could happen,@@ -262,16 +330,24 @@ notFoundList :: [InvVector] } deriving (Eq, Show, Generic, NFData) -instance Serialize NotFound where+instance Serial NotFound where - get = NotFound <$> (repList =<< S.get)+ deserialize = NotFound <$> (repList =<< deserialize) where- repList (VarInt c) = replicateM (fromIntegral c) S.get+ repList (VarInt c) = replicateM (fromIntegral c) deserialize - put (NotFound xs) = do+ serialize (NotFound xs) = do putVarInt $ length xs- forM_ xs put+ forM_ xs serialize +instance Binary NotFound where+ get = deserialize+ put = serialize++instance Serialize NotFound where+ get = deserialize+ put = serialize+ -- | A 'Ping' message is sent to bitcoin peers to check if a connection is still -- open. newtype Ping =@@ -287,13 +363,29 @@ pongNonce :: Word64 } deriving (Eq, Show, Read, Generic, NFData) +instance Serial Ping where+ deserialize = Ping <$> getWord64le+ serialize (Ping n) = putWord64le n++instance Serial Pong where+ deserialize = Pong <$> getWord64le+ serialize (Pong n) = putWord64le n++instance Binary Ping where+ get = deserialize+ put = serialize++instance Binary Pong where+ get = deserialize+ put = serialize+ instance Serialize Ping where- get = Ping <$> getWord64le- put (Ping n) = putWord64le n+ get = deserialize+ put = serialize instance Serialize Pong where- get = Pong <$> getWord64le- put (Pong n) = putWord64le n+ get = deserialize+ put = serialize -- | The 'Reject' message is sent when messages are rejected by a peer. data Reject =@@ -320,9 +412,10 @@ | RejectCheckpoint deriving (Eq, Show, Read, Generic, NFData) -instance Serialize RejectCode where+instance Serial RejectCode where - get = getWord8 >>= \code -> case code of+ deserialize =+ getWord8 >>= \code -> case code of 0x01 -> return RejectMalformed 0x10 -> return RejectInvalid 0x11 -> return RejectObsolete@@ -332,11 +425,11 @@ 0x42 -> return RejectInsufficientFee 0x43 -> return RejectCheckpoint _ -> fail $ unwords- [ "Reject get: Invalid code"- , show code- ]+ [ "Reject get: Invalid code"+ , show code+ ] - put code = putWord8 $ case code of+ serialize code = putWord8 $ case code of RejectMalformed -> 0x01 RejectInvalid -> 0x10 RejectObsolete -> 0x11@@ -346,42 +439,61 @@ RejectInsufficientFee -> 0x42 RejectCheckpoint -> 0x43 +instance Binary RejectCode where+ put = serialize+ get = deserialize++instance Serialize RejectCode where+ put = serialize+ get = deserialize+ -- | Convenience function to build a 'Reject' message. reject :: MessageCommand -> RejectCode -> ByteString -> Reject reject cmd code reason = Reject cmd code (VarString reason) B.empty -instance Serialize Reject where- get =- S.get >>= \(VarString bs) ->- Reject (stringToCommand bs) <$> S.get <*> S.get <*> maybeData+instance Serial Reject where+ deserialize =+ deserialize >>= \(VarString bs) ->+ Reject (stringToCommand bs)+ <$> deserialize+ <*> deserialize+ <*> maybeData where maybeData = isEmpty >>= \done -> if done then return B.empty else getByteString 32- put (Reject cmd code reason dat) = do- put $ VarString $ commandToString cmd- put code- put reason+ serialize (Reject cmd code reason dat) = do+ serialize $ VarString $ commandToString cmd+ serialize code+ serialize reason unless (B.null dat) $ putByteString dat +instance Binary Reject where+ put = serialize+ get = deserialize++instance Serialize Reject where+ put = serialize+ get = deserialize+ -- | Data type representing a variable-length integer. The 'VarInt' type -- usually precedes an array or a string that can vary in length. newtype VarInt = VarInt { getVarInt :: Word64 } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize VarInt where+instance Serial VarInt where - get = VarInt <$> ( getWord8 >>= go )+ deserialize = VarInt <$> ( getWord8 >>= go ) where go 0xff = getWord64le go 0xfe = fromIntegral <$> getWord32le go 0xfd = fromIntegral <$> getWord16le go x = fromIntegral <$> return x - put (VarInt x)+ serialize (VarInt x) | x < 0xfd = putWord8 $ fromIntegral x | x <= 0xffff = do@@ -394,23 +506,39 @@ putWord8 0xff putWord64le x -putVarInt :: Integral a => a -> Put-putVarInt = put . VarInt . fromIntegral+instance Binary VarInt where+ put = serialize+ get = deserialize +instance Serialize VarInt where+ put = serialize+ get = deserialize++putVarInt :: (MonadPut m, Integral a) => a -> m ()+putVarInt = serialize . VarInt . fromIntegral+ -- | Data type for serialization of variable-length strings. newtype VarString = VarString { getVarString :: ByteString } deriving (Eq, Show, Read, Generic, NFData) -instance Serialize VarString where+instance Serial VarString where - get = VarString <$> (readBS =<< S.get)+ deserialize = VarString <$> (readBS =<< deserialize) where readBS (VarInt len) = getByteString (fromIntegral len) - put (VarString bs) = do+ serialize (VarString bs) = do putVarInt $ B.length bs putByteString bs +instance Binary VarString where+ put = serialize+ get = deserialize++instance Serialize VarString where+ put = serialize+ get = deserialize+ -- | When a bitcoin node creates an outgoing connection to another node, -- the first message it will send is a 'Version' message. The other node -- will similarly respond with it's own 'Version' message.@@ -436,40 +564,48 @@ , relay :: !Bool } deriving (Eq, Show, Generic, NFData) -instance Serialize Version where+instance Serial Version where - get = Version <$> getWord32le- <*> getWord64le- <*> getWord64le- <*> S.get- <*> S.get- <*> getWord64le- <*> S.get- <*> getWord32le- <*> (go =<< isEmpty)+ deserialize = Version <$> getWord32le+ <*> getWord64le+ <*> getWord64le+ <*> deserialize+ <*> deserialize+ <*> getWord64le+ <*> deserialize+ <*> getWord32le+ <*> (go =<< isEmpty) where go True = return True go False = getBool - put (Version v s t ar as n ua sh r) = do+ serialize (Version v s t ar as n ua sh r) = do putWord32le v putWord64le s putWord64le t- put ar- put as+ serialize ar+ serialize as putWord64le n- put ua+ serialize ua putWord32le sh putBool r +instance Binary Version where+ put = serialize+ get = deserialize++instance Serialize Version where+ put = serialize+ get = deserialize+ -- | 0x00 is 'False', anything else is 'True'.-getBool :: Get Bool+getBool :: MonadGet m => m Bool getBool = go =<< getWord8 where go 0 = return False go _ = return True -putBool :: Bool -> Put+putBool :: MonadPut m => Bool -> m () putBool True = putWord8 1 putBool False = putWord8 0 @@ -512,13 +648,21 @@ String str <- lexP return (stringToCommand (cs str)) -instance Serialize MessageCommand where- get = go <$> getByteString 12+instance Serial MessageCommand where+ deserialize = go <$> getByteString 12 where go bs = let str = unpackCommand bs in stringToCommand str- put mc = putByteString $ packCommand $ commandToString mc+ serialize mc = putByteString $ packCommand $ commandToString mc++instance Binary MessageCommand where+ put = serialize+ get = deserialize++instance Serialize MessageCommand where+ put = serialize+ get = deserialize instance IsString MessageCommand where fromString str = stringToCommand (cs str)
src/Haskoin/Network/Message.hs view
@@ -21,13 +21,13 @@ import Control.DeepSeq import Control.Monad (unless)+import Data.Binary (Binary (..)) import Data.ByteString (ByteString) import qualified Data.ByteString as BS-import Data.Serialize (Serialize, encode, get, put)-import Data.Serialize.Get (Get, getByteString, getWord32be,- getWord32le, isolate, lookAhead)-import Data.Serialize.Put (Putter, putByteString, putWord32be,- putWord32le)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..)) import Data.Word (Word32) import GHC.Generics (Generic) import Haskoin.Block.Common@@ -51,19 +51,29 @@ , headChecksum :: !CheckSum32 } deriving (Eq, Show, Generic, NFData) -instance Serialize MessageHeader where+instance Serial MessageHeader where - get = MessageHeader <$> getWord32be- <*> get- <*> getWord32le- <*> get+ deserialize =+ MessageHeader+ <$> getWord32be+ <*> deserialize+ <*> getWord32le+ <*> deserialize - put (MessageHeader m c l chk) = do+ serialize (MessageHeader m c l chk) = do putWord32be m- put c+ serialize c putWord32le l- put chk+ serialize chk +instance Binary MessageHeader where+ put = serialize+ get = deserialize++instance Serialize MessageHeader where+ put = serialize+ get = deserialize+ -- | The 'Message' type is used to identify all the valid messages that can be -- sent between bitcoin peers. Only values of type 'Message' will be accepted -- by other bitcoin peers as bitcoin protocol messages need to be correctly@@ -124,9 +134,9 @@ msgType (MOther c _) = MCOther c -- | Deserializer for network messages.-getMessage :: Network -> Get Message+getMessage :: MonadGet m => Network -> m Message getMessage net = do- (MessageHeader mgc cmd len chk) <- get+ (MessageHeader mgc cmd len chk) <- deserialize bs <- lookAhead $ getByteString $ fromIntegral len unless (mgc == getNetworkMagic net)@@ -135,28 +145,30 @@ (checkSum32 bs == chk) (fail $ "get: Invalid message checksum: " ++ show chk) if len > 0- then isolate (fromIntegral len) $- case cmd of- MCVersion -> MVersion <$> get- MCAddr -> MAddr <$> get- MCInv -> MInv <$> get- MCGetData -> MGetData <$> get- MCNotFound -> MNotFound <$> get- MCGetBlocks -> MGetBlocks <$> get- MCGetHeaders -> MGetHeaders <$> get- MCTx -> MTx <$> get- MCBlock -> MBlock <$> get- MCMerkleBlock -> MMerkleBlock <$> get- MCHeaders -> MHeaders <$> get- MCFilterLoad -> MFilterLoad <$> get- MCFilterAdd -> MFilterAdd <$> get- MCPing -> MPing <$> get- MCPong -> MPong <$> get- MCAlert -> MAlert <$> get- MCReject -> MReject <$> get- MCOther c -> MOther c <$> getByteString (fromIntegral len)- _ -> fail $ "get: command " ++ show cmd ++- " should not carry a payload"+ then do+ bs <- ensure (fromIntegral len)+ let f = case cmd of+ MCVersion -> MVersion <$> deserialize+ MCAddr -> MAddr <$> deserialize+ MCInv -> MInv <$> deserialize+ MCGetData -> MGetData <$> deserialize+ MCNotFound -> MNotFound <$> deserialize+ MCGetBlocks -> MGetBlocks <$> deserialize+ MCGetHeaders -> MGetHeaders <$> deserialize+ MCTx -> MTx <$> deserialize+ MCBlock -> MBlock <$> deserialize+ MCMerkleBlock -> MMerkleBlock <$> deserialize+ MCHeaders -> MHeaders <$> deserialize+ MCFilterLoad -> MFilterLoad <$> deserialize+ MCFilterAdd -> MFilterAdd <$> deserialize+ MCPing -> MPing <$> deserialize+ MCPong -> MPong <$> deserialize+ MCAlert -> MAlert <$> deserialize+ MCReject -> MReject <$> deserialize+ MCOther c -> MOther c <$> getByteString (fromIntegral len)+ _ -> fail $ "get: command " ++ show cmd +++ " should not carry a payload"+ either fail return (runGetS f bs) else case cmd of MCGetAddr -> return MGetAddr MCVerAck -> return MVerAck@@ -167,35 +179,35 @@ " is expected to carry a payload" -- | Serializer for network messages.-putMessage :: Network -> Putter Message+putMessage :: MonadPut m => Network -> Message -> m () putMessage net msg = do let (cmd, payload) = case msg of- MVersion m -> (MCVersion, encode m)- MVerAck -> (MCVerAck, BS.empty)- MAddr m -> (MCAddr, encode m)- MInv m -> (MCInv, encode m)- MGetData m -> (MCGetData, encode m)- MNotFound m -> (MCNotFound, encode m)- MGetBlocks m -> (MCGetBlocks, encode m)- MGetHeaders m -> (MCGetHeaders, encode m)- MTx m -> (MCTx, encode m)- MBlock m -> (MCBlock, encode m)- MMerkleBlock m -> (MCMerkleBlock, encode m)- MHeaders m -> (MCHeaders, encode m)- MGetAddr -> (MCGetAddr, BS.empty)- MFilterLoad m -> (MCFilterLoad, encode m)- MFilterAdd m -> (MCFilterAdd, encode m)- MFilterClear -> (MCFilterClear, BS.empty)- MPing m -> (MCPing, encode m)- MPong m -> (MCPong, encode m)- MAlert m -> (MCAlert, encode m)- MMempool -> (MCMempool, BS.empty)- MReject m -> (MCReject, encode m)- MSendHeaders -> (MCSendHeaders, BS.empty)- MOther c p -> (MCOther c, p)+ MVersion m -> (MCVersion, runPutS $ serialize m)+ MVerAck -> (MCVerAck, BS.empty)+ MAddr m -> (MCAddr, runPutS $ serialize m)+ MInv m -> (MCInv, runPutS $ serialize m)+ MGetData m -> (MCGetData, runPutS $ serialize m)+ MNotFound m -> (MCNotFound, runPutS $ serialize m)+ MGetBlocks m -> (MCGetBlocks, runPutS $ serialize m)+ MGetHeaders m -> (MCGetHeaders, runPutS $ serialize m)+ MTx m -> (MCTx, runPutS $ serialize m)+ MBlock m -> (MCBlock, runPutS $ serialize m)+ MMerkleBlock m -> (MCMerkleBlock, runPutS $ serialize m)+ MHeaders m -> (MCHeaders, runPutS $ serialize m)+ MGetAddr -> (MCGetAddr, BS.empty)+ MFilterLoad m -> (MCFilterLoad, runPutS $ serialize m)+ MFilterAdd m -> (MCFilterAdd, runPutS $ serialize m)+ MFilterClear -> (MCFilterClear, BS.empty)+ MPing m -> (MCPing, runPutS $ serialize m)+ MPong m -> (MCPong, runPutS $ serialize m)+ MAlert m -> (MCAlert, runPutS $ serialize m)+ MMempool -> (MCMempool, BS.empty)+ MReject m -> (MCReject, runPutS $ serialize m)+ MSendHeaders -> (MCSendHeaders, BS.empty)+ MOther c p -> (MCOther c, p) chk = checkSum32 payload len = fromIntegral $ BS.length payload header = MessageHeader (getNetworkMagic net) cmd len chk- put header+ serialize header putByteString payload
src/Haskoin/Script/Common.hs view
@@ -24,12 +24,16 @@ import Control.DeepSeq import Control.Monad-import Data.ByteString (ByteString)-import qualified Data.ByteString as B+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Hashable-import Data.Serialize as S-import Data.Word (Word8)-import GHC.Generics (Generic)+import Data.Serialize (Serialize (..))+import Data.Word (Word8)+import GHC.Generics (Generic) -- | Data type representing a transaction script. Scripts are defined as lists -- of script operators 'ScriptOp'. Scripts are used to:@@ -47,18 +51,26 @@ } deriving (Eq, Show, Read, Generic, Hashable, NFData) -instance Serialize Script where- get =+instance Serial Script where+ deserialize = Script <$> getScriptOps where getScriptOps = do empty <- isEmpty if empty then return []- else (:) <$> get <*> getScriptOps+ else (:) <$> deserialize <*> getScriptOps - put (Script ops) = forM_ ops put+ serialize (Script ops) = forM_ ops serialize +instance Binary Script where+ put = serialize+ get = deserialize++instance Serialize Script where+ put = serialize+ get = deserialize+ -- | Data type representing the type of an OP_PUSHDATA opcode. data PushDataType =@@ -203,8 +215,8 @@ | OP_INVALIDOPCODE !Word8 deriving (Show, Read, Eq, Generic, Hashable, NFData) -instance Serialize ScriptOp where- get = go =<< (fromIntegral <$> getWord8)+instance Serial ScriptOp where+ deserialize = go =<< (fromIntegral <$> getWord8) where go op | op == 0x00 = return OP_0@@ -359,7 +371,7 @@ | otherwise = return $ OP_INVALIDOPCODE op - put op = case op of+ serialize op = case op of (OP_PUSHDATA payload optype)-> do let len = B.length payload@@ -523,7 +535,14 @@ -- Bitcoin Cash May 2020 hard fork OP_REVERSEBYTES -> putWord8 0xbc +instance Binary ScriptOp where+ put = serialize+ get = deserialize +instance Serialize ScriptOp where+ put = serialize+ get = deserialize+ -- | Check whether opcode is only data. isPushOp :: ScriptOp -> Bool isPushOp op = case op of@@ -565,7 +584,13 @@ | i `elem` [1 .. 16] = op | otherwise = err where- op = either (const err) id . S.decode . B.singleton . fromIntegral $ i + 0x50+ op = either+ (const err)+ id .+ runGetS deserialize .+ B.singleton .+ fromIntegral $+ i + 0x50 err = error $ "intToScriptOp: Invalid integer " ++ show i -- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. This functions@@ -575,4 +600,4 @@ | res `elem` [1..16] = return res | otherwise = Left $ "scriptOpToInt: invalid opcode " ++ show s where- res = fromIntegral (B.head $ S.encode s) - 0x50+ res = fromIntegral (B.head $ runPutS $ serialize s) - 0x50
src/Haskoin/Script/SigHash.hs view
@@ -41,16 +41,18 @@ import qualified Data.Aeson as J import Data.Bits import qualified Data.ByteString as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Hashable import Data.Maybe import Data.Scientific-import Data.Serialize import Data.Word import GHC.Generics (Generic) import Haskoin.Constants+import Haskoin.Crypto import Haskoin.Crypto.Hash import Haskoin.Network.Common-import Haskoin.Crypto import Haskoin.Script.Common import Haskoin.Transaction.Common import Haskoin.Util@@ -207,8 +209,8 @@ let newTx = Tx (txVersion tx) newIn newOut [] (txLockTime tx) return $ doubleSHA256 $- runPut $ do- put newTx+ runPutS $ do+ serialize newTx putWord32le $ fromIntegral sh where fout = Script $ filter (/= OP_CODESEPARATOR) $ scriptOps out@@ -218,13 +220,13 @@ buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn] buildInputs txins out i sh | hasAnyoneCanPayFlag sh =- [ (txins !! i) { scriptInput = encode out } ]+ [ (txins !! i) { scriptInput = runPutS $ serialize out } ] | isSigHashAll sh || isSigHashUnknown sh = single | otherwise = zipWith noSeq single [0 ..] where emptyIn = map (\ti -> ti { scriptInput = BS.empty }) txins single =- updateIndex i emptyIn $ \ti -> ti { scriptInput = encode out }+ updateIndex i emptyIn $ \ti -> ti { scriptInput = runPutS $ serialize out } noSeq ti j = if i == j then ti@@ -251,35 +253,35 @@ -> SigHash -- ^ what to sign -> Hash256 -- ^ hash to be signed txSigHashForkId net tx out v i sh =- doubleSHA256 . runPut $ do+ doubleSHA256 . runPutS $ do putWord32le $ txVersion tx- put hashPrevouts- put hashSequence- put $ prevOutput $ txIn tx !! i+ serialize hashPrevouts+ serialize hashSequence+ serialize $ prevOutput $ txIn tx !! i putScript out putWord64le v putWord32le $ txInSequence $ txIn tx !! i- put hashOutputs+ serialize hashOutputs putWord32le $ txLockTime tx putWord32le $ fromIntegral $ sigHashAddNetworkId net sh where hashPrevouts | not $ hasAnyoneCanPayFlag sh =- doubleSHA256 $ runPut $ mapM_ (put . prevOutput) $ txIn tx+ doubleSHA256 $ runPutS $ mapM_ (serialize . prevOutput) $ txIn tx | otherwise = zeros hashSequence | not (hasAnyoneCanPayFlag sh) && not (isSigHashSingle sh) && not (isSigHashNone sh) =- doubleSHA256 $ runPut $ mapM_ (putWord32le . txInSequence) $ txIn tx+ doubleSHA256 $ runPutS $ mapM_ (putWord32le . txInSequence) $ txIn tx | otherwise = zeros hashOutputs | not (isSigHashSingle sh) && not (isSigHashNone sh) =- doubleSHA256 $ runPut $ mapM_ put $ txOut tx+ doubleSHA256 $ runPutS $ mapM_ serialize $ txOut tx | isSigHashSingle sh && i < length (txOut tx) =- doubleSHA256 $ encode $ txOut tx !! i+ doubleSHA256 $ runPutS $ serialize $ txOut tx !! i | otherwise = zeros putScript s = do- let encodedScript = encode s+ let encodedScript = runPutS $ serialize s putVarInt $ BS.length encodedScript putByteString encodedScript zeros :: Hash256@@ -302,7 +304,7 @@ encodeTxSig :: TxSignature -> BS.ByteString encodeTxSig TxSignatureEmpty = error "Can not encode an empty signature" encodeTxSig (TxSignature sig (SigHash n)) =- runPut $ putSig sig >> putWord8 (fromIntegral n)+ runPutS $ putSig sig >> putWord8 (fromIntegral n) -- | Deserialize a 'TxSignature'. decodeTxSig :: Network -> BS.ByteString -> Either String TxSignature
src/Haskoin/Script/Standard.hs view
@@ -50,11 +50,13 @@ import qualified Data.Aeson.Encoding as A import Data.ByteString (ByteString) import qualified Data.ByteString as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Function (on) import Data.Hashable import Data.List (sortBy)-import Data.Maybe (fromJust, isJust)-import Data.Serialize as S+import Data.Maybe (fromJust, isJust, isNothing) import Data.Word (Word8) import GHC.Generics (Generic) import Haskoin.Constants@@ -145,17 +147,19 @@ decodeOutput :: Script -> Either String ScriptOutput decodeOutput s = case scriptOps s of -- Pay to PubKey- [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs+ [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> runGetS deserialize bs -- Pay to PubKey Hash [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->- PayPKHash <$> S.decode bs+ PayPKHash <$> runGetS deserialize bs -- Pay to Script Hash [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->- PayScriptHash <$> S.decode bs+ PayScriptHash <$> runGetS deserialize bs -- Pay to Witness [OP_0, OP_PUSHDATA bs OPCODE]- | BS.length bs == 20 -> PayWitnessPKHash <$> S.decode bs- | BS.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs+ | BS.length bs == 20 -> PayWitnessPKHash <$> runGetS deserialize bs+ | BS.length bs == 32 -> PayWitnessScriptHash <$> runGetS deserialize bs+ | BS.length bs /= 20 && BS.length bs /= 32 ->+ Left "Version 0 segwit program must be 20 or 32 bytes long" -- Other Witness [ver, OP_PUSHDATA bs _] | isJust (opWitnessVersion ver)@@ -204,23 +208,23 @@ opWitnessVersion OP_14 = Just 14 opWitnessVersion OP_15 = Just 15 opWitnessVersion OP_16 = Just 16-opWitnessVersion _ = Nothing+opWitnessVersion _ = Nothing -- | Similar to 'decodeOutput' but decodes from a 'ByteString'. decodeOutputBS :: ByteString -> Either String ScriptOutput-decodeOutputBS = decodeOutput <=< S.decode+decodeOutputBS = decodeOutput <=< runGetS deserialize -- | Computes a 'Script' from a standard 'ScriptOutput'. encodeOutput :: ScriptOutput -> Script encodeOutput s = Script $ case s of -- Pay to PubKey- (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]+ (PayPK k) -> [opPushData $ runPutS $ serialize k, OP_CHECKSIG] -- Pay to PubKey Hash Address (PayPKHash h) -> [ OP_DUP , OP_HASH160- , opPushData $ S.encode h+ , opPushData $ runPutS $ serialize h , OP_EQUALVERIFY, OP_CHECKSIG ] -- Pay to MultiSig Keys@@ -228,17 +232,17 @@ | r <= length ps -> let opM = intToScriptOp r opN = intToScriptOp $ length ps- keys = map (opPushData . S.encode) ps+ keys = map (opPushData . runPutS . serialize) ps in opM : keys ++ [opN, OP_CHECKMULTISIG] | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys" -- Pay to Script Hash Address (PayScriptHash h) ->- [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]+ [ OP_HASH160, opPushData $ runPutS $ serialize h, OP_EQUAL] -- Pay to Witness PubKey Hash Address (PayWitnessPKHash h) ->- [ OP_0, opPushData $ S.encode h ]+ [ OP_0, opPushData $ runPutS $ serialize h ] (PayWitnessScriptHash h) ->- [ OP_0, opPushData $ S.encode h ]+ [ OP_0, opPushData $ runPutS $ serialize h ] (PayWitness v h) -> [ case witnessVersionOp v of Nothing -> error "encodeOutput: invalid witness version"@@ -249,15 +253,15 @@ -- | Similar to 'encodeOutput' but encodes to a ByteString encodeOutputBS :: ScriptOutput -> ByteString-encodeOutputBS = S.encode . encodeOutput+encodeOutputBS = runPutS . serialize . encodeOutput -- | Encode script as pay-to-script-hash script toP2SH :: Script -> ScriptOutput-toP2SH = PayScriptHash . addressHash . S.encode+toP2SH = PayScriptHash . addressHash . runPutS . serialize -- | Encode script as a pay-to-witness-script-hash script toP2WSH :: Script -> ScriptOutput-toP2WSH = PayWitnessScriptHash . sha256 . S.encode+toP2WSH = PayWitnessScriptHash . sha256 . runPutS . serialize -- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@ matchPayMulSig :: Script -> Either String ScriptOutput@@ -269,7 +273,7 @@ else Left "matchPayMulSig: Invalid M or N parameters" _ -> Left "matchPayMulSig: script did not match output template" where- go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)+ go (OP_PUSHDATA bs _:xs) = liftM2 (:) (runGetS deserialize bs) (go xs) go [] = return [] go _ = Left "matchPayMulSig: invalid multisig opcode" @@ -277,8 +281,8 @@ -- their compressed serialized representations. Refer to BIP-67. sortMulSig :: ScriptOutput -> ScriptOutput sortMulSig out = case out of- PayMulSig keys r -> PayMulSig (sortBy (compare `on` encode) keys) r- _ -> error "Can only call orderMulSig on PayMulSig scripts"+ PayMulSig keys r -> PayMulSig (sortBy (compare `on` (runPutS . serialize)) keys) r+ _ -> error "Can only call orderMulSig on PayMulSig scripts" -- | Data type describing standard transaction input scripts. Input scripts -- provide the signing data required to unlock the coins of the output they are@@ -347,7 +351,7 @@ matchPK [op] = SpendPK <$> f op matchPK _ = Nothing matchPKHash [op, OP_PUSHDATA pub _] =- SpendPKHash <$> f op <*> eitherToMaybe (decode pub)+ SpendPKHash <$> f op <*> eitherToMaybe (runGetS deserialize pub) matchPKHash _ = Nothing matchMulSig (x:xs) = do guard $ x == OP_0@@ -379,7 +383,7 @@ -- | Like 'decodeInput' but decodes directly from a serialized script -- 'ByteString'. decodeInputBS :: Network -> ByteString -> Either String ScriptInput-decodeInputBS net = decodeInput net <=< decode+decodeInputBS net = decodeInput net <=< runGetS deserialize -- | Encode a standard input into a script. encodeInput :: ScriptInput -> Script@@ -391,7 +395,7 @@ -- | Similar to 'encodeInput' but encodes directly to a serialized script -- 'ByteString'. encodeInputBS :: ScriptInput -> ByteString-encodeInputBS = encode . encodeInput+encodeInputBS = runPutS . serialize . encodeInput -- | Encode a standard 'SimpleInput' into opcodes as an input 'Script'. encodeSimpleInput :: SimpleInput -> Script@@ -399,7 +403,7 @@ Script $ case s of SpendPK ts -> [f ts]- SpendPKHash ts p -> [f ts, opPushData $ encode p]+ SpendPKHash ts p -> [f ts, opPushData $ runPutS $ serialize p] SpendMulSig xs -> OP_0 : map f xs where f TxSignatureEmpty = OP_0
src/Haskoin/Transaction/Builder.hs view
@@ -49,13 +49,15 @@ import Control.Monad.Identity (runIdentity) import Crypto.Secp256k1 import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Conduit (ConduitT, Void, await, runConduit, (.|)) import Data.Conduit.List (sourceList) import Data.Either (fromRight) import Data.List (nub) import Data.Maybe (catMaybes, fromJust, isJust)-import Data.Serialize (decode, encode) import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word (Word64)@@ -219,8 +221,20 @@ guessTxSize pki msi pkout msout = 8 + inpLen + inp + outLen + out where- inpLen = B.length $ encode $ VarInt $ fromIntegral $ length msi + pki- outLen = B.length $ encode $ VarInt $ fromIntegral $ pkout + msout+ inpLen =+ B.length .+ runPutS .+ serialize .+ VarInt .+ fromIntegral $+ length msi + pki+ outLen =+ B.length .+ runPutS .+ serialize .+ VarInt .+ fromIntegral $+ pkout + msout inp = pki * 148 + sum (map guessMSSize msi) -- (20: hash160) + (5: opcodes) + -- (1: script len) + (8: Word64)@@ -234,12 +248,14 @@ guessMSSize :: (Int,Int) -> Int guessMSSize (m, n) -- OutPoint (36) + Sequence (4) + Script- = 40 + fromIntegral (B.length $ encode $ VarInt $ fromIntegral scp) + scp+ = 40 ++ fromIntegral (B.length $ runPutS . serialize $ VarInt $ fromIntegral scp) ++ scp -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG where rdm = fromIntegral $- B.length $ encode $ opPushData $ B.replicate (n * 34 + 3) 0+ B.length $ runPutS $ serialize $ opPushData $ B.replicate (n * 34 + 3) 0 -- Redeem + m*sig + OP_0 scp = rdm + m * 73 + 1 @@ -414,9 +430,9 @@ wp so = decodeWitnessInput net =<< viewWitnessProgram net so ws nestedScriptOutput :: Either String ScriptOutput- nestedScriptOutput = scriptOps <$> decode inp >>= \case+ nestedScriptOutput = scriptOps <$> runGetS deserialize inp >>= \case [OP_PUSHDATA bs _] -> decodeOutputBS bs- _ -> Left "nestedScriptOutput: not a nested output"+ _ -> Left "nestedScriptOutput: not a nested output" verifyLegacyInput :: ScriptOutput -> ScriptInput -> Bool verifyLegacyInput so si = case (so, si) of@@ -443,7 +459,7 @@ verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub) (PayWitnessScriptHash h, Just rdm'@(PayPKHash kh), SpendPKHash (TxSignature sig sh) pub) -> payToWitnessScriptAddress rdm' == p2wshAddr h &&- addressHash (encode pub) == kh &&+ addressHash (runPutS (serialize pub)) == kh && verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub) (PayWitnessScriptHash h, Just rdm'@(PayMulSig pubs r), SpendMulSig sigs) -> payToWitnessScriptAddress rdm' == p2wshAddr h &&
src/Haskoin/Transaction/Builder/Sign.hs view
@@ -28,12 +28,14 @@ import Data.Aeson (FromJSON, ToJSON (..), object, pairs, parseJSON, withObject, (.:), (.:?), (.=))+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either (rights) import Data.Hashable (Hashable) import Data.List (find, nub) import Data.Maybe (catMaybes, fromMaybe, mapMaybe, maybeToList)-import qualified Data.Serialize as S import Data.Word (Word64) import GHC.Generics (Generic) import Haskoin.Address (getAddrHash160, pubKeyAddr)@@ -125,7 +127,7 @@ } where f si x = x {scriptInput = encodeInputBS si}- g so' x = x {scriptInput = S.encode . opPushData $ encodeOutputBS so'}+ g so' x = x {scriptInput = runPutS . serialize . opPushData $ encodeOutputBS so'} txis = txIn tx nextTxIn so' si | isSegwit so' && nest = updateIndex i txis (g so')
src/Haskoin/Transaction/Common.hs view
@@ -31,15 +31,20 @@ import Control.Applicative ((<|>)) import Control.DeepSeq import Control.Monad (forM_, guard, liftM2, mzero,- replicateM, (<=<))+ replicateM, unless, when, (<=<)) import Data.Aeson as A import Data.Aeson.Encoding (unsafeToEncoding)+import Data.Binary (Binary (..)) import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.ByteString.Builder (char7)+import qualified Data.ByteString.Lazy as BL+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Hashable (Hashable) import Data.Maybe (fromMaybe)-import Data.Serialize as S+import Data.Serialize (Serialize (..)) import Data.String (IsString, fromString) import Data.String.Conversions (cs) import Data.Text (Text)@@ -52,8 +57,16 @@ -- | Transaction id: hash of transaction excluding witness data. newtype TxHash = TxHash { getTxHash :: Hash256 }- deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)+ deriving (Eq, Ord, Generic, Hashable, Serial, NFData) +instance Serialize TxHash where+ put = serialize+ get = deserialize++instance Binary TxHash where+ put = serialize+ get = deserialize+ instance Show TxHash where showsPrec _ = shows . txHashToHex @@ -74,24 +87,28 @@ instance ToJSON TxHash where toJSON = A.String . txHashToHex toEncoding h =- unsafeToEncoding $ char7 '"' <> hexBuilder (B.reverse (S.encode h)) <> char7 '"'+ unsafeToEncoding $+ char7 '"' <>+ hexBuilder (BL.reverse (runPutL (serialize h))) <>+ char7 '"' -- | Transaction hash excluding signatures. nosigTxHash :: Tx -> TxHash nosigTxHash tx =- TxHash $ doubleSHA256 $ S.encode tx { txIn = map clearInput $ txIn tx }+ TxHash $ doubleSHA256 $ runPutS $+ serialize tx { txIn = map clearInput $ txIn tx } where clearInput ti = ti { scriptInput = B.empty } -- | Convert transaction hash to hex form, reversing bytes. txHashToHex :: TxHash -> Text-txHashToHex (TxHash h) = encodeHex (B.reverse (S.encode h))+txHashToHex (TxHash h) = encodeHex (B.reverse (runPutS (serialize h))) -- | Convert transaction hash from hex, reversing bytes. hexToTxHash :: Text -> Maybe TxHash hexToTxHash hex = do bs <- B.reverse <$> decodeHex hex- h <- either (const Nothing) Just (S.decode bs)+ h <- either (const Nothing) Just (runGetS deserialize bs) return $ TxHash h -- | Witness stack for SegWit transactions.@@ -117,36 +134,45 @@ -- | Compute transaction hash. txHash :: Tx -> TxHash-txHash tx = TxHash (doubleSHA256 (S.encode tx {txWitness = []}))+txHash tx = TxHash . doubleSHA256 . runPutS $ serialize tx {txWitness = []} instance IsString Tx where fromString =- fromMaybe e . (eitherToMaybe . S.decode <=< decodeHex) . cs+ fromMaybe e . (eitherToMaybe . runGetS deserialize <=< decodeHex) . cs where e = error "Could not read transaction from hex string" -instance Serialize Tx where- get = parseWitnessTx <|> parseLegacyTx- put tx+instance Serial Tx where+ deserialize =+ isWitnessTx >>= \w -> if w then parseWitnessTx else parseLegacyTx+ serialize tx | null (txWitness tx) = putLegacyTx tx | otherwise = putWitnessTx tx -putInOut :: Tx -> Put+instance Binary Tx where+ put = serialize+ get = deserialize++instance Serialize Tx where+ put = serialize+ get = deserialize++putInOut :: MonadPut m => Tx -> m () putInOut tx = do putVarInt $ length (txIn tx)- forM_ (txIn tx) put+ forM_ (txIn tx) serialize putVarInt $ length (txOut tx)- forM_ (txOut tx) put+ forM_ (txOut tx) serialize -- | Non-SegWit transaction serializer.-putLegacyTx :: Tx -> Put+putLegacyTx :: MonadPut m => Tx -> m () putLegacyTx tx = do putWord32le (txVersion tx) putInOut tx putWord32le (txLockTime tx) -- | Witness transaciton serializer.-putWitnessTx :: Tx -> Put+putWitnessTx :: MonadPut m => Tx -> m () putWitnessTx tx = do putWord32le (txVersion tx) putWord8 0x00@@ -155,49 +181,61 @@ putWitnessData (txWitness tx) putWord32le (txLockTime tx) +isWitnessTx :: MonadGet m => m Bool+isWitnessTx = lookAhead $ do+ _ <- getWord32le+ m <- getWord8+ f <- getWord8+ return (m == 0x00 && f == 0x01)+ -- | Non-SegWit transaction deseralizer.-parseLegacyTx :: Get Tx+parseLegacyTx :: MonadGet m => m Tx parseLegacyTx = do v <- getWord32le- is <- replicateList =<< S.get- os <- replicateList =<< S.get+ is <- replicateList =<< deserialize+ os <- replicateList =<< deserialize+ when (length is == 0x00 && length os == 0x01) $ fail "Witness transaction" l <- getWord32le return Tx- {txVersion = v, txIn = is, txOut = os, txWitness = [], txLockTime = l}+ { txVersion = v+ , txIn = is+ , txOut = os+ , txWitness = []+ , txLockTime = l+ } where- replicateList (VarInt c) = replicateM (fromIntegral c) S.get+ replicateList (VarInt c) = replicateM (fromIntegral c) deserialize -- | Witness transaction deserializer.-parseWitnessTx :: Get Tx+parseWitnessTx :: MonadGet m => m Tx parseWitnessTx = do v <- getWord32le m <- getWord8 f <- getWord8- guard $ m == 0x00- guard $ f == 0x01- is <- replicateList =<< S.get- os <- replicateList =<< S.get+ unless (m == 0x00 && f == 0x01) $ fail "Not a witness transaction"+ is <- replicateList =<< deserialize+ os <- replicateList =<< deserialize w <- parseWitnessData $ length is l <- getWord32le return Tx {txVersion = v, txIn = is, txOut = os, txWitness = w, txLockTime = l} where- replicateList (VarInt c) = replicateM (fromIntegral c) S.get+ replicateList (VarInt c) = replicateM (fromIntegral c) deserialize -- | Witness data deserializer. Requires count of inputs.-parseWitnessData :: Int -> Get WitnessData+parseWitnessData :: MonadGet m => Int -> m WitnessData parseWitnessData n = replicateM n parseWitnessStack where parseWitnessStack = do- VarInt i <- S.get+ VarInt i <- deserialize replicateM (fromIntegral i) parseWitnessStackItem parseWitnessStackItem = do- VarInt i <- S.get+ VarInt i <- deserialize getByteString $ fromIntegral i -- | Witness data serializer.-putWitnessData :: WitnessData -> Put+putWitnessData :: MonadPut m => WitnessData -> m () putWitnessData = mapM_ putWitnessStack where putWitnessStack ws = do@@ -246,18 +284,26 @@ , txInSequence :: !Word32 } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData) -instance Serialize TxIn where- get =- TxIn <$> S.get <*> (readBS =<< S.get) <*> getWord32le+instance Serial TxIn where+ deserialize =+ TxIn <$> deserialize <*> (readBS =<< deserialize) <*> getWord32le where readBS (VarInt len) = getByteString $ fromIntegral len - put (TxIn o s q) = do- put o+ serialize (TxIn o s q) = do+ serialize o putVarInt $ B.length s putByteString s putWord32le q +instance Binary TxIn where+ get = deserialize+ put = serialize++instance Serialize TxIn where+ get = deserialize+ put = serialize+ instance FromJSON TxIn where parseJSON = withObject "TxIn" $ \o ->@@ -288,17 +334,25 @@ , scriptOutput :: !ByteString } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData) -instance Serialize TxOut where- get = do+instance Serial TxOut where+ deserialize = do val <- getWord64le- (VarInt len) <- S.get+ VarInt len <- deserialize TxOut val <$> getByteString (fromIntegral len) - put (TxOut o s) = do+ serialize (TxOut o s) = do putWord64le o putVarInt $ B.length s putByteString s +instance Binary TxOut where+ put = serialize+ get = deserialize++instance Serialize TxOut where+ put = serialize+ get = deserialize+ instance FromJSON TxOut where parseJSON = withObject "TxOut" $ \o ->@@ -319,11 +373,19 @@ , outPointIndex :: !Word32 } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData) -instance Serialize OutPoint where- get = do- (h,i) <- liftM2 (,) S.get getWord32le+instance Serial OutPoint where+ deserialize = do+ (h,i) <- liftM2 (,) deserialize getWord32le return $ OutPoint h i- put (OutPoint h i) = put h >> putWord32le i+ serialize (OutPoint h i) = serialize h >> putWord32le i++instance Binary OutPoint where+ put = serialize+ get = deserialize++instance Serialize OutPoint where+ put = serialize+ get = deserialize instance FromJSON OutPoint where parseJSON =
src/Haskoin/Transaction/Partial.hs view
@@ -35,12 +35,16 @@ import Control.Monad (guard, replicateM, void) import Data.ByteString (ByteString) import qualified Data.ByteString as B-import Data.Hashable (Hashable)+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (Serial (..)) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap+import Data.Hashable (Hashable) import Data.List (foldl') import Data.Maybe (fromMaybe, isJust)-import Data.Serialize as S+import Data.Serialize (Get, Put, Serialize)+import qualified Data.Serialize as S import GHC.Generics (Generic) import GHC.Word (Word32, Word8) import Haskoin.Address (Address (..), pubKeyAddr)@@ -64,7 +68,8 @@ -- output per output in the unsigned transaction. The inputs and outputs in the -- 'PartiallySignedTransaction' line up by index with the inputs and outputs in -- the unsigned transaction.-data PartiallySignedTransaction = PartiallySignedTransaction+data PartiallySignedTransaction =+ PartiallySignedTransaction { unsignedTransaction :: Tx , globalUnknown :: UnknownMap , inputs :: [Input]@@ -100,8 +105,9 @@ instance NFData Output --- | A map of raw PSBT keys to byte strings for extra data. The 'keyType' field cannot overlap with any of the reserved--- 'keyType' fields specified in the PSBT specification.+-- | A map of raw PSBT keys to byte strings for extra data. The 'keyType' field+-- cannot overlap with any of the reserved 'keyType' fields specified in the+-- PSBT specification. newtype UnknownMap = UnknownMap { unknownMap :: HashMap Key ByteString } deriving (Show, Eq, Semigroup, Monoid, Generic) @@ -117,8 +123,11 @@ instance Hashable Key --- | Take two 'PartiallySignedTransaction's and merge them. The 'unsignedTransaction' field in both must be the same.-merge :: PartiallySignedTransaction -> PartiallySignedTransaction -> Maybe PartiallySignedTransaction+-- | Take two 'PartiallySignedTransaction's and merge them. The+-- 'unsignedTransaction' field in both must be the same.+merge :: PartiallySignedTransaction+ -> PartiallySignedTransaction+ -> Maybe PartiallySignedTransaction merge psbt1 psbt2 | unsignedTransaction psbt1 == unsignedTransaction psbt2 = Just $ psbt1@@ -130,38 +139,68 @@ mergeInput :: Input -> Input -> Input mergeInput a b = Input- { nonWitnessUtxo = if isJust witUtx then Nothing else nonWitnessUtxo a <|> nonWitnessUtxo b- , witnessUtxo = witUtx- , sigHashType = sigHashType a <|> sigHashType b- , partialSigs = partialSigs a <> partialSigs b- , inputHDKeypaths = inputHDKeypaths a <> inputHDKeypaths b- , inputUnknown = inputUnknown a <> inputUnknown b- , inputRedeemScript = inputRedeemScript a <|> inputRedeemScript b- , inputWitnessScript = inputWitnessScript a <|> inputWitnessScript b- , finalScriptSig = finalScriptSig a <|> finalScriptSig b- , finalScriptWitness = finalScriptWitness a <|> finalScriptWitness b+ { nonWitnessUtxo =+ if isJust witUtx+ then Nothing+ else nonWitnessUtxo a <|> nonWitnessUtxo b+ , witnessUtxo =+ witUtx+ , sigHashType =+ sigHashType a <|> sigHashType b+ , partialSigs =+ partialSigs a <> partialSigs b+ , inputHDKeypaths =+ inputHDKeypaths a <> inputHDKeypaths b+ , inputUnknown =+ inputUnknown a <> inputUnknown b+ , inputRedeemScript =+ inputRedeemScript a <|> inputRedeemScript b+ , inputWitnessScript =+ inputWitnessScript a <|> inputWitnessScript b+ , finalScriptSig =+ finalScriptSig a <|> finalScriptSig b+ , finalScriptWitness =+ finalScriptWitness a <|> finalScriptWitness b } where witUtx = witnessUtxo a <|> witnessUtxo b mergeOutput :: Output -> Output -> Output mergeOutput a b = Output- { outputRedeemScript = outputRedeemScript a <|> outputRedeemScript b- , outputWitnessScript = outputWitnessScript a <|> outputWitnessScript b- , outputHDKeypaths = outputHDKeypaths a <> outputHDKeypaths b- , outputUnknown = outputUnknown a <> outputUnknown b+ { outputRedeemScript =+ outputRedeemScript a <|> outputRedeemScript b+ , outputWitnessScript =+ outputWitnessScript a <|> outputWitnessScript b+ , outputHDKeypaths =+ outputHDKeypaths a <> outputHDKeypaths b+ , outputUnknown =+ outputUnknown a <> outputUnknown b } -- | Take partial signatures from all of the 'Input's and finalize the signature.-complete :: PartiallySignedTransaction -> PartiallySignedTransaction-complete psbt = psbt { inputs = map (completeInput . analyzeInputs) (indexed $ inputs psbt) }+complete :: PartiallySignedTransaction+ -> PartiallySignedTransaction+complete psbt =+ psbt+ {+ inputs = map+ (completeInput . analyzeInputs)+ (indexed $ inputs psbt)+ } where- analyzeInputs (i, input) = (outputScript =<< witnessUtxo input <|> nonWitScript, input)+ analyzeInputs (i, input) =+ (,)+ (outputScript =<< witnessUtxo input <|> nonWitScript)+ input where nonWitScript = getPrevOut i =<< nonWitnessUtxo input getPrevOut i tx =- (txOut tx !!?) . fromIntegral . outPointIndex . prevOutput =<< txIn (unsignedTransaction psbt) !!? i+ (txOut tx !!?) .+ fromIntegral .+ outPointIndex .+ prevOutput =<<+ txIn (unsignedTransaction psbt) !!? i xs !!? i = lookup i $ indexed xs outputScript = eitherToMaybe . decodeOutputBS . scriptOutput@@ -173,65 +212,127 @@ indexed = zip [0..] completeSig :: Input -> ScriptOutput -> Input+ completeSig input (PayPK k) =- input { finalScriptSig = eitherToMaybe . S.decode =<< HashMap.lookup k (partialSigs input) }+ input+ { finalScriptSig =+ eitherToMaybe . runGetS deserialize =<<+ HashMap.lookup k (partialSigs input)+ }+ completeSig input (PayPKHash h)- | [(k, sig)] <- HashMap.toList $ partialSigs input- , PubKeyAddress h == pubKeyAddr k- = input { finalScriptSig = Just $ Script [opPushData sig, opPushData (S.encode k)] }-completeSig input (PayMulSig pubKeys m) | length sigs >= m = input { finalScriptSig = finalSig }+ | [(k, sig)] <- HashMap.toList (partialSigs input)+ , PubKeyAddress h == pubKeyAddr k =+ input+ {+ finalScriptSig =+ Just $+ Script+ [+ opPushData sig,+ opPushData (runPutS (serialize k))+ ]+ }++completeSig input (PayMulSig pubKeys m)+ | length sigs >= m =+ input { finalScriptSig = finalSig } where sigs = collectSigs m pubKeys input- finalSig = Script . (OP_0 :) . (map opPushData sigs <>) . pure . opPushData . S.encode <$> inputRedeemScript input+ finalSig =+ Script .+ (OP_0 :) .+ (map opPushData sigs <>) .+ pure . opPushData . runPutS . serialize <$>+ inputRedeemScript input+ completeSig input (PayScriptHash h) | Just rdmScript <- inputRedeemScript input , PayScriptHash h == toP2SH rdmScript , Right decodedScript <- decodeOutput rdmScript- , not (isPayScriptHash decodedScript)- = completeSig input decodedScript+ , not (isPayScriptHash decodedScript) =+ completeSig input decodedScript+ completeSig input (PayWitnessPKHash h)- | [(k, sig)] <- HashMap.toList $ partialSigs input- , PubKeyAddress h == pubKeyAddr k- = input { finalScriptWitness = Just [sig, S.encode k]- , finalScriptSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input+ | [(k, sig)] <- HashMap.toList (partialSigs input)+ , PubKeyAddress h == pubKeyAddr k =+ input+ {+ finalScriptWitness =+ Just [sig, runPutS $ serialize k],+ finalScriptSig =+ Script . pure . opPushData . runPutS . serialize <$>+ inputRedeemScript input } completeSig input (PayWitnessScriptHash h) | Just witScript <- inputWitnessScript input , PayWitnessScriptHash h == toP2WSH witScript- , Right decodedScript <- decodeOutput witScript- = completeWitnessSig input decodedScript+ , Right decodedScript <- decodeOutput witScript =+ completeWitnessSig input decodedScript+ completeSig input _ = input completeWitnessSig :: Input -> ScriptOutput -> Input-completeWitnessSig input script@(PayMulSig pubKeys m) | length sigs >= m = input- { finalScriptWitness = Just finalWit- , finalScriptSig = finalSig- }+completeWitnessSig input script@(PayMulSig pubKeys m)+ | length sigs >= m =+ input+ {+ finalScriptWitness = Just finalWit,+ finalScriptSig = finalSig+ } where sigs = collectSigs m pubKeys input- finalSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input+ finalSig = Script . pure . opPushData . runPutS . serialize <$>+ inputRedeemScript input finalWit = mempty : sigs <> [encodeOutputBS script]+ completeWitnessSig input _ = input collectSigs :: Int -> [PubKeyI] -> Input -> [ByteString]-collectSigs m pubKeys input = take m . reverse $ foldl' lookupKey [] pubKeys+collectSigs m pubKeys input =+ take m . reverse $ foldl' lookupKey [] pubKeys where- lookupKey sigs key = maybe sigs (:sigs) $ HashMap.lookup key (partialSigs input)+ lookupKey sigs key =+ maybe sigs (: sigs) $+ HashMap.lookup key (partialSigs input) --- | Take a finalized 'PartiallySignedTransaction' and produce the signed final transaction. You may need to call--- 'complete' on the 'PartiallySignedTransaction' before producing the final transaction.+-- | Take a finalized 'PartiallySignedTransaction' and produce the signed final+-- transaction. You may need to call 'complete' on the+-- 'PartiallySignedTransaction' before producing the final transaction. finalTransaction :: PartiallySignedTransaction -> Tx-finalTransaction psbt = setInputs . foldl' finalizeInput ([], []) $ zip (txIn tx) (inputs psbt)+finalTransaction psbt =+ setInputs .+ foldl' finalizeInput ([], []) $+ zip (txIn tx) (inputs psbt) where tx = unsignedTransaction psbt- hasWitness = any (isJust . finalScriptWitness) (inputs psbt)- setInputs (ins, witData) = tx { txIn = reverse ins, txWitness = if hasWitness then reverse witData else [] }- finalizeInput (ins, witData) (txInput, psbtInput) = maybe finalWitness finalScript $ finalScriptSig psbtInput+ hasWitness =+ any+ (isJust . finalScriptWitness)+ (inputs psbt)+ setInputs (ins, witData) =+ tx+ {+ txIn = reverse ins,+ txWitness = if hasWitness then reverse witData else []+ }+ finalizeInput (ins, witData) (txInput, psbtInput) =+ maybe finalWitness finalScript $+ finalScriptSig psbtInput where- finalScript script = (txInput { scriptInput = encode script }:ins, []:witData)- finalWitness = (ins, fromMaybe [] (finalScriptWitness psbtInput):witData)+ finalScript script =+ (+ txInput { scriptInput = runPutS $ serialize script } : ins,+ [] : witData+ )+ finalWitness =+ (+ ins,+ fromMaybe [] (finalScriptWitness psbtInput) : witData+ ) --- | Take an unsigned transaction and produce an empty 'PartiallySignedTransaction'+-- | Take an unsigned transaction and produce an empty+-- 'PartiallySignedTransaction' emptyPSBT :: Tx -> PartiallySignedTransaction emptyPSBT tx = PartiallySignedTransaction { unsignedTransaction = tx@@ -251,120 +352,190 @@ instance Serialize PartiallySignedTransaction where get = do- magic <- getBytes 4+ magic <- S.getBytes 4 guard $ magic == "psbt"- headerSep <- getWord8+ headerSep <- S.getWord8 guard $ headerSep == 0xff - keySize <- getWord8+ keySize <- S.getWord8 guard $ keySize == 1- globalUnsignedTxType <- getWord8+ globalUnsignedTxType <- S.getWord8 guard $ globalUnsignedTxType == 0x00- unsignedTransaction <- getSizedBytes+ unsignedTransaction <- getSizedBytes deserialize guard $ all (B.null . scriptInput) (txIn unsignedTransaction) guard $ null (txWitness unsignedTransaction) - globalUnknown <- get- globalEnd <- getWord8+ globalUnknown <- S.get+ globalEnd <- S.getWord8 guard $ globalEnd == 0x00 - inputs <- replicateM (length $ txIn unsignedTransaction) get- outputs <- replicateM (length $ txOut unsignedTransaction) get+ inputs <-+ replicateM+ (length (txIn unsignedTransaction))+ S.get+ outputs <-+ replicateM+ (length (txOut unsignedTransaction))+ S.get - return PartiallySignedTransaction { unsignedTransaction, globalUnknown, inputs, outputs }+ return+ PartiallySignedTransaction+ {+ unsignedTransaction,+ globalUnknown,+ inputs,+ outputs+ } - put PartiallySignedTransaction{ unsignedTransaction, globalUnknown, inputs, outputs } = do- putByteString "psbt"- putWord8 0xff -- Header separator+ put PartiallySignedTransaction+ {+ unsignedTransaction,+ globalUnknown,+ inputs,+ outputs+ } = do+ S.putByteString "psbt"+ S.putWord8 0xff -- Header separator - putWord8 0x01 -- Key size- putWord8 0x00 -- Unsigned Transaction type- putSizedBytes unsignedTransaction- put globalUnknown- putWord8 0x00 -- Global end+ S.putWord8 0x01 -- Key size+ S.putWord8 0x00 -- Unsigned Transaction type+ putSizedBytes $ serialize unsignedTransaction+ S.put globalUnknown+ S.putWord8 0x00 -- Global end - mapM_ put inputs- mapM_ put outputs+ mapM_ S.put inputs+ mapM_ S.put outputs instance Serialize Key where get = do- VarInt keySize <- get+ VarInt keySize <- deserialize guard $ keySize > 0- t <- getWord8- k <- getBytes (fromIntegral keySize - 1)+ t <- S.getWord8+ k <- S.getBytes (fromIntegral keySize - 1) return (Key t k) put (Key t k) = do putVarInt $ 1 + B.length k- putWord8 t- putByteString k+ S.putWord8 t+ S.putByteString k instance Serialize UnknownMap where get = go HashMap.empty where getItem m = do- k <- get- VarString v <- get+ k <- S.get+ VarString v <- deserialize go $ HashMap.insert k v m go m = do- isEnd <- lookAhead getWord8+ isEnd <- S.lookAhead S.getWord8 if isEnd == 0x00 then return (UnknownMap m) else getItem m - put (UnknownMap m) = void $ HashMap.traverseWithKey (\k v -> put k >> put (VarString v)) m+ put (UnknownMap m) =+ void $+ HashMap.traverseWithKey+ (\k v -> S.put k >> serialize (VarString v))+ m instance Serialize Input where- get = getMap getInputItem setInputUnknown emptyInput+ get =+ getMap getInputItem setInputUnknown emptyInput where- setInputUnknown f input = input { inputUnknown = UnknownMap $ f (unknownMap $ inputUnknown input) }+ setInputUnknown f input =+ input+ {+ inputUnknown =+ UnknownMap (f (unknownMap (inputUnknown input)))+ } - put Input { nonWitnessUtxo, witnessUtxo, partialSigs, sigHashType- , inputRedeemScript, inputWitnessScript, inputHDKeypaths- , finalScriptSig, finalScriptWitness, inputUnknown- } = do- whenJust (putKeyValue InNonWitnessUtxo) nonWitnessUtxo- whenJust (putKeyValue InWitnessUtxo) witnessUtxo+ put Input+ {+ nonWitnessUtxo,+ witnessUtxo,+ partialSigs,+ sigHashType,+ inputRedeemScript,+ inputWitnessScript,+ inputHDKeypaths,+ finalScriptSig,+ finalScriptWitness,+ inputUnknown+ } = do+ whenJust (putKeyValue InNonWitnessUtxo . serialize)+ nonWitnessUtxo+ whenJust (putKeyValue InWitnessUtxo . serialize)+ witnessUtxo putPartialSig partialSigs- whenJust putSigHash sigHashType- whenJust (putKeyValue InRedeemScript) inputRedeemScript- whenJust (putKeyValue InWitnessScript) inputWitnessScript+ whenJust putSigHash+ sigHashType+ whenJust (putKeyValue InRedeemScript . serialize)+ inputRedeemScript+ whenJust (putKeyValue InWitnessScript . serialize)+ inputWitnessScript putHDPath InBIP32Derivation inputHDKeypaths- whenJust (putKeyValue InFinalScriptSig) finalScriptSig- whenJust (putKeyValue InFinalScriptWitness) finalScriptWitness- put inputUnknown- putWord8 0x00+ whenJust (putKeyValue InFinalScriptSig . serialize)+ finalScriptSig+ whenJust (putKeyValue InFinalScriptWitness . serialize)+ finalScriptWitness+ S.put inputUnknown+ S.putWord8 0x00 where- putPartialSig = putPubKeyMap InPartialSig . fmap VarString+ putPartialSig =+ putPubKeyMap serialize InPartialSig . fmap VarString putSigHash sigHash = do putKey InSigHashType- putWord8 0x04- putWord32le (fromIntegral sigHash)+ S.putWord8 0x04+ S.putWord32le (fromIntegral sigHash) instance Serialize Output where get = getMap getOutputItem setOutputUnknown emptyOutput where- setOutputUnknown f output = output { outputUnknown = UnknownMap $ f (unknownMap $ outputUnknown output) }+ setOutputUnknown f output =+ output+ {+ outputUnknown =+ UnknownMap (f (unknownMap (outputUnknown output)))+ } - put Output{ outputRedeemScript, outputWitnessScript, outputHDKeypaths, outputUnknown } = do- whenJust (putKeyValue OutRedeemScript) outputRedeemScript- whenJust (putKeyValue OutWitnessScript) outputWitnessScript- putHDPath OutBIP32Derivation outputHDKeypaths- put outputUnknown- putWord8 0x00+ put Output+ {+ outputRedeemScript,+ outputWitnessScript,+ outputHDKeypaths,+ outputUnknown+ } = do+ whenJust (putKeyValue OutRedeemScript . serialize)+ outputRedeemScript+ whenJust (putKeyValue OutWitnessScript . serialize)+ outputWitnessScript+ putHDPath OutBIP32Derivation+ outputHDKeypaths+ S.put outputUnknown+ S.putWord8 0x00 -putSizedBytes :: Serialize a => a -> Put-putSizedBytes a = putVarInt (B.length bs) >> putByteString bs- where bs = encode a+putSizedBytes :: Put -> Put+putSizedBytes f = do+ putVarInt (B.length bs)+ S.putByteString bs+ where+ bs = S.runPut f -getSizedBytes :: Serialize a => Get a-getSizedBytes = getNested (fromIntegral . getVarInt <$> get) get+getSizedBytes :: Get a -> Get a+getSizedBytes f =+ S.getNested+ (fromIntegral . getVarInt <$> deserialize)+ f -putKeyValue :: (Enum t, Serialize v) => t -> v -> Put-putKeyValue t v = putKey t >> putSizedBytes v+putKeyValue :: Enum t => t -> Put -> Put+putKeyValue t v = do+ putKey t+ putSizedBytes v putKey :: Enum t => t -> Put-putKey t = putVarInt (1 :: Word8) >> putWord8 (enumWord8 t)+putKey t = do+ putVarInt (1 :: Word8)+ S.putWord8 (enumWord8 t) getMap :: (Bounded t, Enum t) => (Int -> a -> t -> Get a)@@ -372,16 +543,17 @@ -> a -> Get a getMap getMapItem setUnknown = go where- getItem keySize m (Right t) = getMapItem (fromIntegral keySize - 1) m t >>= go+ getItem keySize m (Right t) =+ getMapItem (fromIntegral keySize - 1) m t >>= go getItem keySize m (Left t) = do- k <- getBytes (fromIntegral keySize - 1)- VarString v <- get+ k <- S.getBytes (fromIntegral keySize - 1)+ VarString v <- deserialize go $ setUnknown (HashMap.insert (Key t k) v) m go m = do- keySize <- getVarInt <$> get+ keySize <- getVarInt <$> deserialize if keySize == 0 then return m- else getItem keySize m =<< (word8Enum <$> getWord8)+ else getItem keySize m =<< (word8Enum <$> S.getWord8) data InputType = InNonWitnessUtxo@@ -406,89 +578,124 @@ instance NFData OutputType getInputItem :: Int -> Input -> InputType -> Get Input+ getInputItem 0 input@Input{nonWitnessUtxo = Nothing} InNonWitnessUtxo = do- utxo <- getSizedBytes- return $ input { nonWitnessUtxo = Just utxo }+ utxo <- getSizedBytes deserialize+ return input { nonWitnessUtxo = Just utxo }+ getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do- utxo <- getSizedBytes- return $ input { witnessUtxo = Just utxo }+ utxo <- getSizedBytes deserialize+ return input { witnessUtxo = Just utxo }+ getInputItem keySize input InPartialSig = do (k, v) <- getPartialSig- return $ input { partialSigs = HashMap.insert k v (partialSigs input) }+ return+ input+ {+ partialSigs = HashMap.insert k v (partialSigs input)+ } where- getPartialSig = (,) <$> isolate keySize get <*> (getVarString <$> get)+ getPartialSig =+ (,)+ <$> S.isolate keySize deserialize+ <*> (getVarString <$> deserialize)+ getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do- VarInt size <- get+ VarInt size <- deserialize guard $ size == 0x04- sigHash <- fromIntegral <$> getWord32le+ sigHash <- fromIntegral <$> S.getWord32le return $ input { sigHashType = Just sigHash }+ getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do- script <- getSizedBytes+ script <- getSizedBytes deserialize return $ input { inputRedeemScript = Just script }+ getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do- script <- getSizedBytes+ script <- getSizedBytes deserialize return $ input { inputWitnessScript = Just script }+ getInputItem keySize input InBIP32Derivation = do (k, v) <- getHDPath keySize- return $ input { inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input) }+ return+ input+ {+ inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input)+ }+ getInputItem 0 input@Input{finalScriptSig = Nothing} InFinalScriptSig = do- script <- getSizedBytes+ script <- getSizedBytes deserialize return $ input { finalScriptSig = Just script }+ getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do scripts <- map getVarString <$> getVarIntList return $ input { finalScriptWitness = Just scripts } where getVarIntList = do- VarInt n <- get- replicateM (fromIntegral n) get+ VarInt n <- deserialize+ replicateM (fromIntegral n) deserialize+ getInputItem keySize input inputType = fail $ "Incorrect key size for input item or item already existed: " <> show (keySize, input, inputType) getOutputItem :: Int -> Output -> OutputType -> Get Output+ getOutputItem 0 output@Output{outputRedeemScript = Nothing} OutRedeemScript = do- script <- getSizedBytes+ script <- getSizedBytes deserialize return $ output { outputRedeemScript = Just script }+ getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do- script <- getSizedBytes+ script <- getSizedBytes deserialize return $ output { outputWitnessScript = Just script }+ getOutputItem keySize output OutBIP32Derivation = do (k, v) <- getHDPath keySize return $ output { outputHDKeypaths = HashMap.insert k v (outputHDKeypaths output) }+ getOutputItem keySize output outputType = fail $ "Incorrect key size for output item or item already existed: " <> show (keySize, output, outputType) getHDPath :: Int -> Get (PubKeyI, (Fingerprint, [KeyIndex]))-getHDPath keySize = (,) <$> isolate keySize get <*> (unPSBTHDPath <$> get)+getHDPath keySize =+ (,)+ <$> S.isolate keySize deserialize+ <*> (unPSBTHDPath <$> S.get) putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put-putHDPath t = putPubKeyMap t . fmap PSBTHDPath+putHDPath t = putPubKeyMap S.put t . fmap PSBTHDPath -newtype PSBTHDPath = PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) }+newtype PSBTHDPath =+ PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) } deriving (Show, Eq, Generic) instance NFData PSBTHDPath instance Serialize PSBTHDPath where get = do- VarInt valueSize <- get+ VarInt valueSize <- deserialize guard $ valueSize `mod` 4 == 0 let numIndices = (fromIntegral valueSize - 4) `div` 4 PSBTHDPath <$>- isolate+ S.isolate (fromIntegral valueSize)- ((,) <$> getWord32le <*> getKeyIndexList numIndices)+ ((,) <$> S.getWord32le <*> getKeyIndexList numIndices) where- getKeyIndexList n = replicateM n getWord32le- put (PSBTHDPath (fp, kis)) = putVarInt (B.length bs) >> putByteString bs+ getKeyIndexList n = replicateM n S.getWord32le++ put (PSBTHDPath (fp, kis)) = do+ putVarInt (B.length bs)+ S.putByteString bs where- bs = runPut $ putWord32le fp >> mapM_ putWord32le kis+ bs = S.runPut $ S.putWord32le fp >> mapM_ S.putWord32le kis -putPubKeyMap :: (Serialize a, Enum t) => t -> HashMap PubKeyI a -> Put-putPubKeyMap t = void . HashMap.traverseWithKey putItem+putPubKeyMap :: Enum t => (a -> Put) -> t -> HashMap PubKeyI a -> Put+putPubKeyMap f t =+ void . HashMap.traverseWithKey putItem where- putItem k v = put (Key (enumWord8 t) (encode k)) >> put v+ putItem k v = do+ S.put $ Key (enumWord8 t) (runPutS (serialize k))+ f v enumWord8 :: Enum a => a -> Word8 enumWord8 = fromIntegral . fromEnum
src/Haskoin/Transaction/Segwit.hs view
@@ -30,7 +30,9 @@ ) where import Data.ByteString (ByteString)-import qualified Data.Serialize as S+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Haskoin.Constants import Haskoin.Keys.Common import Haskoin.Script@@ -59,8 +61,8 @@ -- @since 0.11.0.0 toWitnessStack :: WitnessProgram -> WitnessStack toWitnessStack = \case- P2WPKH (WitnessProgramPKH sig key) -> [encodeTxSig sig, S.encode key]- P2WSH (WitnessProgramSH stack scr) -> stack <> [S.encode scr]+ P2WPKH (WitnessProgramPKH sig key) -> [encodeTxSig sig, runPutS (serialize key)]+ P2WSH (WitnessProgramSH stack scr) -> stack <> [runPutS (serialize scr)] EmptyWitnessProgram -> mempty -- | High level representation of a P2WPKH witness@@ -89,10 +91,10 @@ viewWitnessProgram net so witness = case so of PayWitnessPKHash _ | length witness == 2 -> do sig <- decodeTxSig net $ head witness- pubkey <- S.decode $ witness !! 1+ pubkey <- runGetS deserialize $ witness !! 1 return . P2WPKH $ WitnessProgramPKH sig pubkey PayWitnessScriptHash _ | not (null witness) -> do- redeemScript <- S.decode $ last witness+ redeemScript <- runGetS deserialize $ last witness return . P2WSH $ WitnessProgramSH (init witness) redeemScript _ | null witness -> return EmptyWitnessProgram | otherwise -> Left "viewWitnessProgram: Invalid witness program"@@ -112,7 +114,7 @@ (PayPK _, [sigBS]) -> SpendPK <$> decodeTxSig net sigBS (PayPKHash _, [sigBS, keyBS]) ->- SpendPKHash <$> decodeTxSig net sigBS <*> S.decode keyBS+ SpendPKHash <$> decodeTxSig net sigBS <*> runGetS deserialize keyBS (PayMulSig _ _, "" : sigsBS) -> SpendMulSig <$> traverse (decodeTxSig net) sigsBS _ -> Left "decodeWitnessInput: Non-standard script output"@@ -138,7 +140,7 @@ simpleInputStack :: SimpleInput -> [ByteString] simpleInputStack = \case SpendPK sig -> [f sig]- SpendPKHash sig k -> [f sig, S.encode k]+ SpendPKHash sig k -> [f sig, runPutS (serialize k)] SpendMulSig sigs -> "" : fmap f sigs where f TxSignatureEmpty = ""
src/Haskoin/Util.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-| Module : Haskoin.Util@@ -17,7 +18,9 @@ , integerToBS , hexBuilder , encodeHex+ , encodeHexLazy , decodeHex+ , decodeHexLazy , getBits -- * Maybe & Either Helpers@@ -40,23 +43,43 @@ , dropFieldLabel , dropSumLabels + -- * Serialization Helpers+ , putList, getList+ , putMaybe, getMaybe+ , putLengthBytes, getLengthBytes+ , putInteger, getInteger+ , putInt32be, getInt32be+ , putInt64be, getInt64be+ , getIntMap, putIntMap+ , getTwo, putTwo+ ) where -import Control.Monad (guard)-import Control.Monad.Except (ExceptT (..), liftEither)-import Data.Aeson.Types (Options (..), SumEncoding (..),- defaultOptions, defaultTaggedObject)+import Control.Monad+import Control.Monad.Except (ExceptT (..), liftEither)+import Data.Aeson.Types (Options (..), SumEncoding (..),+ defaultOptions,+ defaultTaggedObject) import Data.Bits-import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Base16 as B16+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base16 as B16 import Data.ByteString.Builder-import qualified Data.ByteString.Lazy as BL-import Data.Char (toLower)+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Base16 as BL16+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Char (toLower)+import Data.Int+import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap import Data.List-import Data.Text (Text)-import qualified Data.Text.Encoding as E-import Data.Word (Word8)+import Data.Text (Text)+import qualified Data.Text.Encoding as E+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Encoding as EL+import Data.Word -- ByteString helpers @@ -76,23 +99,23 @@ f 0 = Nothing f x = Just (fromInteger x :: Word8, x `shiftR` 8) -hexBuilder :: ByteString -> Builder-hexBuilder = byteStringHex+hexBuilder :: BL.ByteString -> Builder+hexBuilder = lazyByteStringHex --- | Encode as string of human-readable hex characters. encodeHex :: ByteString -> Text-encodeHex = E.decodeUtf8 . BL.toStrict . toLazyByteString . byteStringHex+encodeHex = B16.encodeBase16 --- | Decode string of human-readable hex characters.+-- | Encode as string of human-readable hex characters.+encodeHexLazy :: BL.ByteString -> TL.Text+encodeHexLazy = BL16.encodeBase16+ decodeHex :: Text -> Maybe ByteString-# if MIN_VERSION_base16_bytestring(1,0,0)-decodeHex = eitherToMaybe . B16.decode . E.encodeUtf8-# else-decodeHex text =- let (x, b) = B16.decode (E.encodeUtf8 text)- in guard (b == BS.empty) >> return x-# endif+decodeHex = eitherToMaybe . B16.decodeBase16 . E.encodeUtf8 +-- | Decode string of human-readable hex characters.+decodeHexLazy :: TL.Text -> Maybe BL.ByteString+decodeHexLazy = eitherToMaybe . BL16.decodeBase16 . EL.encodeUtf8+ -- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString' -- will be smallest required to hold that many bits, padded with zeroes to the -- right.@@ -205,3 +228,125 @@ out' = ((acc `shiftR` bits') .&. maxv) : out in inner acc out' bits' | otherwise = (out, bits)++--+-- Serialization helpers+--++putInt32be :: MonadPut m => Int32 -> m ()+putInt32be n+ | n < 0 = putWord32be (complement (fromIntegral (abs n)) + 1)+ | otherwise = putWord32be (fromIntegral (abs n))++getInt32be :: MonadGet m => m Int32+getInt32be = do+ n <- getWord32be+ if testBit n 31+ then return (negate (complement (fromIntegral n) + 1))+ else return (fromIntegral n)++putInt64be :: MonadPut m => Int64 -> m ()+putInt64be n+ | n < 0 = putWord64be (complement (fromIntegral (abs n)) + 1)+ | otherwise = putWord64be (fromIntegral (abs n))++getInt64be :: MonadGet m => m Int64+getInt64be = do+ n <- getWord64be+ if testBit n 63+ then return (negate (complement (fromIntegral n) + 1))+ else return (fromIntegral n)++putInteger :: MonadPut m => Integer -> m ()+putInteger n+ | n >= lo && n <= hi = do+ putWord8 0x00+ putInt32be (fromIntegral n)+ | otherwise = do+ putWord8 0x01+ putWord8 (fromIntegral (signum n))+ let len = (nrBits (abs n) + 7) `div` 8+ putWord64be (fromIntegral len)+ mapM_ putWord8 (unroll (abs n))+ where+ lo = fromIntegral (minBound :: Int32)+ hi = fromIntegral (maxBound :: Int32)++getInteger :: MonadGet m => m Integer+getInteger =+ getWord8 >>= \case+ 0 -> fromIntegral <$> getInt32be+ _ -> do+ sign <- getWord8+ bytes <- getList getWord8+ let v = roll bytes+ return $! if sign == 0x01 then v else - v++putMaybe :: MonadPut m => (a -> m ()) -> Maybe a -> m ()+putMaybe f Nothing = putWord8 0x00+putMaybe f (Just x) = putWord8 0x01 >> f x++getMaybe :: MonadGet m => m a -> m (Maybe a)+getMaybe f =+ getWord8 >>= \case+ 0x00 -> return Nothing+ 0x01 -> Just <$> f+ _ -> fail "Not a Maybe"++putLengthBytes :: MonadPut m => ByteString -> m ()+putLengthBytes bs = do+ putWord64be (fromIntegral (BS.length bs))+ putByteString bs++getLengthBytes :: MonadGet m => m ByteString+getLengthBytes = do+ len <- fromIntegral <$> getWord64be+ getByteString len++--+-- Fold and unfold an Integer to and from a list of its bytes+--+unroll :: (Integral a, Bits a) => a -> [Word8]+unroll = unfoldr step+ where+ step 0 = Nothing+ step i = Just (fromIntegral i, i `shiftR` 8)++roll :: (Integral a, Bits a) => [Word8] -> a+roll = foldr unstep 0+ where+ unstep b a = a `shiftL` 8 .|. fromIntegral b++nrBits :: (Ord a, Integral a) => a -> Int+nrBits k =+ let expMax = until (\e -> 2 ^ e > k) (* 2) 1+ findNr :: Int -> Int -> Int+ findNr lo hi+ | mid == lo = hi+ | 2 ^ mid <= k = findNr mid hi+ | 2 ^ mid > k = findNr lo mid+ where mid = (lo + hi) `div` 2+ in findNr (expMax `div` 2) expMax++-- | Read as a list of pairs of int and element.+getIntMap :: MonadGet m => m Int -> m a -> m (IntMap a)+getIntMap i m = IntMap.fromList <$> getList (getTwo i m)++putIntMap :: MonadPut m => (Int -> m ()) -> (a -> m ()) -> IntMap a -> m ()+putIntMap f g = putList (putTwo f g) . IntMap.toAscList++putTwo :: MonadPut m => (a -> m ()) -> (b -> m ()) -> (a, b) -> m ()+putTwo f g (x, y) = f x >> g y++getTwo :: MonadGet m => m a -> m b -> m (a, b)+getTwo f g = (,) <$> f <*> g++putList :: MonadPut m => (a -> m ()) -> [a] -> m ()+putList f ls = do+ putWord64be (fromIntegral (length ls))+ mapM_ f ls++getList :: MonadGet m => m a -> m [a]+getList f = do+ l <- fromIntegral <$> getWord64be+ replicateM l f
src/Haskoin/Util/Arbitrary/Script.hs view
@@ -274,7 +274,7 @@ arbitraryWitOutput :: Gen ScriptOutput arbitraryWitOutput = do- ver <- choose (0, 16)+ ver <- choose (1, 16) len <- choose (2, 40) ws <- vectorOf len arbitrary let bs = B.pack ws
src/Haskoin/Util/Arbitrary/Util.hs view
@@ -32,23 +32,24 @@ ) where -import Control.Monad (forM_, (<=<))-import qualified Data.Aeson as A-import qualified Data.Aeson.Encoding as A-import qualified Data.Aeson.Types as A-import Data.ByteString (ByteString, pack)-import qualified Data.ByteString.Short as BSS-import qualified Data.Map.Strict as Map+import Control.Monad (forM_, (<=<))+import qualified Data.Aeson as A+import qualified Data.Aeson.Encoding as A+import qualified Data.Aeson.Types as A+import Data.ByteString (ByteString, pack)+import qualified Data.ByteString.Short as BSS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import qualified Data.Map.Strict as Map import Data.Proxy-import qualified Data.Serialize as S-import Data.Time.Clock (UTCTime (..))-import Data.Time.Clock.POSIX (posixSecondsToUTCTime)-import qualified Data.Typeable as T-import Data.Word (Word32)+import Data.Time.Clock (UTCTime (..))+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import qualified Data.Typeable as T+import Data.Word (Word32) import Haskoin.Constants-import Test.Hspec (Spec, describe, shouldBe,- shouldSatisfy)-import Test.Hspec.QuickCheck (prop)+import Test.Hspec (Spec, describe, shouldBe, shouldSatisfy)+import Test.Hspec.QuickCheck (prop) import Test.QuickCheck -- | Arbitrary strict 'ByteString'.@@ -94,7 +95,7 @@ -- Helpers for creating Serial and JSON Identity tests data SerialBox =- forall a. (Show a, Eq a, T.Typeable a, S.Serialize a) =>+ forall a. (Show a, Eq a, T.Typeable a, Serial a) => SerialBox (Gen a) data ReadBox =@@ -115,7 +116,7 @@ testIdentity :: [SerialBox] -> [ReadBox] -> [JsonBox] -> [NetBox] -> Spec testIdentity serialVals readVals jsonVals netVals = do- describe "Data.Serialize Encoding" $+ describe "Binary Encoding" $ forM_ serialVals $ \(SerialBox g) -> testSerial g describe "Read/Show Encoding" $ forM_ readVals $ \(ReadBox g) -> testRead g@@ -124,12 +125,13 @@ describe "Data.Aeson Encoding with Network" $ forM_ netVals $ \(NetBox (j,e,p,g)) -> testNetJson j e p g --- | Generate Data.Serialize identity tests+-- | Generate binary identity tests testSerial ::- (Eq a, Show a, T.Typeable a, S.Serialize a) => Gen a -> Spec+ (Eq a, Show a, T.Typeable a, Serial a) => Gen a -> Spec testSerial gen =- prop ("Data.Serialize encoding/decoding identity for " <> name) $- forAll gen $ \x -> (S.decode . S.encode) x `shouldBe` Right x+ prop ("Binary encoding/decoding identity for " <> name) $+ forAll gen $ \x ->+ (runGetS deserialize . runPutS . serialize) x `shouldBe` Right x where name = show $ T.typeRep $ proxy gen proxy :: Gen a -> Proxy a
test/Haskoin/Crypto/HashSpec.hs view
@@ -7,8 +7,10 @@ import qualified Data.ByteString.Char8 as C import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Short as BSS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Maybe (fromJust)-import Data.Serialize as S import Data.String (fromString) import Data.String.Conversions import Data.Text (Text)@@ -17,15 +19,14 @@ import Haskoin.Crypto import Haskoin.Util import Haskoin.Util.Arbitrary+import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit import Test.QuickCheck serialVals :: [SerialBox] serialVals = [ SerialBox arbitraryBS- , SerialBox arbitraryBSS , SerialBox arbitraryHash160 , SerialBox arbitraryHash256 , SerialBox arbitraryHash512@@ -50,13 +51,13 @@ prop "decodeCompact . encodeCompact i == i" decEncCompact prop "from string Hash512" $ forAll arbitraryHash512 $ \h ->- fromString (cs $ encodeHex $ encode h) == h+ fromString (cs $ encodeHex $ runPutS $ serialize h) == h prop "from string Hash256" $ forAll arbitraryHash256 $ \h ->- fromString (cs $ encodeHex $ encode h) == h+ fromString (cs $ encodeHex $ runPutS $ serialize h) == h prop "from string Hash160" $ forAll arbitraryHash160 $ \h ->- fromString (cs $ encodeHex $ encode h) == h+ fromString (cs $ encodeHex $ runPutS $ serialize h) == h describe "Test Vectors" $ do it "Passes RIPEMD160 test vectors" $ mapM_ (testVector ripemd160 getHash160) ripemd160Vectors
test/Haskoin/Keys/ExtendedSpec.hs view
@@ -6,9 +6,11 @@ import Data.Aeson as A import Data.Bits ((.&.)) import qualified Data.ByteString.Lazy.Char8 as B8+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either (isLeft) import Data.Maybe (fromJust, isJust, isNothing)-import Data.Serialize as S import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text)@@ -19,9 +21,9 @@ import Haskoin.Util import Haskoin.Util.Arbitrary import Haskoin.UtilSpec (customCerealID)+import Test.HUnit (Assertion, assertBool, assertEqual) import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit (Assertion, assertBool, assertEqual) import Test.QuickCheck hiding ((.&.)) serialVals :: [SerialBox]@@ -309,8 +311,8 @@ runVector :: XPrvKey -> TestVector -> Assertion runVector m v = do- assertBool "xPrvID" $ encodeHex (S.encode $ xPrvID m) == v !! 0- assertBool "xPrvFP" $ encodeHex (S.encode $ xPrvFP m) == v !! 1+ assertBool "xPrvID" $ encodeHex (runPutS . serialize $ xPrvID m) == v !! 0+ assertBool "xPrvFP" $ encodeHex (runPutS . serialize $ xPrvFP m) == v !! 1 assertBool "xPrvAddr" $ addrToText btc (xPubAddr $ deriveXPubKey m) == Just (v !! 2) assertBool "bip44Addr" $@@ -320,18 +322,18 @@ assertBool "xPrvWIF" $ xPrvWif btc m == v !! 5 assertBool "pubKey" $ encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m) == v !! 6- assertBool "chain code" $ encodeHex (S.encode $ xPrvChain m) == v !! 7+ assertBool "chain code" $ encodeHex (runPutS . serialize $ xPrvChain m) == v !! 7 assertBool "Hex PubKey" $- encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m) == v !! 8- assertBool "Hex PrvKey" $ encodeHex (runPut (putXPrvKey btc m)) == v !! 9+ encodeHex (runPutS $ putXPubKey btc $ deriveXPubKey m) == v !! 8+ assertBool "Hex PrvKey" $ encodeHex (runPutS (putXPrvKey btc m)) == v !! 9 assertBool "Base58 PubKey" $ xPubExport btc (deriveXPubKey m) == v !! 10 assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 11 -- This function was used to generate addition data for the test vectors genVector :: XPrvKey -> [(Text, Text)] genVector m =- [ ("xPrvID", encodeHex (S.encode $ xPrvID m))- , ("xPrvFP", encodeHex (S.encode $ xPrvFP m))+ [ ("xPrvID", encodeHex (runPutS . serialize $ xPrvID m))+ , ("xPrvFP", encodeHex (runPutS . serialize $ xPrvFP m)) , ("xPrvAddr", fromJust $ addrToText btc (xPubAddr $ deriveXPubKey m)) , ( "bip44Addr" , fromJust $@@ -339,9 +341,9 @@ , ("prvKey", encodeHex (getSecKey $ xPrvKey m)) , ("xPrvWIF", xPrvWif btc m) , ("pubKey", encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m))- , ("chain code", encodeHex (S.encode $ xPrvChain m))- , ("Hex PubKey", encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m))- , ("Hex PrvKey", encodeHex (runPut (putXPrvKey btc m)))+ , ("chain code", encodeHex (runPutS . serialize $ xPrvChain m))+ , ("Hex PubKey", encodeHex (runPutS $ putXPubKey btc $ deriveXPubKey m))+ , ("Hex PrvKey", encodeHex (runPutS (putXPrvKey btc m))) ] parseVector :: TestKey -> [TestVector] -> [(Text, XPrvKey, TestVector)]
test/Haskoin/KeysSpec.hs view
@@ -7,8 +7,11 @@ import Data.Aeson.Lens import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Maybe-import Data.Serialize as S+import qualified Data.Serialize as S import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text)@@ -20,15 +23,14 @@ import Haskoin.Util import Haskoin.Util.Arbitrary import Haskoin.UtilSpec (readTestFile)+import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit import Test.QuickCheck serialVals :: [SerialBox] serialVals =- [ SerialBox (arbitrary :: Gen SecKey)- , SerialBox (snd <$> arbitraryKeyPair) -- PubKeyI+ [ SerialBox (snd <$> arbitraryKeyPair) -- PubKeyI ] readVals :: [ReadBox]@@ -51,7 +53,7 @@ forAll arbitraryKeyPair (isCanonicalPubKey . snd) prop "Public key fromString identity" $ forAll arbitraryKeyPair $ \(_, k) ->- fromString (cs . encodeHex $ S.encode k) == k+ fromString (cs . encodeHex $ runPutS $ serialize k) == k describe "SecKey properties" $ prop "fromWif . toWif identity" $ forAll arbitraryNetwork $ \net ->@@ -88,13 +90,13 @@ -- Non-canonical public key: compressed nor uncompressed (BS.index bs 0 `notElem` [2,3,4]) where- bs = S.encode p+ bs = runPutS $ serialize p testMiniKey :: Assertion testMiniKey = assertEqual "fromMiniKey" (Just res) (go "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy") where- go = fmap (encodeHex . S.encode . secKeyData) . fromMiniKey+ go = fmap (encodeHex . runPutS . S.put . secKeyData) . fromMiniKey res = "4c7a9640c72dc2099f23715d0c8a0d8a35f8906e3cab61dd3f78b67bf887c9ab" -- Test vectors from:@@ -107,14 +109,14 @@ -- Test from WIF to SecKey let isComp = obj ^?! key "isCompressed" . _Bool prvKeyM = fromWif net a- prvKeyHexM = encodeHex . S.encode . secKeyData <$> prvKeyM+ prvKeyHexM = encodeHex . runPutS . S.put . secKeyData <$> prvKeyM assertBool "Valid PrvKey" $ isJust prvKeyM assertEqual "Valid compression" (Just isComp) (secKeyCompressed <$> prvKeyM) assertEqual "WIF matches payload" (Just payload) prvKeyHexM let prvAsPubM = (eitherToMaybe . decodeOutputBS <=< decodeHex) a assertBool "PrvKey is invalid ScriptOutput" $ isNothing prvAsPubM -- Test from SecKey to WIF- let secM = eitherToMaybe . S.decode =<< decodeHex payload+ let secM = eitherToMaybe . runGetS S.get =<< decodeHex payload wifM = toWif net . wrapSecKey isComp <$> secM assertEqual "Payload matches WIF" (Just a) wifM | otherwise = do@@ -124,7 +126,9 @@ assertBool ("Valid Address " <> cs a) $ isJust addrM assertEqual "Address matches payload" (Just payload) scriptM let pubAsWifM = fromWif net a- pubAsSecM = eitherToMaybe . S.decode =<< decodeHex a :: Maybe SecKey+ pubAsSecM =+ eitherToMaybe . runGetS S.get =<<+ decodeHex a :: Maybe SecKey assertBool "Address is invalid Wif" $ isNothing pubAsWifM assertBool "Address is invalid PrvKey" $ isNothing pubAsSecM -- Test Script to Addr@@ -145,7 +149,7 @@ testKeyIOInvalidVector :: [Text] -> Assertion testKeyIOInvalidVector [a] = do let wifMs = (`fromWif` a) <$> allNets- secKeyM = (eitherToMaybe . S.decode <=< decodeHex) a :: Maybe SecKey+ secKeyM = (eitherToMaybe . runGetS S.get <=< decodeHex) a :: Maybe SecKey scriptM = (eitherToMaybe . decodeOutputBS <=< decodeHex) a :: Maybe ScriptOutput assertBool "Payload is invalid WIF" $ all isNothing wifMs assertBool "Payload is invalid SecKey" $ isNothing secKeyM
test/Haskoin/NetworkSpec.hs view
@@ -1,8 +1,10 @@ {-# LANGUAGE OverloadedStrings #-} module Haskoin.NetworkSpec (spec) where +import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Maybe (fromJust)-import Data.Serialize as S import Data.Text (Text) import Data.Word (Word32) import Haskoin.Address@@ -13,9 +15,9 @@ import Haskoin.Util import Haskoin.Util.Arbitrary import Haskoin.UtilSpec (customCerealID)+import Test.HUnit (Assertion, assertBool, assertEqual) import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit (Assertion, assertBool, assertEqual) import Test.QuickCheck serialVals :: [SerialBox]@@ -67,7 +69,7 @@ assertBool "Bloom filter doesn't contain vector 3" $ bloomContains f3 v3 assertBool "Bloom filter doesn't contain vector 4" $ bloomContains f4 v4 assertBool "Bloom filter serialization is incorrect" $- S.encode f4 == bs+ runPutS (serialize f4) == bs where f0 = bloomCreate 3 0.01 n BloomUpdateAll f1 = bloomInsert f0 v1@@ -88,11 +90,11 @@ bloomFilter3 :: Assertion bloomFilter3 = assertBool "Bloom filter serialization is incorrect" $- S.encode f2 == bs+ runPutS (serialize f2) == bs where f0 = bloomCreate 2 0.001 0 BloomUpdateAll- f1 = bloomInsert f0 $ S.encode p- f2 = bloomInsert f1 $ S.encode $ getAddrHash160 $ pubKeyAddr p+ f1 = bloomInsert f0 $ runPutS $ serialize p+ f2 = bloomInsert f1 $ runPutS $ serialize $ getAddrHash160 $ pubKeyAddr p k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C" p = derivePubKeyI k bs = fromJust $ decodeHex "038fc16b080000000000000001"@@ -105,7 +107,7 @@ relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8" bf1 = bloomInsert bf0 relevantOutputHash bf2 = fromJust $ bloomRelevantUpdate bf1 relevantTx- spendTxInput = encode .prevOutput <$> txIn spendRelevantTx+ spendTxInput = runPutS . serialize . prevOutput <$> txIn spendRelevantTx irrelevantOutputNotUpdated :: Assertion irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2
test/Haskoin/ScriptSpec.hs view
@@ -5,10 +5,12 @@ import Data.Aeson as A import Data.ByteString (ByteString) import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either import Data.List import Data.Maybe-import Data.Serialize as S import Data.String import Data.String.Conversions (cs) import Data.Text (Text)@@ -21,9 +23,9 @@ import Haskoin.Util import Haskoin.Util.Arbitrary import Haskoin.UtilSpec (readTestFile)+import Test.HUnit as HUnit import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit as HUnit import Test.QuickCheck import Text.Read @@ -89,21 +91,26 @@ decodeInput net (encodeInput si) `shouldBe` Right si prop "can sort multisig scripts" $ forAll arbitraryMSOutput $ \out ->- map S.encode (getOutputMulSigKeys (sortMulSig out)) `shouldSatisfy` \xs ->- xs == sort xs+ map+ (runPutS . serialize)+ (getOutputMulSigKeys (sortMulSig out))+ `shouldSatisfy`+ \xs -> xs == sort xs it "can decode inputs with empty signatures" $ do decodeInput net (Script [OP_0]) `shouldBe` Right (RegularInput (SpendPK TxSignatureEmpty)) decodeInput net (Script [opPushData ""]) `shouldBe` Right (RegularInput (SpendPK TxSignatureEmpty))- let pk =- derivePubKeyI $- wrapSecKey True $ fromJust $ secKey $ B.replicate 32 1- decodeInput net (Script [OP_0, opPushData $ S.encode pk]) `shouldBe`+ let pk = derivePubKeyI $+ wrapSecKey True $ fromJust $ secKey $ B.replicate 32 1+ decodeInput net (Script [OP_0, opPushData $ runPutS $ serialize pk])+ `shouldBe` Right (RegularInput (SpendPKHash TxSignatureEmpty pk))- decodeInput net (Script [OP_0, OP_0]) `shouldBe`+ decodeInput net (Script [OP_0, OP_0])+ `shouldBe` Right (RegularInput (SpendMulSig [TxSignatureEmpty]))- decodeInput net (Script [OP_0, OP_0, OP_0, OP_0]) `shouldBe`+ decodeInput net (Script [OP_0, OP_0, OP_0, OP_0])+ `shouldBe` Right (RegularInput (SpendMulSig $ replicate 3 TxSignatureEmpty)) scriptSpec :: Network -> Spec@@ -189,7 +196,7 @@ txI = TxIn { prevOutput = nullOutPoint- , scriptInput = S.encode $ Script [OP_0, OP_0]+ , scriptInput = runPutS $ serialize $ Script [OP_0, OP_0] , txInSequence = maxBound } @@ -216,7 +223,7 @@ replaceToken :: String -> String replaceToken str = case readMaybe $ "OP_" <> str of- Just opcode -> "0x" <> cs (encodeHex $ S.encode (opcode :: ScriptOp))+ Just opcode -> "0x" <> cs (encodeHex $ runPutS $ serialize (opcode :: ScriptOp)) _ -> str strictSigSpec :: Network -> Spec@@ -251,10 +258,10 @@ let tx = fromString txStr s = fromMaybe (error $ "Could not decode script: " <> cs scpStr) $- eitherToMaybe . S.decode =<< decodeHex (cs scpStr)+ eitherToMaybe . runGetS deserialize =<< decodeHex (cs scpStr) sh = fromIntegral shI res =- eitherToMaybe . S.decode . B.reverse =<<+ eitherToMaybe . runGetS deserialize . B.reverse =<< decodeHex (cs resStr) Just (txSigHash net tx s 0 i sh) `shouldBe` res @@ -275,9 +282,9 @@ let tx = fromString txStr s = fromMaybe (error $ "Could not decode script: " <> cs scpStr) $- eitherToMaybe . S.decode =<< decodeHex (cs scpStr)+ eitherToMaybe . runGetS deserialize =<< decodeHex (cs scpStr) sh = fromIntegral shI- res = eitherToMaybe . S.decode =<< decodeHex (cs resStr)+ res = eitherToMaybe . runGetS deserialize =<< decodeHex (cs resStr) Just (txSigHashForkId net tx s val i sh) `shouldBe` res sigHashSpec :: Network -> Spec@@ -359,7 +366,7 @@ where s = do s' <- decodeHex ops- eitherToMaybe $ S.decode s'+ eitherToMaybe $ runGetS deserialize s' b = do o <- s d <- eitherToMaybe $ decodeOutput o@@ -413,7 +420,7 @@ encodeScriptVector :: Assertion encodeScriptVector =- assertEqual "Encode script" res (encodeHex $ S.encode s)+ assertEqual "Encode script" res (encodeHex $ runPutS $ serialize s) where res = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58b\
test/Haskoin/Transaction/PartialSpec.hs view
@@ -4,14 +4,17 @@ import Control.Monad.Fail (MonadFail) import Data.ByteString (ByteString)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either (fromRight, isLeft, isRight) import Data.HashMap.Strict (fromList, singleton) import Data.Maybe (fromJust, isJust) import Data.Serialize as S import Data.Text (Text)-import Test.Hspec import Test.HUnit (Assertion, assertBool, assertEqual)+import Test.Hspec import Test.QuickCheck import Haskoin.Address@@ -150,10 +153,11 @@ hexScript :: Text -> ByteString hexScript =- either (error "Could not decode script") encodeScript . S.decode . fromJust . decodeHex+ either (error "Could not decode script") encodeScript .+ runGetS deserialize . fromJust . decodeHex where encodeScript :: Script -> ByteString- encodeScript = S.encode+ encodeScript = runPutS . serialize invalidVecTest :: Text -> Assertion invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT@@ -194,7 +198,12 @@ currTx = unfinalizedTx (txHash prevTx) prevTx = testUtxo [prevOut] prevOutScript = addressToScript (pubKeyAddr pubKey)- prevOut = TxOut { outValue = 200000000, scriptOutput = S.encode prevOutScript }+ prevOut =+ TxOut+ {+ outValue = 200000000,+ scriptOutput = runPutS (serialize prevOutScript)+ } h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll
test/Haskoin/TransactionSpec.hs view
@@ -2,14 +2,16 @@ module Haskoin.TransactionSpec (spec) where -import qualified Data.ByteString as B+import qualified Data.ByteString as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial import Data.Either import Data.Maybe-import Data.Serialize as S-import Data.String (fromString)+import Data.String (fromString) import Data.String.Conversions-import Data.Text (Text)-import Data.Word (Word32, Word64)+import Data.Text (Text)+import Data.Word (Word32, Word64) import Haskoin.Address import Haskoin.Constants import Haskoin.Keys@@ -17,9 +19,9 @@ import Haskoin.Transaction import Haskoin.Util import Haskoin.Util.Arbitrary+import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck-import Test.HUnit import Test.QuickCheck serialVals :: [SerialBox]@@ -94,7 +96,7 @@ testTxidVector (tid, tx) = assertEqual "txid" (Just tid) (txHashToHex . txHash <$> txM) where- txM = eitherToMaybe . S.decode =<< decodeHex tx+ txM = eitherToMaybe . runGetS deserialize =<< decodeHex tx txidVectors :: [(Text, Text)] txidVectors =@@ -156,7 +158,10 @@ testPKHashVector :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion testPKHashVector (is, os, res) =- assertEqual "Build PKHash Tx" (Right res) (encodeHex . S.encode <$> txE)+ assertEqual+ "Build PKHash Tx"+ (Right res)+ (encodeHex . runPutS . serialize <$> txE) where txE = buildAddrTx btc (map f is) os f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix@@ -223,7 +228,7 @@ where delta = pki + sum (map fst msi) guess = guessTxSize pki msi pkout msout- len = B.length $ S.encode tx+ len = B.length $ runPutS $ serialize tx ins = map f $ txIn tx f i = fromRight (error "Could not decode input") $@@ -231,7 +236,7 @@ pki = length $ filter isSpendPKHash ins msi = concatMap shData ins shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]- shData _ = []+ shData _ = [] out = map (fromRight (error "Could not decode transaction output") .