monacoin-ruby 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/bitcoin.rb +933 -0
- data/lib/bitcoin/bloom_filter.rb +125 -0
- data/lib/bitcoin/builder.rb +494 -0
- data/lib/bitcoin/connection.rb +130 -0
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ext_key.rb +191 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +75 -0
- data/lib/bitcoin/ffi/openssl.rb +388 -0
- data/lib/bitcoin/ffi/secp256k1.rb +266 -0
- data/lib/bitcoin/key.rb +280 -0
- data/lib/bitcoin/litecoin.rb +83 -0
- data/lib/bitcoin/logger.rb +86 -0
- data/lib/bitcoin/protocol.rb +189 -0
- data/lib/bitcoin/protocol/address.rb +50 -0
- data/lib/bitcoin/protocol/alert.rb +46 -0
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +285 -0
- data/lib/bitcoin/protocol/handler.rb +43 -0
- data/lib/bitcoin/protocol/parser.rb +194 -0
- data/lib/bitcoin/protocol/partial_merkle_tree.rb +61 -0
- data/lib/bitcoin/protocol/reject.rb +38 -0
- data/lib/bitcoin/protocol/script_witness.rb +31 -0
- data/lib/bitcoin/protocol/tx.rb +587 -0
- data/lib/bitcoin/protocol/txin.rb +142 -0
- data/lib/bitcoin/protocol/txout.rb +95 -0
- data/lib/bitcoin/protocol/version.rb +88 -0
- data/lib/bitcoin/script.rb +1656 -0
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +3 -0
- metadata +32 -1
@@ -0,0 +1,125 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
class BloomFilter
|
3
|
+
SEED_SHIFT = 0xfba4c795
|
4
|
+
BIT_MASK = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]
|
5
|
+
|
6
|
+
MAX_FILTER_SIZE = 36000
|
7
|
+
MAX_HASH_FUNCS = 50
|
8
|
+
|
9
|
+
# flags for filterload message
|
10
|
+
BLOOM_UPDATE_NONE = 0
|
11
|
+
BLOOM_UPDATE_ALL = 1
|
12
|
+
BLOOM_UPDATE_P2PUBKEY_ONLY = 2
|
13
|
+
|
14
|
+
attr_reader :filter, :nfunc, :tweak
|
15
|
+
|
16
|
+
def initialize(elements, fp_rate, tweak)
|
17
|
+
init_filter(elements, fp_rate)
|
18
|
+
@tweak = tweak
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_data(data)
|
22
|
+
@nfunc.times.each do |fi|
|
23
|
+
idx = calc_index(data, fi)
|
24
|
+
i = idx / 8
|
25
|
+
@filter[i] = (@filter[i].ord | BIT_MASK[idx % 8]).chr
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def contains?(data)
|
30
|
+
@nfunc.times.all? do |fi|
|
31
|
+
idx = calc_index(data, fi)
|
32
|
+
i = idx / 8
|
33
|
+
@filter[i].ord & BIT_MASK[idx % 8] != 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_address(address)
|
38
|
+
add_data(Bitcoin.hash160_from_address(address).htb)
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_outpoint(prev_tx_hash, prev_output)
|
42
|
+
add_data(prev_tx_hash.htb_reverse + [prev_output].pack('V'))
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
#
|
48
|
+
# calculate filter size and number of funcs.
|
49
|
+
# See: https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki
|
50
|
+
#
|
51
|
+
def init_filter(elements, fp_rate)
|
52
|
+
ln2 = Math.log(2)
|
53
|
+
|
54
|
+
# using #ceil instead of #floor may be better, but it's bitcoinj's way
|
55
|
+
|
56
|
+
calc_m = (-Math.log(fp_rate) * elements / ln2 / ln2 / 8).floor;
|
57
|
+
@filter_size = [1, [calc_m, MAX_FILTER_SIZE].min].max;
|
58
|
+
@filter = "\x00" * @filter_size
|
59
|
+
|
60
|
+
calc_k = (@filter_size * 8 * ln2 / elements).floor
|
61
|
+
@nfunc = [1, [calc_k, MAX_HASH_FUNCS].min].max
|
62
|
+
end
|
63
|
+
|
64
|
+
def rotate_left32(x, r)
|
65
|
+
return (x << r) | (x >> (32 - r))
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# calculate MurmurHash3
|
70
|
+
# See: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
|
71
|
+
#
|
72
|
+
def calc_index(data, hash_index)
|
73
|
+
object = data.bytes
|
74
|
+
h1 = (hash_index * SEED_SHIFT + @tweak) & 0xffffffff
|
75
|
+
c1 = 0xcc9e2d51
|
76
|
+
c2 = 0x1b873593
|
77
|
+
|
78
|
+
num_blocks = (object.length / 4) * 4
|
79
|
+
i = 0
|
80
|
+
# body
|
81
|
+
while i < num_blocks
|
82
|
+
k1 = (object[i] & 0xFF) |
|
83
|
+
((object[i + 1] & 0xFF) << 8) |
|
84
|
+
((object[i + 2] & 0xFF) << 16) |
|
85
|
+
((object[i + 3] & 0xFF) << 24)
|
86
|
+
|
87
|
+
k1 *= c1; k1 &= 0xffffffff
|
88
|
+
k1 = rotate_left32(k1, 15)
|
89
|
+
k1 *= c2; k1 &= 0xffffffff
|
90
|
+
|
91
|
+
h1 ^= k1
|
92
|
+
h1 = rotate_left32(h1, 13)
|
93
|
+
h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff
|
94
|
+
|
95
|
+
i += 4
|
96
|
+
end
|
97
|
+
|
98
|
+
k1 = 0
|
99
|
+
flg = object.length & 3
|
100
|
+
if flg >= 3
|
101
|
+
k1 ^= (object[num_blocks + 2] & 0xff) << 16
|
102
|
+
end
|
103
|
+
if flg >= 2
|
104
|
+
k1 ^= (object[num_blocks + 1] & 0xff) << 8
|
105
|
+
end
|
106
|
+
if flg >= 1
|
107
|
+
k1 ^= (object[num_blocks] & 0xff)
|
108
|
+
k1 *= c1; k1 &= 0xffffffff
|
109
|
+
k1 = rotate_left32(k1, 15)
|
110
|
+
k1 *= c2; k1 &= 0xffffffff
|
111
|
+
h1 ^= k1
|
112
|
+
end
|
113
|
+
|
114
|
+
# finalization
|
115
|
+
h1 ^= object.length
|
116
|
+
h1 ^= h1 >> 16
|
117
|
+
h1 *= 0x85ebca6b; h1 &= 0xffffffff
|
118
|
+
h1 ^= h1 >> 13
|
119
|
+
h1 *= 0xc2b2ae35; h1 &= 0xffffffff
|
120
|
+
h1 ^= h1 >> 16
|
121
|
+
|
122
|
+
return (h1 & 0xffffffff) % (@filter_size * 8)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,494 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
|
5
|
+
# Optional DSL to help create blocks and transactions.
|
6
|
+
#
|
7
|
+
# see also BlockBuilder, TxBuilder, TxInBuilder, TxOutBuilder, ScriptBuilder
|
8
|
+
module Builder
|
9
|
+
|
10
|
+
# build a Bitcoin::Protocol::Block matching the given +target+.
|
11
|
+
# see BlockBuilder for details.
|
12
|
+
def build_block(target = "00".ljust(64, 'f'))
|
13
|
+
c = BlockBuilder.new
|
14
|
+
yield c
|
15
|
+
c.block(target)
|
16
|
+
end
|
17
|
+
|
18
|
+
# build a Bitcoin::Protocol::Tx.
|
19
|
+
# see TxBuilder for details.
|
20
|
+
def build_tx opts = {}
|
21
|
+
c = TxBuilder.new
|
22
|
+
yield c
|
23
|
+
c.tx opts
|
24
|
+
end
|
25
|
+
|
26
|
+
# build a Bitcoin::Script.
|
27
|
+
# see ScriptBuilder for details.
|
28
|
+
def script
|
29
|
+
c = ScriptBuilder.new
|
30
|
+
yield c
|
31
|
+
c.script
|
32
|
+
end
|
33
|
+
|
34
|
+
# DSL to create a Bitcoin::Protocol::Block used by Builder#create_block.
|
35
|
+
# block = blk("00".ljust(32, 'f')) do |b|
|
36
|
+
# b.prev_block "\x00"*32
|
37
|
+
# b.tx do |t|
|
38
|
+
# t.input {|i| i.coinbase }
|
39
|
+
# t.output do |o|
|
40
|
+
# o.value 5000000000;
|
41
|
+
# o.to Bitcoin::Key.generate.addr
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# See Bitcoin::Builder::TxBuilder for details on building transactions.
|
47
|
+
class BlockBuilder
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@block = P::Block.new(nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
# specify block version. this is usually not necessary. defaults to 1.
|
54
|
+
def version v
|
55
|
+
@version = v
|
56
|
+
end
|
57
|
+
|
58
|
+
# set the hash of the previous block.
|
59
|
+
def prev_block hash
|
60
|
+
@prev_block = hash
|
61
|
+
end
|
62
|
+
|
63
|
+
# set the block timestamp (defaults to current time).
|
64
|
+
def time time
|
65
|
+
@time = time
|
66
|
+
end
|
67
|
+
|
68
|
+
# add transactions to the block (see TxBuilder).
|
69
|
+
def tx tx = nil
|
70
|
+
tx ||= ( c = TxBuilder.new; yield c; c.tx )
|
71
|
+
@block.tx << tx
|
72
|
+
tx
|
73
|
+
end
|
74
|
+
|
75
|
+
# create the block according to values specified via DSL.
|
76
|
+
def block target
|
77
|
+
@block.ver = @version || 1
|
78
|
+
@block.prev_block = @prev_block.htb.reverse
|
79
|
+
@block.mrkl_root = @mrkl_root
|
80
|
+
@block.time = @time || Time.now.to_i
|
81
|
+
@block.nonce = 0
|
82
|
+
@block.mrkl_root = Bitcoin.hash_mrkl_tree(@block.tx.map(&:hash)).last.htb.reverse
|
83
|
+
find_hash(target)
|
84
|
+
block = P::Block.new(@block.to_payload)
|
85
|
+
raise "Payload Error" unless block.to_payload == @block.to_payload
|
86
|
+
block
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# increment nonce/time to find a block hash matching the +target+.
|
92
|
+
def find_hash target
|
93
|
+
@block.bits = Bitcoin.encode_compact_bits(target)
|
94
|
+
t = Time.now
|
95
|
+
@block.recalc_block_hash
|
96
|
+
until @block.hash.to_i(16) < target.to_i(16)
|
97
|
+
@block.nonce += 1
|
98
|
+
@block.recalc_block_hash
|
99
|
+
if @block.nonce == 100000
|
100
|
+
if t
|
101
|
+
tt = 1 / ((Time.now - t) / 100000) / 1000
|
102
|
+
print "\r%.2f khash/s" % tt
|
103
|
+
end
|
104
|
+
t = Time.now
|
105
|
+
@block.time = Time.now.to_i
|
106
|
+
@block.nonce = 0
|
107
|
+
$stdout.flush
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# DSL to create Bitcoin::Protocol::Tx used by Builder#build_tx.
|
115
|
+
# tx = tx do |t|
|
116
|
+
# t.input do |i|
|
117
|
+
# i.prev_out prev_tx, 0
|
118
|
+
# i.signature_key key
|
119
|
+
# end
|
120
|
+
# t.output do |o|
|
121
|
+
# o.value 12345 # 0.00012345 BTC
|
122
|
+
# o.to key.addr
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# Signs every input that has a signature key and where the previous outputs
|
127
|
+
# pk_script is known. If unable to sign, the resulting txin will include
|
128
|
+
# the #sig_hash that needs to be signed.
|
129
|
+
#
|
130
|
+
# See TxInBuilder and TxOutBuilder for details on how to build in/outputs.
|
131
|
+
class TxBuilder
|
132
|
+
|
133
|
+
def initialize
|
134
|
+
@tx = P::Tx.new(nil)
|
135
|
+
@tx.ver, @tx.lock_time = 1, 0
|
136
|
+
@ins, @outs = [], []
|
137
|
+
end
|
138
|
+
|
139
|
+
# specify tx version. this is usually not necessary. defaults to 1.
|
140
|
+
def version n
|
141
|
+
@tx.ver = n
|
142
|
+
end
|
143
|
+
|
144
|
+
# specify tx lock_time. this is usually not necessary. defaults to 0.
|
145
|
+
def lock_time n
|
146
|
+
@tx.lock_time = n
|
147
|
+
end
|
148
|
+
|
149
|
+
# add an input to the transaction (see TxInBuilder).
|
150
|
+
def input
|
151
|
+
c = TxInBuilder.new
|
152
|
+
yield c
|
153
|
+
@ins << c
|
154
|
+
end
|
155
|
+
|
156
|
+
# add an output to the transaction (see TxOutBuilder).
|
157
|
+
def output value = nil, recipient = nil, type = :address
|
158
|
+
c = TxOutBuilder.new
|
159
|
+
c.value(value) if value
|
160
|
+
c.to(recipient, type) if recipient
|
161
|
+
yield c if block_given?
|
162
|
+
@outs << c
|
163
|
+
end
|
164
|
+
|
165
|
+
# Create the transaction according to values specified via DSL.
|
166
|
+
# Sign each input that has a signature key specified. If there is
|
167
|
+
# no key, store the sig_hash in the input, so it can easily be
|
168
|
+
# signed later.
|
169
|
+
#
|
170
|
+
# When :change_address and :input_value options are given, it will
|
171
|
+
# automatically create a change output sending the remaining funds
|
172
|
+
# to the given address. The :leave_fee option can be used in this
|
173
|
+
# case to specify a tx fee that should be left unclaimed by the
|
174
|
+
# change output.
|
175
|
+
def tx opts = {}
|
176
|
+
return @tx if @tx.hash
|
177
|
+
|
178
|
+
if opts[:change_address] && !opts[:input_value]
|
179
|
+
raise "Must give 'input_value' when auto-generating change output!"
|
180
|
+
end
|
181
|
+
@ins.each {|i| @tx.add_in(i.txin) }
|
182
|
+
@outs.each {|o| @tx.add_out(o.txout) }
|
183
|
+
if opts[:change_address]
|
184
|
+
output_value = @tx.out.map(&:value).inject(:+) || 0
|
185
|
+
change_value = opts[:input_value] - output_value
|
186
|
+
if opts[:leave_fee]
|
187
|
+
fee = @tx.minimum_block_fee + (opts[:extra_fee] || 0)
|
188
|
+
if change_value >= fee
|
189
|
+
change_value -= fee
|
190
|
+
else
|
191
|
+
change_value = 0
|
192
|
+
end
|
193
|
+
end
|
194
|
+
if change_value > 0
|
195
|
+
script = Script.to_address_script(opts[:change_address])
|
196
|
+
@tx.add_out(P::TxOut.new(change_value, script))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
@ins.each_with_index do |inc, i|
|
201
|
+
sign_input(i, inc)
|
202
|
+
end
|
203
|
+
|
204
|
+
# run our tx through an encode/decode cycle to make sure that the binary format is sane
|
205
|
+
raise "Payload Error" unless P::Tx.new(@tx.to_witness_payload).to_payload == @tx.to_payload
|
206
|
+
@tx.instance_eval do
|
207
|
+
@payload = to_payload
|
208
|
+
@hash = hash_from_payload(@payload)
|
209
|
+
end
|
210
|
+
|
211
|
+
@tx
|
212
|
+
end
|
213
|
+
|
214
|
+
# coinbase inputs don't need to be signed, they only include the given +coinbase_data+
|
215
|
+
def include_coinbase_data i, inc
|
216
|
+
script_sig = [inc.coinbase_data].pack("H*")
|
217
|
+
@tx.in[i].script_sig_length = script_sig.bytesize
|
218
|
+
@tx.in[i].script_sig = script_sig
|
219
|
+
end
|
220
|
+
|
221
|
+
def sig_hash_and_all_keys_exist?(inc, sig_script)
|
222
|
+
return false unless @sig_hash && inc.has_keys?
|
223
|
+
script = Bitcoin::Script.new(sig_script)
|
224
|
+
return true if script.is_hash160? || script.is_pubkey? || script.is_witness_v0_keyhash? || (Bitcoin.namecoin? && script.is_namecoin?)
|
225
|
+
if script.is_multisig?
|
226
|
+
return inc.has_multiple_keys? && inc.key.size >= script.get_signatures_required
|
227
|
+
end
|
228
|
+
raise "Script type must be hash160, pubkey, p2wpkh or multisig"
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_empty_script_sig_to_input(i)
|
232
|
+
@tx.in[i].script_sig_length = 0
|
233
|
+
@tx.in[i].script_sig = ""
|
234
|
+
# add the sig_hash that needs to be signed, so it can be passed on to a signing device
|
235
|
+
@tx.in[i].sig_hash = @sig_hash
|
236
|
+
# add the address the sig_hash needs to be signed with as a convenience for the signing device
|
237
|
+
@tx.in[i].sig_address = Script.new(@prev_script).get_address if @prev_script
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_script_sig(inc)
|
241
|
+
if inc.has_multiple_keys?
|
242
|
+
# multiple keys given, generate signature for each one
|
243
|
+
sigs = inc.sign(@sig_hash)
|
244
|
+
if redeem_script = inc.instance_eval { @redeem_script }
|
245
|
+
# when a redeem_script was specified, assume we spend a p2sh multisig script
|
246
|
+
script_sig = Script.to_p2sh_multisig_script_sig(redeem_script, sigs)
|
247
|
+
else
|
248
|
+
# when no redeem_script is given, do a regular multisig spend
|
249
|
+
script_sig = Script.to_multisig_script_sig(*sigs)
|
250
|
+
end
|
251
|
+
else
|
252
|
+
# only one key given, generate signature and script_sig
|
253
|
+
sig = inc.sign(@sig_hash)
|
254
|
+
script_sig = Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
|
255
|
+
end
|
256
|
+
return script_sig
|
257
|
+
end
|
258
|
+
|
259
|
+
# Sign input number +i+ with data from given +inc+ object (a TxInBuilder).
|
260
|
+
def sign_input i, inc
|
261
|
+
if @tx.in[i].coinbase?
|
262
|
+
include_coinbase_data(i, inc)
|
263
|
+
else
|
264
|
+
@prev_script = inc.instance_variable_get(:@prev_out_script)
|
265
|
+
|
266
|
+
# get the signature script; use +redeem_script+ if given
|
267
|
+
# (indicates spending a p2sh output), otherwise use the prev_script
|
268
|
+
sig_script = inc.instance_eval { @redeem_script }
|
269
|
+
sig_script ||= @prev_script
|
270
|
+
|
271
|
+
# when a sig_script was found, generate the sig_hash to be signed
|
272
|
+
if sig_script
|
273
|
+
script = Script.new(sig_script)
|
274
|
+
if script.is_witness_v0_keyhash?
|
275
|
+
@sig_hash = @tx.signature_hash_for_witness_input(i, sig_script, inc.value)
|
276
|
+
else
|
277
|
+
@sig_hash = @tx.signature_hash_for_input(i, sig_script)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# when there is a sig_hash and one or more signature_keys were specified
|
282
|
+
if sig_hash_and_all_keys_exist?(inc, sig_script)
|
283
|
+
# add the script_sig to the txin
|
284
|
+
if script.is_witness_v0_keyhash? # for p2wpkh
|
285
|
+
@tx.in[i].script_witness.stack << inc.sign(@sig_hash) + [Script::SIGHASH_TYPE[:all]].pack("C")
|
286
|
+
@tx.in[i].script_witness.stack << inc.key.pub.htb
|
287
|
+
else
|
288
|
+
@tx.in[i].script_sig = get_script_sig(inc)
|
289
|
+
end
|
290
|
+
# double-check that the script_sig is valid to spend the given prev_script
|
291
|
+
raise "Signature error" if @prev_script && !@tx.verify_input_signature(i, @prev_script)
|
292
|
+
elsif inc.has_multiple_keys?
|
293
|
+
raise "Keys missing for multisig signing"
|
294
|
+
else
|
295
|
+
# no sig_hash, add an empty script_sig.
|
296
|
+
add_empty_script_sig_to_input(i)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
# Randomize the outputs using SecureRandom
|
302
|
+
def randomize_outputs
|
303
|
+
@outs.sort_by!{ SecureRandom.random_bytes(4).unpack("I")[0] }
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# Create a Bitcoin::Protocol::TxIn used by TxBuilder#input.
|
308
|
+
#
|
309
|
+
# Inputs need the transaction hash and the index of the output they spend.
|
310
|
+
# You can pass either the transaction, or just its hash (in hex form).
|
311
|
+
# To sign the input, builder also needs the pk_script of the previous output.
|
312
|
+
# If you specify a tx hash instead of the whole tx, you need to specify the
|
313
|
+
# output script separately.
|
314
|
+
#
|
315
|
+
# t.input do |i|
|
316
|
+
# i.prev_out prev_tx # previous transaction
|
317
|
+
# i.prev_out_index 0 # index of previous output
|
318
|
+
# i.signature_key key # Bitcoin::Key used to sign the input
|
319
|
+
# end
|
320
|
+
#
|
321
|
+
# t.input {|i| i.prev_out prev_tx, 0 }
|
322
|
+
#
|
323
|
+
# If you want to spend a p2sh output, you also need to specify the +redeem_script+.
|
324
|
+
#
|
325
|
+
# t.input do |i|
|
326
|
+
# i.prev_out prev_tx, 0
|
327
|
+
# i.redeem_script prev_out.redeem_script
|
328
|
+
# end
|
329
|
+
#
|
330
|
+
# If you want to spend a multisig output, just provide an array of keys to #signature_key.
|
331
|
+
class TxInBuilder
|
332
|
+
attr_reader :prev_tx, :prev_script, :redeem_script, :key, :coinbase_data, :prev_out_value
|
333
|
+
|
334
|
+
def initialize
|
335
|
+
@txin = P::TxIn.new
|
336
|
+
@prev_out_hash = "\x00" * 32
|
337
|
+
@prev_out_index = 0
|
338
|
+
end
|
339
|
+
|
340
|
+
# Previous transaction that contains the output we want to use.
|
341
|
+
# You can either pass the transaction, or just the tx hash.
|
342
|
+
# If you pass only the hash, you need to pass the previous outputs
|
343
|
+
# +script+ separately if you want the txin to be signed.
|
344
|
+
def prev_out tx, idx = nil, script = nil, prev_value = nil
|
345
|
+
if tx.is_a?(Bitcoin::P::Tx)
|
346
|
+
@prev_tx = tx
|
347
|
+
@prev_out_hash = tx.binary_hash
|
348
|
+
@prev_out_script = tx.out[idx].pk_script if idx
|
349
|
+
else
|
350
|
+
@prev_out_hash = tx.htb.reverse
|
351
|
+
end
|
352
|
+
@prev_out_script = script if script
|
353
|
+
@prev_out_index = idx if idx
|
354
|
+
@prev_out_value = prev_value if prev_value
|
355
|
+
end
|
356
|
+
|
357
|
+
# Index of the output in the #prev_out transaction.
|
358
|
+
def prev_out_index i
|
359
|
+
@prev_out_index = i
|
360
|
+
@prev_out_script = @prev_tx.out[i].pk_script if @prev_tx
|
361
|
+
end
|
362
|
+
|
363
|
+
# Previous output's +pk_script+. Needed when only the tx hash is specified as #prev_out.
|
364
|
+
def prev_out_script script
|
365
|
+
@prev_out_script = script
|
366
|
+
end
|
367
|
+
|
368
|
+
# Previous output's +value+. Needed when only spend segwit utxo.
|
369
|
+
def prev_out_value value
|
370
|
+
@prev_out_value = value
|
371
|
+
end
|
372
|
+
|
373
|
+
def value
|
374
|
+
@prev_out_value
|
375
|
+
end
|
376
|
+
|
377
|
+
# Redeem script for P2SH output. To spend from a P2SH output, you need to provide
|
378
|
+
# the script with a hash matching the P2SH address.
|
379
|
+
def redeem_script script
|
380
|
+
@redeem_script = script
|
381
|
+
end
|
382
|
+
|
383
|
+
# Specify sequence. This is usually not needed.
|
384
|
+
def sequence s
|
385
|
+
@sequence = s
|
386
|
+
end
|
387
|
+
|
388
|
+
# Bitcoin::Key used to sign the signature_hash for the input.
|
389
|
+
# see Bitcoin::Script.signature_hash_for_input and Bitcoin::Key.sign.
|
390
|
+
def signature_key key
|
391
|
+
@key = key
|
392
|
+
end
|
393
|
+
|
394
|
+
# Specify that this is a coinbase input. Optionally set +data+.
|
395
|
+
# If this is set, no other options need to be given.
|
396
|
+
def coinbase data = nil
|
397
|
+
@coinbase_data = data || OpenSSL::Random.random_bytes(32)
|
398
|
+
@prev_out_hash = "\x00" * 32
|
399
|
+
@prev_out_index = 4294967295
|
400
|
+
end
|
401
|
+
|
402
|
+
# Create the txin according to specified values
|
403
|
+
def txin
|
404
|
+
@txin.prev_out = @prev_out_hash
|
405
|
+
@txin.prev_out_index = @prev_out_index
|
406
|
+
@txin.sequence = @sequence || "\xff\xff\xff\xff"
|
407
|
+
@txin
|
408
|
+
end
|
409
|
+
|
410
|
+
def has_multiple_keys?
|
411
|
+
@key.is_a?(Array)
|
412
|
+
end
|
413
|
+
|
414
|
+
def has_keys?
|
415
|
+
@key && (has_multiple_keys? ? @key.all?(&:priv) : @key.priv)
|
416
|
+
end
|
417
|
+
|
418
|
+
def is_witness_v0_keyhash?
|
419
|
+
@prev_out_script && Script.new(@prev_out_script).is_witness_v0_keyhash?
|
420
|
+
end
|
421
|
+
|
422
|
+
def sign(sig_hash)
|
423
|
+
if has_multiple_keys?
|
424
|
+
@key.map {|k| k.sign(sig_hash) }
|
425
|
+
else
|
426
|
+
@key.sign(sig_hash)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Create a Bitcoin::Script used by TxOutBuilder#script.
|
432
|
+
class ScriptBuilder
|
433
|
+
attr_reader :script, :redeem_script
|
434
|
+
|
435
|
+
def initialize
|
436
|
+
@type = :address
|
437
|
+
@script = nil
|
438
|
+
end
|
439
|
+
|
440
|
+
# Script type (:pubkey, :address/hash160, :multisig).
|
441
|
+
# Defaults to :address.
|
442
|
+
def type type
|
443
|
+
@type = type.to_sym
|
444
|
+
end
|
445
|
+
|
446
|
+
# Recipient(s) of the script.
|
447
|
+
# Depending on the #type, this should be an address, a hash160 pubkey,
|
448
|
+
# or an array of multisig pubkeys.
|
449
|
+
def recipient *data
|
450
|
+
@script, @redeem_script = *Script.send("to_#{@type}_script", *data)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
# Create a Bitcoin::Protocol::TxOut used by TxBuilder#output.
|
455
|
+
#
|
456
|
+
# t.output 12345, address
|
457
|
+
# t.output 12345, p2sh_address, :script_hash
|
458
|
+
#
|
459
|
+
# t.output {|o| o.value 12345; o.to address }
|
460
|
+
#
|
461
|
+
# t.output do |o|
|
462
|
+
# o.value 12345
|
463
|
+
# o.script {|s| s.recipient address }
|
464
|
+
# end
|
465
|
+
#
|
466
|
+
# t.output {|o| o.to "deadbeef", :op_return }
|
467
|
+
class TxOutBuilder
|
468
|
+
attr_reader :txout
|
469
|
+
|
470
|
+
def initialize
|
471
|
+
@txout = P::TxOut.new(0)
|
472
|
+
end
|
473
|
+
|
474
|
+
# Set output value (in base units / "satoshis")
|
475
|
+
def value value
|
476
|
+
@txout.value = value
|
477
|
+
end
|
478
|
+
|
479
|
+
# Set recipient address and script type (defaults to :address).
|
480
|
+
def to recipient, type = :address
|
481
|
+
@txout.pk_script, @txout.redeem_script = *Bitcoin::Script.send("to_#{type}_script", *recipient)
|
482
|
+
end
|
483
|
+
|
484
|
+
# Add a script to the output (see ScriptBuilder).
|
485
|
+
def script &block
|
486
|
+
c = ScriptBuilder.new
|
487
|
+
yield c
|
488
|
+
@txout.pk_script, @txout.redeem_script = c.script, c.redeem_script
|
489
|
+
end
|
490
|
+
|
491
|
+
end
|
492
|
+
|
493
|
+
end
|
494
|
+
end
|