bitcoinrb 0.3.1 → 0.7.0
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/.ruby-version +1 -1
- data/.travis.yml +6 -3
- data/README.md +17 -6
- data/bitcoinrb.gemspec +9 -8
- data/exe/bitcoinrbd +5 -0
- data/lib/bitcoin.rb +35 -19
- data/lib/bitcoin/bip85_entropy.rb +111 -0
- data/lib/bitcoin/block_filter.rb +14 -0
- data/lib/bitcoin/block_header.rb +2 -0
- data/lib/bitcoin/chain_params.rb +9 -8
- data/lib/bitcoin/chainparams/regtest.yml +1 -1
- data/lib/bitcoin/chainparams/signet.yml +39 -0
- data/lib/bitcoin/chainparams/testnet.yml +1 -1
- data/lib/bitcoin/constants.rb +45 -12
- data/lib/bitcoin/descriptor.rb +1 -1
- data/lib/bitcoin/errors.rb +19 -0
- data/lib/bitcoin/ext.rb +5 -0
- data/lib/bitcoin/ext/ecdsa.rb +31 -0
- data/lib/bitcoin/ext/json_parser.rb +46 -0
- data/lib/bitcoin/ext_key.rb +50 -19
- data/lib/bitcoin/key.rb +46 -29
- data/lib/bitcoin/key_path.rb +12 -5
- data/lib/bitcoin/message.rb +79 -0
- data/lib/bitcoin/message/addr_v2.rb +34 -0
- data/lib/bitcoin/message/base.rb +17 -0
- data/lib/bitcoin/message/cf_parser.rb +16 -0
- data/lib/bitcoin/message/cfcheckpt.rb +36 -0
- data/lib/bitcoin/message/cfheaders.rb +40 -0
- data/lib/bitcoin/message/cfilter.rb +35 -0
- data/lib/bitcoin/message/fee_filter.rb +1 -1
- data/lib/bitcoin/message/filter_load.rb +3 -3
- data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
- data/lib/bitcoin/message/get_cfheaders.rb +24 -0
- data/lib/bitcoin/message/get_cfilters.rb +25 -0
- data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
- data/lib/bitcoin/message/inventory.rb +1 -1
- data/lib/bitcoin/message/merkle_block.rb +1 -1
- data/lib/bitcoin/message/network_addr.rb +141 -18
- data/lib/bitcoin/message/ping.rb +1 -1
- data/lib/bitcoin/message/pong.rb +1 -1
- data/lib/bitcoin/message/send_addr_v2.rb +13 -0
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/message/version.rb +7 -0
- data/lib/bitcoin/mnemonic.rb +7 -7
- data/lib/bitcoin/network/peer.rb +9 -4
- data/lib/bitcoin/network/peer_discovery.rb +1 -1
- data/lib/bitcoin/node/cli.rb +14 -10
- data/lib/bitcoin/node/configuration.rb +3 -1
- data/lib/bitcoin/node/spv.rb +9 -1
- data/lib/bitcoin/opcodes.rb +14 -1
- data/lib/bitcoin/out_point.rb +7 -0
- data/lib/bitcoin/payment_code.rb +92 -0
- data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
- data/lib/bitcoin/psbt/input.rb +8 -17
- data/lib/bitcoin/psbt/output.rb +1 -1
- data/lib/bitcoin/psbt/tx.rb +11 -16
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
- data/lib/bitcoin/rpc/request_handler.rb +3 -3
- data/lib/bitcoin/script/script.rb +68 -28
- data/lib/bitcoin/script/script_error.rb +27 -1
- data/lib/bitcoin/script/script_interpreter.rb +164 -67
- data/lib/bitcoin/script/tx_checker.rb +64 -14
- data/lib/bitcoin/secp256k1.rb +1 -0
- data/lib/bitcoin/secp256k1/native.rb +138 -25
- data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
- data/lib/bitcoin/secp256k1/ruby.rb +82 -54
- data/lib/bitcoin/sighash_generator.rb +156 -0
- data/lib/bitcoin/store.rb +2 -1
- data/lib/bitcoin/store/chain_entry.rb +1 -0
- data/lib/bitcoin/store/db/level_db.rb +2 -2
- data/lib/bitcoin/store/utxo_db.rb +226 -0
- data/lib/bitcoin/tx.rb +17 -88
- data/lib/bitcoin/tx_in.rb +4 -5
- data/lib/bitcoin/tx_out.rb +2 -3
- data/lib/bitcoin/util.rb +34 -6
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet.rb +1 -0
- data/lib/bitcoin/wallet/account.rb +2 -1
- data/lib/bitcoin/wallet/base.rb +3 -3
- data/lib/bitcoin/wallet/db.rb +1 -1
- data/lib/bitcoin/wallet/master_key.rb +1 -0
- data/lib/bitcoin/wallet/utxo.rb +37 -0
- metadata +66 -32
data/lib/bitcoin/store.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'leveldb'
|
1
|
+
require 'leveldb-native'
|
2
2
|
|
3
3
|
module Bitcoin
|
4
4
|
module Store
|
@@ -6,6 +6,7 @@ module Bitcoin
|
|
6
6
|
autoload :DB, 'bitcoin/store/db'
|
7
7
|
autoload :SPVChain, 'bitcoin/store/spv_chain'
|
8
8
|
autoload :ChainEntry, 'bitcoin/store/chain_entry'
|
9
|
+
autoload :UtxoDB, 'bitcoin/store/utxo_db'
|
9
10
|
|
10
11
|
end
|
11
12
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'leveldb'
|
1
|
+
require 'leveldb-native'
|
2
2
|
|
3
3
|
module Bitcoin
|
4
4
|
module Store
|
@@ -12,7 +12,7 @@ module Bitcoin
|
|
12
12
|
def initialize(path = "#{Bitcoin.base_dir}/db/spv")
|
13
13
|
# @logger = Bitcoin::Logger.create(:debug)
|
14
14
|
FileUtils.mkdir_p(path)
|
15
|
-
@db = ::
|
15
|
+
@db = ::LevelDBNative::DB.new(path)
|
16
16
|
# logger.debug 'Opened LevelDB successfully.'
|
17
17
|
end
|
18
18
|
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'leveldb-native'
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
module Store
|
5
|
+
class UtxoDB
|
6
|
+
|
7
|
+
KEY_PREFIX = {
|
8
|
+
out_point: 'o', # key: out_point(tx_hash and index), value: Utxo
|
9
|
+
script: 's', # key: script_pubkey and out_point(tx_hash and index), value: Utxo
|
10
|
+
height: 'h', # key: block_height and out_point, value: Utxo
|
11
|
+
tx_hash: 't', # key: tx_hash of transaction, value: [block_height, tx_index]
|
12
|
+
block: 'b', # key: block_height and tx_index, value: tx_hash
|
13
|
+
tx_payload: 'p', # key: tx_hash, value: Tx
|
14
|
+
}
|
15
|
+
|
16
|
+
attr_reader :level_db, :logger
|
17
|
+
|
18
|
+
def initialize(path = "#{Bitcoin.base_dir}/db/utxo")
|
19
|
+
FileUtils.mkdir_p(path)
|
20
|
+
@level_db = ::LevelDBNative::DB.new(path)
|
21
|
+
@logger = Bitcoin::Logger.create(:debug)
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
level_db.close
|
26
|
+
end
|
27
|
+
|
28
|
+
# Save payload of a transaction into db
|
29
|
+
#
|
30
|
+
# @param [String] tx_hash
|
31
|
+
# @param [String] tx_payload
|
32
|
+
def save_tx(tx_hash, tx_payload)
|
33
|
+
logger.info("UtxoDB#save_tx:#{[tx_hash, tx_payload]}")
|
34
|
+
level_db.batch do
|
35
|
+
# tx_hash -> [block_height, tx_index]
|
36
|
+
key = KEY_PREFIX[:tx_payload] + tx_hash
|
37
|
+
level_db.put(key, tx_payload)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Save tx position (block height and index in the block) into db
|
42
|
+
# When node receives `header` message, node should call save_tx_position to store block height and its index.
|
43
|
+
#
|
44
|
+
# @param [String] tx_hash
|
45
|
+
# @param [Integer] block_height
|
46
|
+
# @param [Integer] tx_index
|
47
|
+
def save_tx_position(tx_hash, block_height, tx_index)
|
48
|
+
logger.info("UtxoDB#save_tx_position:#{[tx_hash, block_height, tx_index]}")
|
49
|
+
level_db.batch do
|
50
|
+
# tx_hash -> [block_height, tx_index]
|
51
|
+
key = KEY_PREFIX[:tx_hash] + tx_hash
|
52
|
+
level_db.put(key, [block_height, tx_index].pack('N2').bth)
|
53
|
+
|
54
|
+
# block_hash and tx_index -> tx_hash
|
55
|
+
key = KEY_PREFIX[:block] + [block_height, tx_index].pack('N2').bth
|
56
|
+
level_db.put(key, tx_hash)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Save utxo into db
|
61
|
+
#
|
62
|
+
# @param [Bitcoin::OutPoint] out_point
|
63
|
+
# @param [Double] value
|
64
|
+
# @param [Bitcoin::Script] script_pubkey
|
65
|
+
# @param [Integer] block_height
|
66
|
+
def save_utxo(out_point, value, script_pubkey, block_height=nil)
|
67
|
+
logger.info("UtxoDB#save_utxo:#{[out_point, value, script_pubkey, block_height]}")
|
68
|
+
level_db.batch do
|
69
|
+
utxo = Bitcoin::Wallet::Utxo.new(out_point.tx_hash, out_point.index, value, script_pubkey, block_height)
|
70
|
+
payload = utxo.to_payload
|
71
|
+
|
72
|
+
# out_point
|
73
|
+
key = KEY_PREFIX[:out_point] + out_point.to_hex
|
74
|
+
return if level_db.contains?(key)
|
75
|
+
level_db.put(key, payload)
|
76
|
+
|
77
|
+
# script_pubkey
|
78
|
+
if script_pubkey
|
79
|
+
key = KEY_PREFIX[:script] + script_pubkey.to_hex + out_point.to_hex
|
80
|
+
level_db.put(key, payload)
|
81
|
+
end
|
82
|
+
|
83
|
+
# height
|
84
|
+
unless block_height.nil?
|
85
|
+
key = KEY_PREFIX[:height] + [block_height].pack('N').bth + out_point.to_hex
|
86
|
+
level_db.put(key, payload)
|
87
|
+
end
|
88
|
+
|
89
|
+
utxo
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get transaction stored via save_tx and save_tx_position
|
94
|
+
#
|
95
|
+
# @param [string] tx_hash
|
96
|
+
# @return [block_height, tx_index, tx_payload]
|
97
|
+
def get_tx(tx_hash)
|
98
|
+
key = KEY_PREFIX[:tx_hash] + tx_hash
|
99
|
+
return [] unless level_db.contains?(key)
|
100
|
+
block_height, tx_index = level_db.get(key).htb.unpack('N2')
|
101
|
+
key = KEY_PREFIX[:tx_payload] + tx_hash
|
102
|
+
tx_payload = level_db.get(key)
|
103
|
+
[block_height, tx_index, tx_payload]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Delete utxo from db
|
107
|
+
#
|
108
|
+
# @param [Bitcoin::Outpoint] out_point
|
109
|
+
# @return [Bitcoin::Wallet::Utxo]
|
110
|
+
def delete_utxo(out_point)
|
111
|
+
level_db.batch do
|
112
|
+
# [:out_point]
|
113
|
+
key = KEY_PREFIX[:out_point] + out_point.to_hex
|
114
|
+
return unless level_db.contains?(key)
|
115
|
+
utxo = Bitcoin::Wallet::Utxo.parse_from_payload(level_db.get(key))
|
116
|
+
level_db.delete(key)
|
117
|
+
|
118
|
+
# [:script]
|
119
|
+
if utxo.script_pubkey
|
120
|
+
key = KEY_PREFIX[:script] + utxo.script_pubkey.to_hex + out_point.to_hex
|
121
|
+
level_db.delete(key)
|
122
|
+
end
|
123
|
+
|
124
|
+
if utxo.block_height
|
125
|
+
# [:height]
|
126
|
+
key = KEY_PREFIX[:height] + [utxo.block_height].pack('N').bth + out_point.to_hex
|
127
|
+
level_db.delete(key)
|
128
|
+
|
129
|
+
# [:block]
|
130
|
+
key = KEY_PREFIX[:block] + [utxo.block_height, utxo.index].pack('N2').bth
|
131
|
+
level_db.delete(key)
|
132
|
+
end
|
133
|
+
|
134
|
+
# handles both [:tx_hash] and [:tx_payload]
|
135
|
+
if utxo.tx_hash
|
136
|
+
key = KEY_PREFIX[:tx_hash] + utxo.tx_hash
|
137
|
+
level_db.delete(key)
|
138
|
+
|
139
|
+
key = KEY_PREFIX[:tx_payload] + utxo.tx_hash
|
140
|
+
level_db.delete(key)
|
141
|
+
end
|
142
|
+
|
143
|
+
utxo
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Get utxo of the specified out point
|
148
|
+
#
|
149
|
+
# @param [Bitcoin::Outpoint] out_point
|
150
|
+
# @return [Bitcoin::Wallet::Utxo]
|
151
|
+
def get_utxo(out_point)
|
152
|
+
level_db.batch do
|
153
|
+
key = KEY_PREFIX[:out_point] + out_point.to_hex
|
154
|
+
return unless level_db.contains?(key)
|
155
|
+
return Bitcoin::Wallet::Utxo.parse_from_payload(level_db.get(key))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# return [Bitcoin::Wallet::Utxo ...]
|
160
|
+
def list_unspent(current_block_height: 9999999, min: 0, max: 9999999, addresses: nil)
|
161
|
+
if addresses
|
162
|
+
list_unspent_by_addresses(current_block_height, min: min, max: max, addresses: addresses)
|
163
|
+
else
|
164
|
+
list_unspent_by_block_height(current_block_height, min: min, max: max)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# @param [Bitcoin::Wallet::Account]
|
169
|
+
# return [Bitcoin::Wallet::Utxo ...]
|
170
|
+
def list_unspent_in_account(account, current_block_height: 9999999, min: 0, max: 9999999)
|
171
|
+
return [] unless account
|
172
|
+
|
173
|
+
script_pubkeys = case account.purpose
|
174
|
+
when Bitcoin::Wallet::Account::PURPOSE_TYPE[:legacy]
|
175
|
+
account.watch_targets.map { |t| Bitcoin::Script.to_p2pkh(t).to_hex }
|
176
|
+
when Bitcoin::Wallet::Account::PURPOSE_TYPE[:nested_witness]
|
177
|
+
account.watch_targets.map { |t| Bitcoin::Script.to_p2wpkh(t).to_p2sh.to_hex }
|
178
|
+
when Bitcoin::Wallet::Account::PURPOSE_TYPE[:native_segwit]
|
179
|
+
account.watch_targets.map { |t| Bitcoin::Script.to_p2wpkh(t).to_hex }
|
180
|
+
end
|
181
|
+
list_unspent_by_script_pubkeys(current_block_height, min: min, max: max, script_pubkeys: script_pubkeys)
|
182
|
+
end
|
183
|
+
|
184
|
+
# @param [Bitcoin::Wallet::Account]
|
185
|
+
# return [Bitcoin::Wallet::Utxo ...]
|
186
|
+
def get_balance(account, current_block_height: 9999999, min: 0, max: 9999999)
|
187
|
+
list_unspent_in_account(account, current_block_height: current_block_height, min: min, max: max).sum { |u| u.value }
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def utxos_between(from, to)
|
193
|
+
level_db.each(from: from, to: to).map { |k, v| Bitcoin::Wallet::Utxo.parse_from_payload(v) }
|
194
|
+
end
|
195
|
+
|
196
|
+
class ::Array
|
197
|
+
def with_height(min, max)
|
198
|
+
select { |u| u.block_height >= min && u.block_height <= max }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def list_unspent_by_block_height(current_block_height, min: 0, max: 9999999)
|
203
|
+
max_height = [current_block_height - min, 0].max
|
204
|
+
min_height = [current_block_height - max, 0].max
|
205
|
+
from = KEY_PREFIX[:height] + [min_height].pack('N').bth + '000000000000000000000000000000000000000000000000000000000000000000000000'
|
206
|
+
to = KEY_PREFIX[:height] + [max_height].pack('N').bth + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
207
|
+
utxos_between(from, to)
|
208
|
+
end
|
209
|
+
|
210
|
+
def list_unspent_by_addresses(current_block_height, min: 0, max: 9999999, addresses: [])
|
211
|
+
script_pubkeys = addresses.map { |a| Bitcoin::Script.parse_from_addr(a).to_hex }
|
212
|
+
list_unspent_by_script_pubkeys(current_block_height, min: min, max: max, script_pubkeys: script_pubkeys)
|
213
|
+
end
|
214
|
+
|
215
|
+
def list_unspent_by_script_pubkeys(current_block_height, min: 0, max: 9999999, script_pubkeys: [])
|
216
|
+
max_height = current_block_height - min
|
217
|
+
min_height = current_block_height - max
|
218
|
+
script_pubkeys.map do |key|
|
219
|
+
from = KEY_PREFIX[:script] + key + '000000000000000000000000000000000000000000000000000000000000000000000000'
|
220
|
+
to = KEY_PREFIX[:script] + key + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
221
|
+
utxos_between(from, to).with_height(min_height, max_height)
|
222
|
+
end.flatten
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/bitcoin/tx.rb
CHANGED
@@ -6,6 +6,8 @@ module Bitcoin
|
|
6
6
|
# Transaction class
|
7
7
|
class Tx
|
8
8
|
|
9
|
+
include Bitcoin::HexConverter
|
10
|
+
|
9
11
|
MAX_STANDARD_VERSION = 2
|
10
12
|
|
11
13
|
# The maximum weight for transactions we're willing to relay/mine
|
@@ -34,13 +36,13 @@ module Bitcoin
|
|
34
36
|
def self.parse_from_payload(payload, non_witness: false)
|
35
37
|
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
36
38
|
tx = new
|
37
|
-
tx.version = buf.read(4).
|
39
|
+
tx.version = buf.read(4).unpack1('V')
|
38
40
|
|
39
41
|
in_count = Bitcoin.unpack_var_int_from_io(buf)
|
40
42
|
witness = false
|
41
43
|
if in_count.zero? && !non_witness
|
42
44
|
tx.marker = 0
|
43
|
-
tx.flag = buf.read(1).
|
45
|
+
tx.flag = buf.read(1).unpack1('c')
|
44
46
|
if tx.flag.zero?
|
45
47
|
buf.pos -= 1
|
46
48
|
else
|
@@ -64,13 +66,13 @@ module Bitcoin
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
67
|
-
tx.lock_time = buf.read(4).
|
69
|
+
tx.lock_time = buf.read(4).unpack1('V')
|
68
70
|
|
69
71
|
tx
|
70
72
|
end
|
71
73
|
|
72
74
|
def hash
|
73
|
-
|
75
|
+
to_hex.to_i(16)
|
74
76
|
end
|
75
77
|
|
76
78
|
def tx_hash
|
@@ -104,12 +106,6 @@ module Bitcoin
|
|
104
106
|
witness? ? serialize_witness_format : serialize_old_format
|
105
107
|
end
|
106
108
|
|
107
|
-
# convert tx to hex format.
|
108
|
-
# @return [String] tx with hex format.
|
109
|
-
def to_hex
|
110
|
-
to_payload.bth
|
111
|
-
end
|
112
|
-
|
113
109
|
def coinbase_tx?
|
114
110
|
inputs.length == 1 && inputs.first.coinbase?
|
115
111
|
end
|
@@ -192,22 +188,22 @@ module Bitcoin
|
|
192
188
|
# @param [Integer] input_index input index.
|
193
189
|
# @param [Integer] hash_type signature hash type
|
194
190
|
# @param [Bitcoin::Script] output_script script pubkey or script code. if script pubkey is P2WSH, set witness script to this.
|
191
|
+
# @param [Hash] opts Data required for each sig version (amount and skip_separator_index params can also be set to this parameter)
|
195
192
|
# @param [Integer] amount bitcoin amount locked in input. required for witness input only.
|
196
193
|
# @param [Integer] skip_separator_index If output_script is P2WSH and output_script contains any OP_CODESEPARATOR,
|
197
194
|
# the script code needs is the witnessScript but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed.
|
198
|
-
def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all],
|
195
|
+
def sighash_for_input(input_index, output_script = nil, opts: {}, hash_type: SIGHASH_TYPE[:all],
|
199
196
|
sig_version: :base, amount: nil, skip_separator_index: 0)
|
200
197
|
raise ArgumentError, 'input_index must be specified.' unless input_index
|
201
198
|
raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
|
202
|
-
raise ArgumentError, 'script_pubkey must be specified.'
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
end
|
199
|
+
raise ArgumentError, 'script_pubkey must be specified.' if [:base, :witness_v0].include?(sig_version) && output_script.nil?
|
200
|
+
|
201
|
+
opts[:amount] = amount if amount
|
202
|
+
opts[:skip_separator_index] = skip_separator_index
|
203
|
+
opts[:sig_version] = sig_version
|
204
|
+
opts[:script_code] = output_script
|
205
|
+
sig_hash_gen = SigHashGenerator.load(sig_version)
|
206
|
+
sig_hash_gen.generate(self, input_index, hash_type, opts)
|
211
207
|
end
|
212
208
|
|
213
209
|
# verify input signature.
|
@@ -225,7 +221,7 @@ module Bitcoin
|
|
225
221
|
script_pubkey = redeem_script if redeem_script.p2wpkh?
|
226
222
|
end
|
227
223
|
|
228
|
-
if has_witness
|
224
|
+
if has_witness
|
229
225
|
verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
|
230
226
|
else
|
231
227
|
verify_input_sig_for_legacy(input_index, script_pubkey, flags)
|
@@ -249,73 +245,6 @@ module Bitcoin
|
|
249
245
|
|
250
246
|
private
|
251
247
|
|
252
|
-
# generate sighash with legacy format
|
253
|
-
def sighash_for_legacy(index, script_code, hash_type)
|
254
|
-
ins = inputs.map.with_index do |i, idx|
|
255
|
-
if idx == index
|
256
|
-
i.to_payload(script_code.delete_opcode(Bitcoin::Opcodes::OP_CODESEPARATOR))
|
257
|
-
else
|
258
|
-
case hash_type & 0x1f
|
259
|
-
when SIGHASH_TYPE[:none], SIGHASH_TYPE[:single]
|
260
|
-
i.to_payload(Bitcoin::Script.new, 0)
|
261
|
-
else
|
262
|
-
i.to_payload(Bitcoin::Script.new)
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
outs = outputs.map(&:to_payload)
|
268
|
-
out_size = Bitcoin.pack_var_int(outputs.size)
|
269
|
-
|
270
|
-
case hash_type & 0x1f
|
271
|
-
when SIGHASH_TYPE[:none]
|
272
|
-
outs = ''
|
273
|
-
out_size = Bitcoin.pack_var_int(0)
|
274
|
-
when SIGHASH_TYPE[:single]
|
275
|
-
return "\x01".ljust(32, "\x00") if index >= outputs.size
|
276
|
-
outs = outputs[0...(index + 1)].map.with_index { |o, idx| (idx == index) ? o.to_payload : o.to_empty_payload }.join
|
277
|
-
out_size = Bitcoin.pack_var_int(index + 1)
|
278
|
-
end
|
279
|
-
|
280
|
-
if hash_type & SIGHASH_TYPE[:anyonecanpay] != 0
|
281
|
-
ins = [ins[index]]
|
282
|
-
end
|
283
|
-
|
284
|
-
buf = [[version].pack('V'), Bitcoin.pack_var_int(ins.size),
|
285
|
-
ins, out_size, outs, [lock_time, hash_type].pack('VV')].join
|
286
|
-
|
287
|
-
Bitcoin.double_sha256(buf)
|
288
|
-
end
|
289
|
-
|
290
|
-
# generate sighash with BIP-143 format
|
291
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
|
292
|
-
def sighash_for_witness(index, script_pubkey_or_script_code, hash_type, amount, skip_separator_index)
|
293
|
-
hash_prevouts = Bitcoin.double_sha256(inputs.map{|i|i.out_point.to_payload}.join)
|
294
|
-
hash_sequence = Bitcoin.double_sha256(inputs.map{|i|[i.sequence].pack('V')}.join)
|
295
|
-
outpoint = inputs[index].out_point.to_payload
|
296
|
-
amount = [amount].pack('Q')
|
297
|
-
nsequence = [inputs[index].sequence].pack('V')
|
298
|
-
hash_outputs = Bitcoin.double_sha256(outputs.map{|o|o.to_payload}.join)
|
299
|
-
|
300
|
-
script_code = script_pubkey_or_script_code.to_script_code(skip_separator_index)
|
301
|
-
|
302
|
-
case (hash_type & 0x1f)
|
303
|
-
when SIGHASH_TYPE[:single]
|
304
|
-
hash_outputs = index >= outputs.size ? "\x00".ljust(32, "\x00") : Bitcoin.double_sha256(outputs[index].to_payload)
|
305
|
-
hash_sequence = "\x00".ljust(32, "\x00")
|
306
|
-
when SIGHASH_TYPE[:none]
|
307
|
-
hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
|
308
|
-
end
|
309
|
-
|
310
|
-
if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
|
311
|
-
hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
|
312
|
-
end
|
313
|
-
hash_type |= (Bitcoin.chain_params.fork_id << 8) if Bitcoin.chain_params.fork_chain?
|
314
|
-
buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint,
|
315
|
-
script_code ,amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
|
316
|
-
Bitcoin.double_sha256(buf)
|
317
|
-
end
|
318
|
-
|
319
248
|
# verify input signature for legacy tx.
|
320
249
|
def verify_input_sig_for_legacy(input_index, script_pubkey, flags)
|
321
250
|
script_sig = inputs[input_index].script_sig
|
data/lib/bitcoin/tx_in.rb
CHANGED
@@ -37,13 +37,12 @@ module Bitcoin
|
|
37
37
|
hash, index = buf.read(36).unpack('a32V')
|
38
38
|
i.out_point = OutPoint.new(hash.bth, index)
|
39
39
|
sig_length = Bitcoin.unpack_var_int_from_io(buf)
|
40
|
-
|
41
|
-
|
42
|
-
i.script_sig.chunks[0] = sig
|
40
|
+
if sig_length == 0
|
41
|
+
i.script_sig = Bitcoin::Script.new
|
43
42
|
else
|
44
|
-
i.script_sig = Script.parse_from_payload(
|
43
|
+
i.script_sig = Script.parse_from_payload(buf.read(sig_length))
|
45
44
|
end
|
46
|
-
i.sequence = buf.read(4).
|
45
|
+
i.sequence = buf.read(4).unpack1('V')
|
47
46
|
i
|
48
47
|
end
|
49
48
|
|