Skip to content

Commit 1c43de8

Browse files
Merge pull request #38 from haskell-bitcoin/serialization-overhaul
Cuts down serialization logic
2 parents 43f505b + 49b948c commit 1c43de8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2379
-2147
lines changed

.hlint.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@
99
- "-fdefer-typed-holes"
1010
- "-Wno-typed-holes"
1111
within: []
12+
13+
- modules:
14+
- name: Data.ByteString
15+
as: BS
16+
- name: Data.ByteString.Lazy
17+
as: BSL

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8-
## 0.1.0
8+
## 0.1.0 - Forked from `haskoin-core` 0.21.2
99

1010
### Changed
1111

12-
- Forked from `haskoin-core` 0.21.1
1312
- Removed Bitcoin Cash support
13+
- Stripped down serialization code

benchmark/Main.hs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import qualified Data.Binary as Bin
1414
import Data.ByteString (ByteString)
1515
import qualified Data.ByteString.Lazy as BSL
1616
import Data.Proxy (Proxy (..))
17-
import Data.Serialize (Serialize)
18-
import qualified Data.Serialize as S
1917
import Data.Text (Text)
2018
import qualified Data.Text as Text
2119
import qualified Data.Text.IO as TIO
@@ -58,7 +56,7 @@ main = do
5856

5957
roundTrip ::
6058
forall a.
61-
(NFData a, Binary a, Serialize a) =>
59+
(NFData a, Binary a) =>
6260
Proxy a ->
6361
String ->
6462
Text ->
@@ -71,11 +69,6 @@ roundTrip _ label xHex =
7169
[ bench "encode" $ nf Bin.encode x
7270
, bench "decode" $ nf binDecode xBytes
7371
]
74-
, bgroup
75-
"cereal"
76-
[ bench "encode" $ nf S.encode x
77-
, bench "decode" $ nf (S.decode @a) xBytes
78-
]
7972
]
8073
where
8174
Just !xBytes = decodeHex $ Text.filter (/= '\n') xHex

bitcoin.cabal

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,9 @@ library
104104
, base >=4.9 && <5
105105
, base16 >=0.3.0.1
106106
, binary >=0.8.8
107-
, bytes >=0.17
108107
, bytestring >=0.10.10.0
109-
, cereal >=0.5.8
110108
, containers >=0.6.2.1
111-
, cryptonite >=0.26
109+
, cryptonite >=0.30
112110
, deepseq >=1.4.4.0
113111
, entropy >=0.4.1.5
114112
, hashable >=1.3.0.0
@@ -160,17 +158,13 @@ test-suite spec
160158
, base64 ==0.4.*
161159
, binary >=0.8.8
162160
, bitcoin
163-
, bytes >=0.17
164161
, bytestring >=0.10.10.0
165-
, cereal >=0.5.8
166162
, containers >=0.6.2.1
167-
, cryptonite >=0.26
163+
, cryptonite >=0.30
168164
, deepseq >=1.4.4.0
169165
, entropy >=0.4.1.5
170166
, hashable >=1.3.0.0
171167
, hspec >=2.7.1
172-
, lens >=4.18.1
173-
, lens-aeson >=1.1
174168
, memory >=0.15.0
175169
, murmur3 >=1.0.3
176170
, network >=3.1.1.1
@@ -185,7 +179,6 @@ test-suite spec
185179
, unordered-containers >=0.2.10.0
186180
, vector >=0.12.1.2
187181
default-language: Haskell2010
188-
build-tool-depends: hspec-discover:hspec-discover
189182

190183
benchmark benchmark
191184
type: exitcode-stdio-1.0
@@ -201,12 +194,10 @@ benchmark benchmark
201194
, base16 >=0.3.0.1
202195
, binary >=0.8.8
203196
, bitcoin
204-
, bytes >=0.17
205197
, bytestring >=0.10.10.0
206-
, cereal >=0.5.8
207198
, containers >=0.6.2.1
208199
, criterion >=1.5 && <1.7
209-
, cryptonite >=0.26
200+
, cryptonite >=0.30
210201
, deepseq >=1.4.4.0
211202
, entropy >=0.4.1.5
212203
, hashable >=1.3.0.0

