haskoin-core 0.9.5 → 0.9.6
raw patch · 6 files changed
+146/−8 lines, 6 files
Files
- CHANGELOG.md +5/−0
- haskoin-core.cabal +2/−2
- src/Network/Haskoin/Address/Bech32.hs +1/−1
- src/Network/Haskoin/Network/Bloom.hs +46/−4
- test/Network/Haskoin/Address/Bech32Spec.hs +13/−0
- test/Network/Haskoin/NetworkSpec.hs +79/−1
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.9.6+### Added+- `bloomRelevantUpdate` implementation for Bloom filters (thanks to @IlyasRidhuan).+- Fix for Bech32 encoding (thanks to @pavel-main).+ ## 0.9.5 ### Added - Expose functions added in 0.9.4.
haskoin-core.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 5f5401cf4317b9dec2a9b9aa52cf78893d599e2b45dc74a8f2bce1653c8a83ad+-- hash: 263bc514478d37e2471bbc351ea84b803c76e518c55d7143d1e7927cd5f49e68 name: haskoin-core-version: 0.9.5+version: 0.9.6 synopsis: Bitcoin & Bitcoin Cash library for Haskell description: Haskoin Core is a complete Bitcoin and Bitcoin Cash library of functions and data types for Haskell developers. category: Bitcoin, Finance, Network
src/Network/Haskoin/Address/Bech32.hs view
@@ -131,7 +131,7 @@ bech32Encode :: HRP -> [Word5] -> Maybe Bech32 bech32Encode hrp dat = do guard $ checkHRP hrp- let dat' = dat ++ bech32CreateChecksum hrp dat+ let dat' = dat ++ bech32CreateChecksum (T.toLower hrp) dat rest = map (charset !) dat' result = T.concat [T.toLower hrp, T.pack "1", T.pack rest] guard $ T.length result <= maxBech32Length
src/Network/Haskoin/Network/Bloom.hs view
@@ -24,6 +24,7 @@ , isBloomEmpty , isBloomFull , acceptsFilters+ , bloomRelevantUpdate ) where import Control.Monad (forM_, replicateM)@@ -33,14 +34,16 @@ import qualified Data.Foldable as F import Data.Hash.Murmur (murmur3) import qualified Data.Sequence as S-import Data.Serialize (Serialize, get, put)+import Data.Serialize (Serialize, get, put,encode) import Data.Serialize.Get (getByteString, getWord32le, getWord8) import Data.Serialize.Put (putByteString, putWord32le, putWord8) import Data.Word import Network.Haskoin.Network.Common-+import Network.Haskoin.Script.Common+import Network.Haskoin.Transaction.Common+import Data.List (foldl') -- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% maxBloomSize :: Int maxBloomSize = 36000@@ -193,8 +196,47 @@ isSet i = S.index s (fromIntegral $ i `shiftR` 3) .&. (bitMask !! fromIntegral (7 .&. i)) /= 0 --- TODO: Write bloomRelevantUpdate--- bloomRelevantUpdate :: BloomFilter -> Tx -> Hash256 -> Maybe BloomFilter+-- | Checks if any of the outputs of a tx is in the current bloom filter.+-- If it is, add the txid and vout as an outpoint (i.e. so that +-- a future tx that spends the output won't be missed).+bloomRelevantUpdate :: BloomFilter + -- ^ Bloom filter+ -> Tx + -- ^ Tx that may (or may not) have relevant outputs+ -> Maybe BloomFilter + -- ^ Returns an updated bloom filter adding relevant output+bloomRelevantUpdate bfilter tx+ | isBloomFull bfilter || isBloomEmpty bfilter = Nothing+ | bloomFlags bfilter == BloomUpdateNone = Nothing+ | length matchOuts > 0 = Just $ foldl' (addRelevant) bfilter matchOuts+ | otherwise = Nothing+ where+ -- TxHash if we end up inserting an outpoint+ h = txHash tx+ -- Decode the scriptOutpus and add vOuts in case we make them outpoints+ decodedOutputScripts = traverse (decodeOutputBS . scriptOutput) $ txOut tx+ err = error $ "Error Decoding output script"+ idxOutputScripts = either (const err) (zip [0..]) decodedOutputScripts+ -- Check if any txOuts were contained in the bloom filter+ matchFilter = filter (\(_,op) -> bloomContains bfilter $ encodeScriptOut op)+ matchOuts = matchFilter idxOutputScripts++ addRelevant :: BloomFilter -> (Word32,ScriptOutput) -> BloomFilter+ addRelevant bfilter (id,scriptOut) = case (bloomFlags bfilter,scriptType) of+ -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig+ (_, True) -> bloomInsert bfilter outpoint+ (BloomUpdateAll, _ ) -> bloomInsert bfilter outpoint+ _ -> error "Error Updating Bloom Filter with relevant outpoint"+ where+ outpoint = encode $ 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 getOutputData) = encode getOutputData+ encodeScriptOut outputHash = (encode . getOutputHash) outputHash -- | Returns True if the filter is empty (all bytes set to 0x00) isBloomEmpty :: BloomFilter -> Bool
test/Network/Haskoin/Address/Bech32Spec.hs view
@@ -28,6 +28,8 @@ it "should be a valid address" $ forM_ validAddresses testValidAddress it "should be an invalid address" $ forM_ invalidAddresses testInvalidAddress+ it "should be same for the input in different case" $+ all (== Just "test12hrzfj") . map (flip bech32Encode []) $ hrpCaseVariants describe "more encoding/decoding cases" $ do it "length > 90" $ assert $@@ -141,3 +143,14 @@ , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv" , "bc1gmk9yu" ]++hrpCaseVariants:: [Text]+hrpCaseVariants = map T.pack hrpTestPermutations++hrpTestPermutations :: [String]+hrpTestPermutations = do+ a <- ['t', 'T']+ b <- ['e', 'E']+ c <- ['s', 'S']+ d <- ['t', 'T']+ return [a, b, c, d]
test/Network/Haskoin/NetworkSpec.hs view
@@ -11,8 +11,10 @@ import Network.Haskoin.Network import Network.Haskoin.Test import Network.Haskoin.Util+import Network.Haskoin.Transaction.Common import Test.Hspec-import Test.HUnit (Assertion, assertBool)+import Test.HUnit (Assertion, assertBool,+ assertEqual) import Test.QuickCheck spec :: Spec@@ -22,6 +24,9 @@ it "bloom filter vector 1" bloomFilter1 it "bloom filter vector 2" bloomFilter2 it "bloom filter vector 3" bloomFilter3+ describe "relevant bloom filter update" $ do+ it "Relevant Update" relevantOutputUpdated+ it "Irrelevant Update" irrelevantOutputNotUpdated describe "serialization of protocol types" $ do it "encodes and decodes varint" $ property $ forAll arbitraryVarInt cerealID@@ -107,3 +112,76 @@ testPutGet :: Eq a => Get a -> Putter a -> a -> Bool testPutGet g p a = runGet g (runPut (p a)) == Right a++relevantOutputUpdated :: Assertion+relevantOutputUpdated = assertBool "Bloom filter output updated" $+ any (bloomContains bf2) spendTxInput+ where+ bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+ relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"+ bf1 = bloomInsert bf0 relevantOutputHash+ bf2 = fromJust $ bloomRelevantUpdate bf1 relevantTx+ spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx ++irrelevantOutputNotUpdated :: Assertion+irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2 + where+ bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+ relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"+ bf1 = bloomInsert bf0 relevantOutputHash+ bf2 = bloomRelevantUpdate bf1 unrelatedTx+ spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx ++-- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)+relevantTx :: Tx+relevantTx = Tx+ { txVersion = 1+ , txIn = [ TxIn+ { prevOutput = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1+ , scriptInput = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6"+ , txInSequence = 4294967295+ } ]+ , txOut =+ [ TxOut { outValue = 100000000, scriptOutput = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac" }+ , TxOut { outValue = 107980000, scriptOutput = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac" }+ ]+ , txWitness = []+ , txLockTime = 0+ }++-- Transaction that spends above (fd6e3b693b844aa431fad46765c1aa019a6b13aebfa9dae916b3ffa43283a300)+spendRelevantTx :: Tx+spendRelevantTx = Tx+ { txVersion = 1+ , txIn = [ TxIn+ { prevOutput = OutPoint "57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9" 0+ , scriptInput = fromJust $ decodeHex "483045022100ecc334821e4e94cc2fdc841d5ad147d5bb942b993ba81460cc446e0410afa811022015fcbc542b734dbb61a05ec06012095096de5839c50808fe56f2b315e877c20d012103fb64e5792fa586172339b776b7017d3d529358cb73be6406a1fc994228d14f88"+ , txInSequence = 4294967295+ }, TxIn+ { prevOutput = OutPoint "cfee6a8d6e68e8fd16df6fff010afffcd19d7e075aa7b707dd1bae6adc420042" 0+ , scriptInput = fromJust $ decodeHex "47304402200e6bb95fa606f254d17089d83c4ceeb19c5d1699b4faddcd4f1f1568286e6b650220087fb8439f31e1b30e47710d095422405f601d6151f2f93e125e1a08a6e29ad4012103b49252e8fc6d5b49c8d14ee71fab45591df4a126a6c453c724f3d356e38f0cee"+ , txInSequence = 4294967295+ } ]+ , txOut =+ [ TxOut { outValue = 3851100, scriptOutput = fromJust $ decodeHex "76a914a297cae82a9a3b932bf023ae274fe2585295c9ca88ac" }+ , TxOut { outValue = 111000000, scriptOutput = fromJust $ decodeHex "76a9148f952c38600a61385974acc30a64f74407f9801488ac" }+ ]+ , txWitness = []+ , txLockTime = 0+ }++-- This random transaction is unrelated to the others+unrelatedTx :: Tx+unrelatedTx = Tx+ { txVersion = 1+ , txIn = [ TxIn+ { prevOutput = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20+ , scriptInput = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1"+ , txInSequence = 4294967295+ }]+ , txOut =+ [ TxOut { outValue = 12600000, scriptOutput = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac" }+ ]+ , txWitness = []+ , txLockTime = 0+ }