@@ -25,44 +25,8 @@ module Bitcoin.Transaction.Builder (
25
25
mergeTxInput ,
26
26
findSigInput ,
27
27
verifyStdInput ,
28
-
29
- -- * Coin Selection
30
- Coin (.. ),
31
- chooseCoins ,
32
- chooseCoinsSink ,
33
- chooseMSCoins ,
34
- chooseMSCoinsSink ,
35
- countMulSig ,
36
- greedyAddSink ,
37
- guessTxFee ,
38
- guessMSTxFee ,
39
- guessTxSize ,
40
- guessMSSize ,
41
28
) where
42
29
43
- import Control.Applicative ((<|>) )
44
- import Control.Arrow (first )
45
- import Control.Monad (foldM , unless )
46
- import Control.Monad.Identity (runIdentity )
47
- import Crypto.Secp256k1
48
- import qualified Data.ByteString as B
49
- import Data.Bytes.Get
50
- import Data.Bytes.Put
51
- import Data.Bytes.Serial
52
- import Data.Conduit (
53
- ConduitT ,
54
- Void ,
55
- await ,
56
- runConduit ,
57
- (.|) ,
58
- )
59
- import Data.Conduit.List (sourceList )
60
- import Data.Either (fromRight )
61
- import Data.List (nub )
62
- import Data.Maybe (catMaybes , fromJust , isJust )
63
- import Data.String.Conversions (cs )
64
- import Data.Text (Text )
65
- import Data.Word (Word64 )
66
30
import Bitcoin.Address
67
31
import Bitcoin.Crypto.Hash (Hash256 , addressHash )
68
32
import Bitcoin.Crypto.Signature
@@ -84,243 +48,21 @@ import Bitcoin.Transaction.Segwit (
84
48
viewWitnessProgram ,
85
49
)
86
50
import Bitcoin.Util
87
-
88
-
89
- -- | Any type can be used as a Coin if it can provide a value in Satoshi.
90
- -- The value is used in coin selection algorithms.
91
- class Coin c where
92
- coinValue :: c -> Word64
93
-
94
-
95
- -- | Coin selection algorithm for normal (non-multisig) transactions. This
96
- -- function returns the selected coins together with the amount of change to
97
- -- send back to yourself, taking the fee into account.
98
- chooseCoins ::
99
- Coin c =>
100
- -- | value to send
101
- Word64 ->
102
- -- | fee per byte
103
- Word64 ->
104
- -- | number of outputs (including change)
105
- Int ->
106
- -- | try to find better solutions
107
- Bool ->
108
- -- | list of ordered coins to choose from
109
- [c ] ->
110
- -- | coin selection and change
111
- Either String ([c ], Word64 )
112
- chooseCoins target fee nOut continue coins =
113
- runIdentity . runConduit $
114
- sourceList coins .| chooseCoinsSink target fee nOut continue
115
-
116
-
117
- -- | Coin selection algorithm for normal (non-multisig) transactions. This
118
- -- function returns the selected coins together with the amount of change to
119
- -- send back to yourself, taking the fee into account. This version uses a Sink
120
- -- for conduit-based coin selection.
121
- chooseCoinsSink ::
122
- (Monad m , Coin c ) =>
123
- -- | value to send
124
- Word64 ->
125
- -- | fee per byte
126
- Word64 ->
127
- -- | number of outputs (including change)
128
- Int ->
129
- -- | try to find better solution
130
- Bool ->
131
- -- | coin selection and change
132
- ConduitT c Void m (Either String ([c ], Word64 ))
133
- chooseCoinsSink target fee nOut continue
134
- | target > 0 =
135
- maybeToEither err
136
- <$> greedyAddSink target (guessTxFee fee nOut) continue
137
- | otherwise = return $ Left " chooseCoins: Target must be > 0"
138
- where
139
- err = " chooseCoins: No solution found"
140
-
141
-
142
- -- | Coin selection algorithm for multisig transactions. This function returns
143
- -- the selected coins together with the amount of change to send back to
144
- -- yourself, taking the fee into account. This function assumes all the coins
145
- -- are script hash outputs that send funds to a multisignature address.
146
- chooseMSCoins ::
147
- Coin c =>
148
- -- | value to send
149
- Word64 ->
150
- -- | fee per byte
151
- Word64 ->
152
- -- | m of n multisig
153
- (Int , Int ) ->
154
- -- | number of outputs (including change)
155
- Int ->
156
- -- | try to find better solution
157
- Bool ->
158
- [c ] ->
159
- -- | coin selection change amount
160
- Either String ([c ], Word64 )
161
- chooseMSCoins target fee ms nOut continue coins =
162
- runIdentity . runConduit $
163
- sourceList coins .| chooseMSCoinsSink target fee ms nOut continue
164
-
165
-
166
- -- | Coin selection algorithm for multisig transactions. This function returns
167
- -- the selected coins together with the amount of change to send back to
168
- -- yourself, taking the fee into account. This function assumes all the coins
169
- -- are script hash outputs that send funds to a multisignature address. This
170
- -- version uses a Sink if you need conduit-based coin selection.
171
- chooseMSCoinsSink ::
172
- (Monad m , Coin c ) =>
173
- -- | value to send
174
- Word64 ->
175
- -- | fee per byte
176
- Word64 ->
177
- -- | m of n multisig
178
- (Int , Int ) ->
179
- -- | number of outputs (including change)
180
- Int ->
181
- -- | try to find better solution
182
- Bool ->
183
- -- | coin selection and change
184
- ConduitT c Void m (Either String ([c ], Word64 ))
185
- chooseMSCoinsSink target fee ms nOut continue
186
- | target > 0 =
187
- maybeToEither err
188
- <$> greedyAddSink target (guessMSTxFee fee ms nOut) continue
189
- | otherwise = return $ Left " chooseMSCoins: Target must be > 0"
190
- where
191
- err = " chooseMSCoins: No solution found"
192
-
193
-
194
- -- | Select coins greedily by starting from an empty solution. If the 'continue'
195
- -- flag is set, the algorithm will try to find a better solution in the stream
196
- -- after a solution is found. If the next solution found is not strictly better
197
- -- than the previously found solution, the algorithm stops and returns the
198
- -- previous solution. If the continue flag is not set, the algorithm will return
199
- -- the first solution it finds in the stream.
200
- greedyAddSink ::
201
- (Monad m , Coin c ) =>
202
- -- | value to send
203
- Word64 ->
204
- -- | coin count to fee function
205
- (Int -> Word64 ) ->
206
- -- | try to find better solutions
207
- Bool ->
208
- -- | coin selection and change
209
- ConduitT c Void m (Maybe ([c ], Word64 ))
210
- greedyAddSink target guessFee continue =
211
- go [] 0 [] 0
212
- where
213
- -- The goal is the value we must reach (including the fee) for a certain
214
- -- amount of selected coins.
215
- goal c = target + guessFee c
216
- go acc aTot ps pTot =
217
- await >>= \ case
218
- -- A coin is available in the stream
219
- Just coin -> do
220
- let val = coinValue coin
221
- -- We have reached the goal using this coin
222
- if val + aTot >= goal (length acc + 1 )
223
- then -- If we want to continue searching for better solutions
224
-
225
- if continue
226
- then -- This solution is the first one or
227
- -- This solution is better than the previous one
228
-
229
- if pTot == 0 || val + aTot < pTot
230
- then -- Continue searching for better solutions in the stream
231
- go [] 0 (coin : acc) (val + aTot)
232
- else -- Otherwise, we stop here and return the previous
233
- -- solution
234
- return $ Just (ps, pTot - goal (length ps))
235
- else -- Otherwise, return this solution
236
-
237
- return $
238
- Just (coin : acc, val + aTot - goal (length acc + 1 ))
239
- else -- We have not yet reached the goal. Add the coin to the
240
- -- accumulator
241
- go (coin : acc) (val + aTot) ps pTot
242
- -- We reached the end of the stream
243
- Nothing ->
244
- return $
245
- if null ps
246
- then -- If no solution was found, return Nothing
247
- Nothing
248
- else -- If we have a solution, return it
249
- Just (ps, pTot - goal (length ps))
250
-
251
-
252
- -- | Estimate tranasction fee to pay based on transaction size estimation.
253
- guessTxFee :: Word64 -> Int -> Int -> Word64
254
- guessTxFee byteFee nOut nIn =
255
- byteFee * fromIntegral (guessTxSize nIn [] nOut 0 )
256
-
257
-
258
- -- | Same as 'guessTxFee' but for multisig transactions.
259
- guessMSTxFee :: Word64 -> (Int , Int ) -> Int -> Int -> Word64
260
- guessMSTxFee byteFee ms nOut nIn =
261
- byteFee * fromIntegral (guessTxSize 0 (replicate nIn ms) nOut 0 )
262
-
263
-
264
- -- | Computes an upper bound on the size of a transaction based on some known
265
- -- properties of the transaction.
266
- guessTxSize ::
267
- -- | number of regular transaction inputs
268
- Int ->
269
- -- | multisig m of n for each input
270
- [(Int , Int )] ->
271
- -- | number of P2PKH outputs
272
- Int ->
273
- -- | number of P2SH outputs
274
- Int ->
275
- -- | upper bound on transaction size
276
- Int
277
- guessTxSize pki msi pkout msout =
278
- 8 + inpLen + inp + outLen + out
279
- where
280
- inpLen =
281
- B. length
282
- . runPutS
283
- . serialize
284
- . VarInt
285
- . fromIntegral
286
- $ length msi + pki
287
- outLen =
288
- B. length
289
- . runPutS
290
- . serialize
291
- . VarInt
292
- . fromIntegral
293
- $ pkout + msout
294
- inp = pki * 148 + sum (map guessMSSize msi)
295
- -- (20: hash160) + (5: opcodes) +
296
- -- (1: script len) + (8: Word64)
297
- out =
298
- pkout * 34
299
- +
300
- -- (20: hash160) + (3: opcodes) +
301
- -- (1: script len) + (8: Word64)
302
- msout * 32
303
-
304
-
305
- -- | Size of a multisig P2SH input.
306
- guessMSSize :: (Int , Int ) -> Int
307
- guessMSSize (m, n) =
308
- -- OutPoint (36) + Sequence (4) + Script
309
- 40
310
- + fromIntegral (B. length $ runPutS . serialize $ VarInt $ fromIntegral scp)
311
- + scp
312
- where
313
- -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG
314
-
315
- rdm =
316
- fromIntegral
317
- . B. length
318
- . runPutS
319
- . serialize
320
- . opPushData
321
- $ B. replicate (n * 34 + 3 ) 0
322
- -- Redeem + m*sig + OP_0
323
- scp = rdm + m * 73 + 1
51
+ import Control.Applicative ((<|>) )
52
+ import Control.Arrow (first )
53
+ import Control.Monad (foldM , unless )
54
+ import Control.Monad.Identity (runIdentity )
55
+ import Crypto.Secp256k1
56
+ import qualified Data.ByteString as B
57
+ import Data.Bytes.Get
58
+ import Data.Bytes.Put
59
+ import Data.Bytes.Serial
60
+ import Data.Either (fromRight )
61
+ import Data.List (nub )
62
+ import Data.Maybe (catMaybes , fromJust , isJust )
63
+ import Data.String.Conversions (cs )
64
+ import Data.Text (Text )
65
+ import Data.Word (Word64 )
324
66
325
67
326
68
{- Build a new Tx -}
0 commit comments