package.yaml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@ dependencies:
2424
- base >=4.9 && <5
2525
- base16 >= 0.3.0.1
2626
- binary >= 0.8.8
27-
- bytes >= 0.17
2827
- bytestring >= 0.10.10.0
29-
- cereal >= 0.5.8
3028
- containers >= 0.6.2.1
31-
- cryptonite >= 0.26
29+
- cryptonite >= 0.30
3230
- deepseq >= 1.4.4.0
3331
- entropy >= 0.4.1.5
3432
- hashable >= 1.3.0.0
@@ -57,17 +55,13 @@ tests:
5755
spec:
5856
main: Spec.hs
5957
source-dirs: test
60-
verbatim:
61-
build-tool-depends: hspec-discover:hspec-discover
6258
dependencies:
6359
- aeson >= 1.4.6.0
6460
- base64 ^>= 0.4
6561
- bitcoin
6662
- hspec >= 2.7.1
6763
- HUnit >= 1.6.0.0
6864
- QuickCheck >= 2.13.2
69-
- lens-aeson >= 1.1
70-
- lens >= 4.18.1
7165
benchmarks:
7266
benchmark:
7367
main: Main.hs

src/Bitcoin/Address.hs

Lines changed: 81 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,36 @@ module Bitcoin.Address (
4545

4646
import Bitcoin.Address.Base58
4747
import Bitcoin.Address.Bech32
48-
import Bitcoin.Crypto
49-
import Bitcoin.Data
50-
import Bitcoin.Keys.Common
51-
import Bitcoin.Script
52-
import Bitcoin.Util
53-
import Control.Applicative
48+
import Bitcoin.Crypto (Hash160, Hash256, addressHash, addressHashL, sha256)
49+
import Bitcoin.Data (Network (..))
50+
import Bitcoin.Keys.Common (PubKeyI)
51+
import Bitcoin.Script (
52+
Script,
53+
ScriptInput (..),
54+
ScriptOutput (..),
55+
SimpleInput (SpendPKHash),
56+
decodeOutput,
57+
decodeOutputBS,
58+
encodeOutput,
59+
encodeOutputBS,
60+
toP2WSH,
61+
)
62+
import Bitcoin.Util (eitherToMaybe, encodeHex, maybeToEither)
63+
import qualified Bitcoin.Util as U
64+
import Control.Applicative ((<|>))
5465
import Control.Arrow (second)
55-
import Control.DeepSeq
56-
import Control.Monad
57-
import Data.Binary (Binary (..))
66+
import Control.DeepSeq (NFData)
67+
import Control.Monad ((<=<))
68+
import Data.Binary (Binary (..), Get, Put)
69+
import qualified Data.Binary as Bin
70+
import Data.Binary.Get (runGet)
71+
import qualified Data.Binary.Get as Get
72+
import Data.Binary.Put (runPut)
73+
import qualified Data.Binary.Put as Put
5874
import Data.ByteString (ByteString)
59-
import qualified Data.ByteString as B
60-
import Data.Bytes.Get
61-
import Data.Bytes.Put
62-
import Data.Bytes.Serial
63-
import Data.Hashable
64-
import Data.Maybe
65-
import Data.Serialize (Serialize (..))
75+
import qualified Data.ByteString as BS
76+
import qualified Data.ByteString.Lazy as BSL
77+
import Data.Hashable (Hashable)
6678
import Data.Text (Text)
6779
import qualified Data.Text as T
6880
import Data.Word (Word8)
@@ -100,50 +112,41 @@ data Address
100112
(Eq, Ord, Generic, Show, Read, Hashable, NFData)
101113

102114

103-
instance Serial Address where
104-
serialize (PubKeyAddress k) = do
105-
putWord8 0x00
106-
serialize k
107-
serialize (ScriptAddress s) = do
108-
putWord8 0x01
109-
serialize s
110-
serialize (WitnessPubKeyAddress h) = do
111-
putWord8 0x02
112-
serialize h
113-
serialize (WitnessScriptAddress s) = do
114-
putWord8 0x03
115-
serialize s
116-
serialize (WitnessAddress v d) = do
117-
putWord8 0x04
118-
putWord8 v
119-
putWord64be (fromIntegral (B.length d))
120-
putByteString d
121-
122-
123-
deserialize =
124-
getWord8 >>= \case
125-
0x00 -> PubKeyAddress <$> deserialize
126-
0x01 -> ScriptAddress <$> deserialize
127-
0x02 -> WitnessPubKeyAddress <$> deserialize
128-
0x03 -> WitnessScriptAddress <$> deserialize
115+
instance Binary Address where
116+
put = \case
117+
PubKeyAddress k -> do
118+
Put.putWord8 0x00
119+
put k
120+
ScriptAddress s -> do
121+
Put.putWord8 0x01
122+
put s
123+
WitnessPubKeyAddress h -> do
124+
Put.putWord8 0x02
125+
put h
126+
WitnessScriptAddress s -> do
127+
Put.putWord8 0x03
128+
put s
129+
WitnessAddress v d -> do
130+
Put.putWord8 0x04
131+
Put.putWord8 v
132+
Put.putWord64be (fromIntegral (BS.length d))
133+
Put.putByteString d
134+
135+
136+
get =
137+
Get.getWord8 >>= \case
138+
0x00 -> PubKeyAddress <$> get
139+
0x01 -> ScriptAddress <$> get
140+
0x02 -> WitnessPubKeyAddress <$> get
141+
0x03 -> WitnessScriptAddress <$> get
129142
0x04 ->
130143
WitnessAddress
131-
<$> getWord8
132-
<*> (getByteString . fromIntegral =<< getWord64be)
144+
<$> Get.getWord8
145+
<*> (Get.getByteString . fromIntegral =<< Get.getWord64be)
133146
b ->
134147
fail . T.unpack $
135148
"Could not decode address type byte: "
136-
<> encodeHex (B.singleton b)
137-
138-
139-
instance Serialize Address where
140-
put = serialize
141-
get = deserialize
142-
143-
144-
instance Binary Address where
145-
put = serialize
146-
get = deserialize
149+
<> encodeHex (BS.singleton b)
147150

148151

149152
-- | 'Address' pays to a public key hash.
@@ -178,17 +181,17 @@ isWitnessAddress _ = False
178181
-- | Convert address to human-readable string. Uses 'Base58', or 'Bech32'
179182
-- depending on network.
180183
addrToText :: Network -> Address -> Maybe Text
181-
addrToText net a@PubKeyAddress{} = Just . encodeBase58Check . runPutS $ base58put net a
182-
addrToText net a@ScriptAddress{} = Just . encodeBase58Check . runPutS $ base58put net a
184+
addrToText net a@PubKeyAddress{} = Just . encodeBase58Check . Put.runPut $ base58put net a
185+
addrToText net a@ScriptAddress{} = Just . encodeBase58Check . Put.runPut $ base58put net a
183186
addrToText net WitnessPubKeyAddress{getAddrHash160 = h} = do
184187
hrp <- getBech32Prefix net
185-
segwitEncode hrp 0 (B.unpack (runPutS $ serialize h))
188+
segwitEncode hrp 0 . BSL.unpack $ Bin.encode h
186189
addrToText net WitnessScriptAddress{getAddrHash256 = h} = do
187190
hrp <- getBech32Prefix net
188-
segwitEncode hrp 0 (B.unpack (runPutS $ serialize h))
191+
segwitEncode hrp 0 . BSL.unpack $ Bin.encode h
189192
addrToText net WitnessAddress{getAddrVersion = v, getAddrData = d} = do
190193
hrp <- getBech32Prefix net
191-
segwitEncode hrp v (B.unpack d)
194+
segwitEncode hrp v (BS.unpack d)
192195

193196

194197
-- | Parse 'Base58', or 'Bech32' address, depending on network.
@@ -200,24 +203,24 @@ textToAddr net txt =
200203
bech32ToAddr :: Network -> Text -> Maybe Address
201204
bech32ToAddr net txt = do
202205
hrp <- getBech32Prefix net
203-
(ver, bs) <- second B.pack <$> segwitDecode hrp txt
206+
(ver, bs) <- second BS.pack <$> segwitDecode hrp txt
204207
case ver of
205-
0 -> case B.length bs of
206-
20 -> WitnessPubKeyAddress <$> eitherToMaybe (runGetS deserialize bs)
207-
32 -> WitnessScriptAddress <$> eitherToMaybe (runGetS deserialize bs)
208+
0 -> case BS.length bs of
209+
20 -> WitnessPubKeyAddress <$> (eitherToMaybe . U.decode . BSL.fromStrict) bs
210+
32 -> WitnessScriptAddress <$> (eitherToMaybe . U.decode . BSL.fromStrict) bs
208211
_ -> Nothing
209212
_ -> Just $ WitnessAddress ver bs
210213

211214

212215
base58ToAddr :: Network -> Text -> Maybe Address
213216
base58ToAddr net txt =
214-
eitherToMaybe . runGetS (base58get net) =<< decodeBase58Check txt
217+
eitherToMaybe . U.runGet (base58get net) =<< decodeBase58Check txt
215218

216219

217-
base58get :: MonadGet m => Network -> m Address
220+
base58get :: Network -> Get Address
218221
base58get net = do
219-
pfx <- getWord8
220-
addr <- deserialize
222+
pfx <- Get.getWord8
223+
addr <- get
221224
f pfx addr
222225
where
223226
f x a
@@ -226,19 +229,19 @@ base58get net = do
226229
| otherwise = fail "Does not recognize address prefix"
227230

228231

229-
base58put :: MonadPut m => Network -> Address -> m ()
232+
base58put :: Network -> Address -> Put
230233
base58put net (PubKeyAddress h) = do
231-
putWord8 (getAddrPrefix net)
232-
serialize h
234+
Put.putWord8 (getAddrPrefix net)
235+
put h
233236
base58put net (ScriptAddress h) = do
234-
putWord8 (getScriptPrefix net)
235-
serialize h
237+
Put.putWord8 (getScriptPrefix net)
238+
put h
236239
base58put _ _ = error "Cannot serialize this address as Base58"
237240

238241

239242
-- | Obtain a standard pay-to-public-key-hash address from a public key.
240243
pubKeyAddr :: PubKeyI -> Address
241-
pubKeyAddr = PubKeyAddress . addressHash . runPutS . serialize
244+
pubKeyAddr = PubKeyAddress . addressHashL . Bin.encode
242245

243246

244247
-- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'.
@@ -249,7 +252,7 @@ p2pkhAddr = PubKeyAddress
249252
-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a
250253
-- public key.
251254
pubKeyWitnessAddr :: PubKeyI -> Address
252-
pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . runPutS . serialize
255+
pubKeyWitnessAddr = WitnessPubKeyAddress . addressHashL . Bin.encode
253256

254257

255258
-- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key.
@@ -259,9 +262,8 @@ pubKeyCompatWitnessAddr =
259262
. addressHash
260263
. encodeOutputBS
261264
. PayWitnessPKHash
262-
. addressHash
263-
. runPutS
264-
. serialize
265+
. addressHashL
266+
. Bin.encode
265267

266268

267269
-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a
@@ -316,7 +318,7 @@ addressToScript = encodeOutput . addressToOutput
316318

317319
-- | Encode address as output script in 'ByteString' form.
318320
addressToScriptBS :: Address -> ByteString
319-
addressToScriptBS = runPutS . serialize . addressToScript
321+
addressToScriptBS = U.encodeS . addressToScript
320322

321323

322324
-- | Decode an output script into an 'Address' if it has such representation.

0 commit comments

Comments
 (0)