bitcoinrb 1.12.0 → 1.13.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/.github/workflows/ruby.yml +1 -1
- data/bitcoinrb.gemspec +1 -6
- data/lib/bitcoin/bip321_uri.rb +3 -6
- data/lib/bitcoin/bip324/fs_chacha20.rb +4 -6
- data/lib/bitcoin/bip324/fs_chacha_poly1305.rb +20 -17
- data/lib/bitcoin/bip324.rb +3 -1
- data/lib/bitcoin/descriptor/checksum.rb +6 -2
- data/lib/bitcoin/descriptor/expression.rb +2 -2
- data/lib/bitcoin/descriptor/sp.rb +219 -0
- data/lib/bitcoin/descriptor.rb +20 -0
- data/lib/bitcoin/ext_key.rb +11 -7
- data/lib/bitcoin/key.rb +1 -1
- data/lib/bitcoin/message/cf_parser.rb +2 -2
- data/lib/bitcoin/message/fee_filter.rb +2 -2
- data/lib/bitcoin/message/header_and_short_ids.rb +4 -4
- data/lib/bitcoin/message/inventory.rb +1 -1
- data/lib/bitcoin/message/network_addr.rb +7 -3
- data/lib/bitcoin/message/ping.rb +5 -3
- data/lib/bitcoin/message/pong.rb +2 -2
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/message/version.rb +4 -4
- data/lib/bitcoin/message.rb +12 -2
- data/lib/bitcoin/message_sign.rb +98 -38
- data/lib/bitcoin/mnemonic.rb +9 -4
- data/lib/bitcoin/psbt/input.rb +51 -33
- data/lib/bitcoin/psbt/key_origin_info.rb +2 -2
- data/lib/bitcoin/psbt/output.rb +9 -5
- data/lib/bitcoin/psbt/tx.rb +16 -10
- data/lib/bitcoin/psbt.rb +1 -1
- data/lib/bitcoin/rpc.rb +0 -2
- data/lib/bitcoin/script/script.rb +14 -4
- data/lib/bitcoin/secp256k1/rfc6979.rb +6 -3
- data/lib/bitcoin/sighash_generator.rb +2 -2
- data/lib/bitcoin/silent_payment/output.rb +46 -0
- data/lib/bitcoin/silent_payment.rb +113 -12
- data/lib/bitcoin/taproot/control_block.rb +1 -0
- data/lib/bitcoin/tx.rb +8 -6
- data/lib/bitcoin/tx_out.rb +2 -2
- data/lib/bitcoin/util.rb +5 -5
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +2 -2
- data/lib/bitcoin/wallet/db.rb +3 -3
- data/lib/bitcoin/wallet/master_key.rb +2 -2
- data/lib/bitcoin/wallet/utxo.rb +2 -2
- data/lib/bitcoin.rb +1 -6
- metadata +18 -108
- data/exe/bitcoinrb-cli +0 -5
- data/exe/bitcoinrbd +0 -46
- data/lib/bitcoin/network/connection.rb +0 -73
- data/lib/bitcoin/network/message_handler.rb +0 -243
- data/lib/bitcoin/network/peer.rb +0 -228
- data/lib/bitcoin/network/peer_discovery.rb +0 -42
- data/lib/bitcoin/network/pool.rb +0 -135
- data/lib/bitcoin/network.rb +0 -13
- data/lib/bitcoin/node/cli.rb +0 -122
- data/lib/bitcoin/node/configuration.rb +0 -40
- data/lib/bitcoin/node/spv.rb +0 -87
- data/lib/bitcoin/node.rb +0 -7
- data/lib/bitcoin/rpc/http_server.rb +0 -84
- data/lib/bitcoin/rpc/request_handler.rb +0 -150
- data/lib/bitcoin/store/chain_entry.rb +0 -68
- data/lib/bitcoin/store/db/level_db.rb +0 -98
- data/lib/bitcoin/store/db.rb +0 -9
- data/lib/bitcoin/store/spv_chain.rb +0 -101
- data/lib/bitcoin/store/utxo_db.rb +0 -226
- data/lib/bitcoin/store.rb +0 -12
data/lib/bitcoin/psbt.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Bitcoin
|
|
|
55
55
|
module_function
|
|
56
56
|
|
|
57
57
|
def self.serialize_to_vector(key_type, key: nil, value: nil)
|
|
58
|
-
key_len = key_type.
|
|
58
|
+
key_len = Bitcoin.pack_var_int(key_type).bytesize
|
|
59
59
|
key_len += key.bytesize if key
|
|
60
60
|
s = Bitcoin.pack_var_int(key_len) << Bitcoin.pack_var_int(key_type)
|
|
61
61
|
s << key if key
|
data/lib/bitcoin/rpc.rb
CHANGED
|
@@ -100,12 +100,22 @@ module Bitcoin
|
|
|
100
100
|
if opcode
|
|
101
101
|
script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
|
|
102
102
|
else
|
|
103
|
-
script << (v
|
|
103
|
+
script << (number_token?(v) ? v.to_i : v)
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
script
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Whether +token+ is a script number rather than pushed data with hex format.
|
|
110
|
+
# A digit only token is ambiguous since #to_s emits pushed data as hex. #to_s never emits
|
|
111
|
+
# a number with more than 10 digits, so a longer even-length token must be pushed data.
|
|
112
|
+
# @param [String] token a token of the script string.
|
|
113
|
+
# @return [Boolean] whether +token+ should be interpreted as a script number.
|
|
114
|
+
def self.number_token?(token)
|
|
115
|
+
return false unless token =~ /^[0-9]+$/
|
|
116
|
+
token.length < 12 || token.length.odd?
|
|
117
|
+
end
|
|
118
|
+
|
|
109
119
|
# generate script from addr.
|
|
110
120
|
# @param [String] addr address.
|
|
111
121
|
# @return [Bitcoin::Script] parsed script.
|
|
@@ -491,11 +501,11 @@ module Bitcoin
|
|
|
491
501
|
size = data.bytesize
|
|
492
502
|
header = if size < OP_PUSHDATA1
|
|
493
503
|
[size].pack('C')
|
|
494
|
-
elsif size
|
|
504
|
+
elsif size <= 0xff
|
|
495
505
|
[OP_PUSHDATA1, size].pack('CC')
|
|
496
|
-
elsif size
|
|
506
|
+
elsif size <= 0xffff
|
|
497
507
|
[OP_PUSHDATA2, size].pack('Cv')
|
|
498
|
-
elsif size
|
|
508
|
+
elsif size <= 0xffffffff
|
|
499
509
|
[OP_PUSHDATA4, size].pack('CV')
|
|
500
510
|
else
|
|
501
511
|
raise ArgumentError, 'data size is too big.'
|
|
@@ -26,10 +26,13 @@ module Bitcoin
|
|
|
26
26
|
# 3.2.g
|
|
27
27
|
v = Bitcoin.hmac_sha256(k, v)
|
|
28
28
|
# 3.2.h
|
|
29
|
-
t = ''
|
|
30
29
|
10000.times do
|
|
31
|
-
|
|
32
|
-
t =
|
|
30
|
+
# T is reset for each candidate, otherwise a retry can never produce a value below the order.
|
|
31
|
+
t = ''
|
|
32
|
+
while t.bytesize < 32
|
|
33
|
+
v = Bitcoin.hmac_sha256(k, v)
|
|
34
|
+
t = (t + v)
|
|
35
|
+
end
|
|
33
36
|
t_num = t.bth.to_i(16)
|
|
34
37
|
return t_num if 1 <= t_num && t_num < Bitcoin::Secp256k1::GROUP.order
|
|
35
38
|
k = Bitcoin.hmac_sha256(k, v + '00'.htb)
|
|
@@ -68,7 +68,7 @@ module Bitcoin
|
|
|
68
68
|
hash_prevouts = Bitcoin.double_sha256(tx.inputs.map{|i|i.out_point.to_payload}.join)
|
|
69
69
|
hash_sequence = Bitcoin.double_sha256(tx.inputs.map{|i|[i.sequence].pack('V')}.join)
|
|
70
70
|
outpoint = tx.inputs[input_index].out_point.to_payload
|
|
71
|
-
amount = [amount].pack('Q')
|
|
71
|
+
amount = [amount].pack('Q<')
|
|
72
72
|
nsequence = [tx.inputs[input_index].sequence].pack('V')
|
|
73
73
|
hash_outputs = Bitcoin.double_sha256(tx.outputs.map{|o|o.to_payload}.join)
|
|
74
74
|
if output_script.p2wsh?
|
|
@@ -120,7 +120,7 @@ module Bitcoin
|
|
|
120
120
|
buf << [hash_type, tx.version, tx.lock_time].pack('CVV')
|
|
121
121
|
unless input_type == SIGHASH_TYPE[:anyonecanpay]
|
|
122
122
|
buf << Bitcoin.sha256(tx.in.map{|i|i.out_point.to_payload}.join) # sha_prevouts
|
|
123
|
-
buf << Bitcoin.sha256(opts[:prevouts].map(&:value).pack('Q
|
|
123
|
+
buf << Bitcoin.sha256(opts[:prevouts].map(&:value).pack('Q<*'))# sha_amounts
|
|
124
124
|
buf << Bitcoin.sha256(opts[:prevouts].map{|o|o.script_pubkey.to_payload(true)}.join) # sha_scriptpubkeys
|
|
125
125
|
buf << Bitcoin.sha256(tx.in.map(&:sequence).pack('V*')) # sha_sequences
|
|
126
126
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Bitcoin
|
|
2
|
+
module SilentPayment
|
|
3
|
+
# Represents a detected silent payment output.
|
|
4
|
+
class Output
|
|
5
|
+
# @return [Bitcoin::TxOut] The detected transaction output.
|
|
6
|
+
attr_reader :tx_out
|
|
7
|
+
|
|
8
|
+
# @return [String] The tweak value (t_k) used to derive the output key (32 bytes binary).
|
|
9
|
+
attr_reader :tweak
|
|
10
|
+
|
|
11
|
+
# @return [Integer, nil] The label used for this output, or nil if no label was used.
|
|
12
|
+
attr_reader :label
|
|
13
|
+
|
|
14
|
+
# @param [Bitcoin::TxOut] tx_out The detected transaction output.
|
|
15
|
+
# @param [String] tweak The tweak value (t_k) as 32 bytes binary.
|
|
16
|
+
# @param [Integer, nil] label The label integer, or nil if no label was used.
|
|
17
|
+
# @raise [ArgumentError] If any parameter has an invalid type.
|
|
18
|
+
def initialize(tx_out, tweak, label = nil)
|
|
19
|
+
raise ArgumentError, "tx_out must be a Bitcoin::TxOut." unless tx_out.is_a?(Bitcoin::TxOut)
|
|
20
|
+
raise ArgumentError, "tweak must be a 32-byte String." unless tweak.is_a?(String) && tweak.bytesize == 32
|
|
21
|
+
raise ArgumentError, "label must be an Integer or nil." unless label.nil? || label.is_a?(Integer)
|
|
22
|
+
@tx_out = tx_out
|
|
23
|
+
@tweak = tweak
|
|
24
|
+
@label = label
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the x-only public key of the output.
|
|
28
|
+
# @return [String] The x-only public key as hex string.
|
|
29
|
+
def pubkey
|
|
30
|
+
tx_out.script_pubkey.witness_data[1].bth
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns the tweak as hex string.
|
|
34
|
+
# @return [String] The tweak value as hex string.
|
|
35
|
+
def tweak_hex
|
|
36
|
+
tweak.bth
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns whether this output was detected using a label.
|
|
40
|
+
# @return [Boolean]
|
|
41
|
+
def labeled?
|
|
42
|
+
!label.nil?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
4
|
+
# BIP-352 silent payment module.
|
|
5
|
+
# @see https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki
|
|
2
6
|
module SilentPayment
|
|
7
|
+
autoload :Output, 'bitcoin/silent_payment/output'
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
#
|
|
9
|
+
# Maximum number of silent payment addresses that can share the same scan public key
|
|
10
|
+
# within a single transaction. This is the maximum number of P2TR outputs that can fit
|
|
11
|
+
# within a 100KB transaction under current standardness rules.
|
|
12
|
+
K_MAX = 2323
|
|
13
|
+
|
|
14
|
+
# Derive payment point.
|
|
15
|
+
#
|
|
16
|
+
# @param [Array<Bitcoin::Script>] prevouts An array of previous output script.
|
|
17
|
+
# @param [Array<Bitcoin::Key>] private_keys An array of Bitcoin::Key objects corresponding to each public key in prevouts.
|
|
18
|
+
# @param [Array<Bech32::SilentPaymentAddr>] recipients
|
|
19
|
+
# @return [Array<ECDSA::Point>] An array of derived points.
|
|
10
20
|
# @raise [ArgumentError]
|
|
11
21
|
def derive_payment_points(prevouts, private_keys, recipients)
|
|
12
22
|
raise ArgumentError, "prevouts must be Array." unless prevouts.is_a? Array
|
|
@@ -14,19 +24,19 @@ module Bitcoin
|
|
|
14
24
|
raise ArgumentError, "prevouts and private_keys must be the same length." unless prevouts.length == private_keys.length
|
|
15
25
|
raise ArgumentError, "recipients must be Array." unless recipients.is_a? Array
|
|
16
26
|
|
|
17
|
-
outpoint_l = inputs.map{|i|i.out_point.to_hex}.sort.first
|
|
18
|
-
|
|
19
27
|
input_pub_keys = []
|
|
20
28
|
field = ECDSA::PrimeField.new(Bitcoin::Secp256k1::GROUP.order)
|
|
21
29
|
sum_priv_keys = 0
|
|
22
30
|
prevouts.each_with_index do |prevout, index|
|
|
23
|
-
|
|
31
|
+
key = private_keys[index]
|
|
32
|
+
raise ArgumentError, "private_keys element must be Bitcoin::Key." unless key.is_a? Bitcoin::Key
|
|
33
|
+
priv_key_int = key.priv_key.to_i(16)
|
|
24
34
|
public_key = extract_public_key(prevout, inputs[index])
|
|
25
35
|
next if public_key.nil?
|
|
26
|
-
private_key = if public_key.p2tr? &&
|
|
27
|
-
field.mod(-
|
|
36
|
+
private_key = if public_key.p2tr? && key.to_point.y.odd?
|
|
37
|
+
field.mod(-priv_key_int)
|
|
28
38
|
else
|
|
29
|
-
|
|
39
|
+
priv_key_int
|
|
30
40
|
end
|
|
31
41
|
input_pub_keys << public_key
|
|
32
42
|
sum_priv_keys = field.mod(sum_priv_keys + private_key)
|
|
@@ -34,6 +44,8 @@ module Bitcoin
|
|
|
34
44
|
agg_pubkey = (Bitcoin::Secp256k1::GROUP.generator.to_jacobian * sum_priv_keys).to_affine
|
|
35
45
|
return [] if agg_pubkey.infinity?
|
|
36
46
|
|
|
47
|
+
outpoint_l = inputs.map{|i|i.out_point.to_hex}.sort.first
|
|
48
|
+
|
|
37
49
|
input_hash = Bitcoin.tagged_hash("BIP0352/Inputs", outpoint_l.htb + agg_pubkey.to_hex.htb).bth
|
|
38
50
|
|
|
39
51
|
destinations = {}
|
|
@@ -42,6 +54,12 @@ module Bitcoin
|
|
|
42
54
|
destinations[sp_addr.scan_key] = [] unless destinations.has_key?(sp_addr.scan_key)
|
|
43
55
|
destinations[sp_addr.scan_key] << sp_addr.spend_key
|
|
44
56
|
end
|
|
57
|
+
|
|
58
|
+
# Check K_max limit: fail if any group exceeds the limit
|
|
59
|
+
destinations.each_value do |spends|
|
|
60
|
+
raise ArgumentError, "Recipient group exceeds K_max limit (#{K_MAX})." if spends.length > K_MAX
|
|
61
|
+
end
|
|
62
|
+
|
|
45
63
|
outputs = []
|
|
46
64
|
destinations.each do |scan_key, spends|
|
|
47
65
|
scan_key = Bitcoin::Key.new(pubkey: scan_key).to_point.to_jacobian
|
|
@@ -55,6 +73,89 @@ module Bitcoin
|
|
|
55
73
|
outputs
|
|
56
74
|
end
|
|
57
75
|
|
|
76
|
+
|
|
77
|
+
# Scan transaction outputs for silent payment outputs belonging to the receiver.
|
|
78
|
+
#
|
|
79
|
+
# @param [Array<Bitcoin::Script>] prevouts An array of previous output scripts corresponding to each input.
|
|
80
|
+
# @param [Bitcoin::Key] scan_private_key The receiver's scan private key (b_scan).
|
|
81
|
+
# @param [Bitcoin::Key] spend_pubkey The receiver's spend key. Pass a Key initialized with spend_priv_key to derive the public key.
|
|
82
|
+
# @param [Array<Integer>] labels An array of label integers for labeled addresses (default: []).
|
|
83
|
+
# @return [Array<Bitcoin::SilentPayment::Output>] An array of detected silent payment outputs.
|
|
84
|
+
# @raise [ArgumentError] If any of the required parameters are invalid.
|
|
85
|
+
def scan_sp_outputs(prevouts, scan_private_key, spend_pubkey, labels = [])
|
|
86
|
+
raise ArgumentError, "prevouts must be Array." unless prevouts.is_a? Array
|
|
87
|
+
raise ArgumentError, "scan_private_key must be Bitcoin::Key." unless scan_private_key.is_a? Bitcoin::Key
|
|
88
|
+
raise ArgumentError, "spend_pubkey must be Bitcoin::Key." unless spend_pubkey.is_a? Bitcoin::Key
|
|
89
|
+
|
|
90
|
+
has_taproot = !outputs.find{|o| o.script_pubkey.p2tr? }.nil?
|
|
91
|
+
return [] unless has_taproot
|
|
92
|
+
sum_pub_keys = Bitcoin::Secp256k1::GROUP.infinity.to_jacobian
|
|
93
|
+
maximum_witness_version = Bitcoin::Opcodes.opcode_to_small_int(Bitcoin::Opcodes::OP_1)
|
|
94
|
+
prevouts.each.with_index do |prevout, index|
|
|
95
|
+
return [] if prevout.witness_program? && prevout.witness_data.first > maximum_witness_version
|
|
96
|
+
|
|
97
|
+
public_key = extract_public_key(prevout, inputs[index])
|
|
98
|
+
next if public_key.nil?
|
|
99
|
+
sum_pub_keys += public_key.to_point.to_jacobian
|
|
100
|
+
end
|
|
101
|
+
return [] if sum_pub_keys.infinity?
|
|
102
|
+
|
|
103
|
+
field = ECDSA::PrimeField.new(Bitcoin::Secp256k1::GROUP.order)
|
|
104
|
+
outpoint_l = inputs.map{|i|i.out_point.to_hex}.sort.first
|
|
105
|
+
input_hash = Bitcoin.tagged_hash("BIP0352/Inputs", outpoint_l.htb + sum_pub_keys.to_affine.to_hex.htb).bth
|
|
106
|
+
ecdh_shared_secret = (sum_pub_keys * field.mod(input_hash.to_i(16) * scan_private_key.priv_key.to_i(16))).to_affine.to_hex.htb
|
|
107
|
+
|
|
108
|
+
# Pre-compute label tweak points with their label values and scalar tweaks
|
|
109
|
+
label_tweaks = labels.map do |m|
|
|
110
|
+
label_tweak = Bitcoin.tagged_hash('BIP0352/Label', scan_private_key.priv_key.htb + [m].pack('N'))
|
|
111
|
+
label_point = Bitcoin::Secp256k1::GROUP.generator.to_jacobian * label_tweak.bti
|
|
112
|
+
[m, label_tweak, label_point]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
k = 0
|
|
116
|
+
results = []
|
|
117
|
+
found_outputs = Set.new
|
|
118
|
+
loop do
|
|
119
|
+
# Stop scanning if K_max limit is reached
|
|
120
|
+
break if k == K_MAX
|
|
121
|
+
|
|
122
|
+
t_k = Bitcoin.tagged_hash('BIP0352/SharedSecret', ecdh_shared_secret + [k].pack('N'))
|
|
123
|
+
p_k = Bitcoin::Secp256k1::GROUP.generator.to_jacobian * t_k.bti + spend_pubkey.to_point.to_jacobian
|
|
124
|
+
found = false
|
|
125
|
+
outputs.each do |output|
|
|
126
|
+
next unless output.script_pubkey.p2tr?
|
|
127
|
+
next if found_outputs.include?(output)
|
|
128
|
+
output_pubkey = Bitcoin::Key.from_xonly_pubkey(output.script_pubkey.witness_data[1].bth)
|
|
129
|
+
|
|
130
|
+
# Check basic match (no label)
|
|
131
|
+
if p_k.to_affine.x == output_pubkey.to_point.x
|
|
132
|
+
results << SilentPayment::Output.new(output, t_k)
|
|
133
|
+
found_outputs << output
|
|
134
|
+
k += 1
|
|
135
|
+
found = true
|
|
136
|
+
break
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Check labeled matches
|
|
140
|
+
label_tweaks.each do |label_value, label_tweak_scalar, label_point|
|
|
141
|
+
p_k_labeled = p_k + label_point
|
|
142
|
+
if p_k_labeled.to_affine.x == output_pubkey.to_point.x
|
|
143
|
+
# Full tweak is t_k + label_tweak (mod order)
|
|
144
|
+
full_tweak = field.mod(t_k.bti + label_tweak_scalar.bti).to_s(16).rjust(64, '0').htb
|
|
145
|
+
results << SilentPayment::Output.new(output, full_tweak, label_value)
|
|
146
|
+
found_outputs << output
|
|
147
|
+
k += 1
|
|
148
|
+
found = true
|
|
149
|
+
break
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
break if found
|
|
153
|
+
end
|
|
154
|
+
break unless found
|
|
155
|
+
end
|
|
156
|
+
results
|
|
157
|
+
end
|
|
158
|
+
|
|
58
159
|
# Extract public keys from +prevout+ and input.
|
|
59
160
|
def extract_public_key(prevout, input)
|
|
60
161
|
if prevout.p2pkh?
|
|
@@ -24,6 +24,7 @@ module Bitcoin
|
|
|
24
24
|
# @return [Bitcoin::Taproot::ControlBlock]
|
|
25
25
|
def self.parse_from_payload(payload)
|
|
26
26
|
raise Bitcoin::Taproot::Error, 'Invalid data length for Control Block' if payload.bytesize > TAPROOT_CONTROL_MAX_SIZE
|
|
27
|
+
raise Bitcoin::Taproot::Error, 'Invalid data length for Control Block' if payload.bytesize < TAPROOT_CONTROL_BASE_SIZE
|
|
27
28
|
raise Bitcoin::Taproot::Error, 'Invalid data length for path in Control Block' unless (payload.bytesize - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE == 0
|
|
28
29
|
control, internal_key, paths = payload.unpack('Ca32a*')
|
|
29
30
|
parity = control & 1
|
data/lib/bitcoin/tx.rb
CHANGED
|
@@ -89,6 +89,10 @@ module Bitcoin
|
|
|
89
89
|
to_hex.to_i(16)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
def eql?(other)
|
|
93
|
+
other.is_a?(Tx) && self == other
|
|
94
|
+
end
|
|
95
|
+
|
|
92
96
|
def tx_hash
|
|
93
97
|
Bitcoin.double_sha256(serialize_old_format).bth
|
|
94
98
|
end
|
|
@@ -228,7 +232,7 @@ module Bitcoin
|
|
|
228
232
|
# @param [Integer] input_index
|
|
229
233
|
# @param [Bitcoin::Script] script_pubkey the script pubkey for target input.
|
|
230
234
|
# @param [Integer] amount the amount of bitcoin, require for witness program only.
|
|
231
|
-
# @param [
|
|
235
|
+
# @param [Integer] flags the flags used when execute script interpreter.
|
|
232
236
|
# @param [Array[Bitcoin::TxOut]] prevouts Previous outputs referenced by all Tx inputs, required for taproot.
|
|
233
237
|
# @return [Boolean] result
|
|
234
238
|
def verify_input_sig(input_index, script_pubkey, amount: nil, flags: STANDARD_SCRIPT_VERIFY_FLAGS, prevouts: [])
|
|
@@ -236,11 +240,9 @@ module Bitcoin
|
|
|
236
240
|
has_witness = inputs[input_index].has_witness?
|
|
237
241
|
has_witness = true if script_pubkey.witness_program?
|
|
238
242
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
script_pubkey = redeem_script if redeem_script.p2wpkh?
|
|
243
|
-
end
|
|
243
|
+
# The script interpreter resolves the redeem script of P2SH, including a nested witness
|
|
244
|
+
# program, from the scriptSig, so the scriptPubkey is passed through as it is.
|
|
245
|
+
flags |= SCRIPT_VERIFY_P2SH if script_pubkey.p2sh?
|
|
244
246
|
|
|
245
247
|
if has_witness
|
|
246
248
|
verify_input_sig_for_witness(input_index, script_pubkey, amount, flags, prevouts)
|
data/lib/bitcoin/tx_out.rb
CHANGED
|
@@ -16,13 +16,13 @@ module Bitcoin
|
|
|
16
16
|
|
|
17
17
|
def self.parse_from_payload(payload)
|
|
18
18
|
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
|
19
|
-
value = buf.read(8).unpack1('
|
|
19
|
+
value = buf.read(8).unpack1('Q<')
|
|
20
20
|
script_size = Bitcoin.unpack_var_int_from_io(buf)
|
|
21
21
|
new(value: value, script_pubkey: Script.parse_from_payload(buf.read(script_size)))
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def to_payload
|
|
25
|
-
[value].pack('Q') << script_pubkey.to_payload(true)
|
|
25
|
+
[value].pack('Q<') << script_pubkey.to_payload(true)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def to_empty_payload
|
data/lib/bitcoin/util.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Bitcoin
|
|
|
14
14
|
|
|
15
15
|
def unpack_var_string(payload)
|
|
16
16
|
size, payload = unpack_var_int(payload)
|
|
17
|
-
size > 0 ? payload.unpack("a#{size}a*") : [
|
|
17
|
+
size && size > 0 ? payload.unpack("a#{size}a*") : ['', payload.to_s]
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def pack_var_int(i)
|
|
@@ -25,7 +25,7 @@ module Bitcoin
|
|
|
25
25
|
elsif i <= 0xffffffff
|
|
26
26
|
[0xfe, i].pack('CV')
|
|
27
27
|
elsif i <= 0xffffffffffffffff
|
|
28
|
-
[0xff, i].pack('CQ')
|
|
28
|
+
[0xff, i].pack('CQ<')
|
|
29
29
|
else
|
|
30
30
|
raise "int(#{i}) too large!"
|
|
31
31
|
end
|
|
@@ -39,7 +39,7 @@ module Bitcoin
|
|
|
39
39
|
when 0xfe
|
|
40
40
|
payload.unpack('xVa*')
|
|
41
41
|
when 0xff
|
|
42
|
-
payload.unpack('
|
|
42
|
+
payload.unpack('xQ<a*')
|
|
43
43
|
else
|
|
44
44
|
payload.unpack('Ca*')
|
|
45
45
|
end
|
|
@@ -54,7 +54,7 @@ module Bitcoin
|
|
|
54
54
|
when 0xfe
|
|
55
55
|
buf.read(4)&.unpack1('V')
|
|
56
56
|
when 0xff
|
|
57
|
-
buf.read(8)&.unpack1('Q')
|
|
57
|
+
buf.read(8)&.unpack1('Q<')
|
|
58
58
|
else
|
|
59
59
|
uchar
|
|
60
60
|
end
|
|
@@ -102,7 +102,7 @@ module Bitcoin
|
|
|
102
102
|
# @return [String] the hash value with binary format.
|
|
103
103
|
def tagged_hash(tag, msg)
|
|
104
104
|
tag_hash = Digest::SHA256.digest(tag)
|
|
105
|
-
Digest::SHA256.digest(tag_hash + tag_hash + msg)
|
|
105
|
+
Digest::SHA256.digest(tag_hash + tag_hash + msg.b)
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
# encode Base58 check address.
|
data/lib/bitcoin/version.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Bitcoin
|
|
|
33
33
|
payload = buf.read
|
|
34
34
|
name, payload = Bitcoin.unpack_var_string(payload)
|
|
35
35
|
name = name.force_encoding('utf-8')
|
|
36
|
-
purpose, index, receive_depth, change_depth, lookahead = payload.unpack('
|
|
36
|
+
purpose, index, receive_depth, change_depth, lookahead = payload.unpack('V*')
|
|
37
37
|
a = Account.new(account_key, purpose, index, name)
|
|
38
38
|
a.receive_depth = receive_depth
|
|
39
39
|
a.change_depth = change_depth
|
|
@@ -44,7 +44,7 @@ module Bitcoin
|
|
|
44
44
|
def to_payload
|
|
45
45
|
payload = account_key.to_payload
|
|
46
46
|
payload << Bitcoin.pack_var_string(name.unpack1('H*').htb)
|
|
47
|
-
payload << [purpose, index, receive_depth, change_depth, lookahead].pack('
|
|
47
|
+
payload << [purpose, index, receive_depth, change_depth, lookahead].pack('V*')
|
|
48
48
|
payload
|
|
49
49
|
end
|
|
50
50
|
|
data/lib/bitcoin/wallet/db.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Bitcoin
|
|
|
32
32
|
|
|
33
33
|
def save_account(account)
|
|
34
34
|
level_db.batch do
|
|
35
|
-
id = [account.purpose, account.index].pack('
|
|
35
|
+
id = [account.purpose, account.index].pack('V*').bth
|
|
36
36
|
key = KEY_PREFIX[:account] + id
|
|
37
37
|
level_db.put(key, account.to_payload)
|
|
38
38
|
end
|
|
@@ -40,14 +40,14 @@ module Bitcoin
|
|
|
40
40
|
|
|
41
41
|
def save_key(account, purpose, index, key)
|
|
42
42
|
pubkey = key.pub
|
|
43
|
-
id = [account.purpose, account.index, purpose, index].pack('
|
|
43
|
+
id = [account.purpose, account.index, purpose, index].pack('V*').bth
|
|
44
44
|
k = KEY_PREFIX[:key] + id
|
|
45
45
|
level_db.put(k, pubkey)
|
|
46
46
|
key
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def get_keys(account)
|
|
50
|
-
id = [account.purpose, account.index].pack('
|
|
50
|
+
id = [account.purpose, account.index].pack('V*').bth
|
|
51
51
|
from = KEY_PREFIX[:key] + id + '00000000'
|
|
52
52
|
to = KEY_PREFIX[:key] + id + 'ffffffff'
|
|
53
53
|
level_db.each(from: from, to: to).map { |k, v| v}
|
|
@@ -81,7 +81,7 @@ module Bitcoin
|
|
|
81
81
|
encrypted_data = ''
|
|
82
82
|
encrypted_data << enc.update(seed)
|
|
83
83
|
encrypted_data << enc.final
|
|
84
|
-
@seed = encrypted_data
|
|
84
|
+
@seed = encrypted_data.bth
|
|
85
85
|
@encrypted = true
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -92,7 +92,7 @@ module Bitcoin
|
|
|
92
92
|
dec.decrypt
|
|
93
93
|
dec.key, dec.iv = key_iv(dec, passphrase)
|
|
94
94
|
decrypted_data = ''
|
|
95
|
-
decrypted_data << dec.update(seed)
|
|
95
|
+
decrypted_data << dec.update(seed.htb)
|
|
96
96
|
decrypted_data << dec.final
|
|
97
97
|
@seed = decrypted_data
|
|
98
98
|
@encrypted = false
|
data/lib/bitcoin/wallet/utxo.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Bitcoin
|
|
|
18
18
|
def self.parse_from_payload(payload)
|
|
19
19
|
return nil if payload.nil?
|
|
20
20
|
|
|
21
|
-
tx_hash, index, block_height, value, payload = payload.unpack('
|
|
21
|
+
tx_hash, index, block_height, value, payload = payload.unpack('H64VVQ<a*')
|
|
22
22
|
|
|
23
23
|
buf = StringIO.new(payload)
|
|
24
24
|
script_size = Bitcoin.unpack_var_int_from_io(buf)
|
|
@@ -27,7 +27,7 @@ module Bitcoin
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def to_payload
|
|
30
|
-
payload = [tx_hash, index, block_height.nil? ? 0 : block_height, value].pack('H64VVQ')
|
|
30
|
+
payload = [tx_hash, index, block_height.nil? ? 0 : block_height, value].pack('H64VVQ<')
|
|
31
31
|
s = script_pubkey.to_payload
|
|
32
32
|
payload << Bitcoin.pack_var_int(s.length) << s
|
|
33
33
|
payload
|
data/lib/bitcoin.rb
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
# https://github.com/lian/bitcoin-ruby/blob/master/COPYING
|
|
3
3
|
|
|
4
4
|
require 'bitcoin/version'
|
|
5
|
-
require 'eventmachine'
|
|
6
5
|
require 'schnorr'
|
|
7
6
|
require 'securerandom'
|
|
7
|
+
require 'stringio'
|
|
8
8
|
require 'json'
|
|
9
9
|
require 'bech32'
|
|
10
10
|
require 'base64'
|
|
11
|
-
require 'observer'
|
|
12
11
|
require 'tmpdir'
|
|
13
12
|
require 'merkle'
|
|
14
13
|
|
|
@@ -36,13 +35,10 @@ module Bitcoin
|
|
|
36
35
|
autoload :ExtKey, 'bitcoin/ext_key'
|
|
37
36
|
autoload :ExtPubkey, 'bitcoin/ext_key'
|
|
38
37
|
autoload :Opcodes, 'bitcoin/opcodes'
|
|
39
|
-
autoload :Node, 'bitcoin/node'
|
|
40
38
|
autoload :Base58, 'bitcoin/base58'
|
|
41
39
|
autoload :Secp256k1, 'bitcoin/secp256k1'
|
|
42
40
|
autoload :Mnemonic, 'bitcoin/mnemonic'
|
|
43
41
|
autoload :ValidationState, 'bitcoin/validation'
|
|
44
|
-
autoload :Network, 'bitcoin/network'
|
|
45
|
-
autoload :Store, 'bitcoin/store'
|
|
46
42
|
autoload :RPC, 'bitcoin/rpc'
|
|
47
43
|
autoload :Wallet, 'bitcoin/wallet'
|
|
48
44
|
autoload :BloomFilter, 'bitcoin/bloom_filter'
|
|
@@ -64,7 +60,6 @@ module Bitcoin
|
|
|
64
60
|
autoload :SilentPayment, 'bitcoin/silent_payment'
|
|
65
61
|
autoload :BIP324, 'bitcoin/bip324'
|
|
66
62
|
autoload :BIP321URI, 'bitcoin/bip321_uri'
|
|
67
|
-
autoload :BIP353, 'bitcoin/bip353'
|
|
68
63
|
|
|
69
64
|
require_relative 'bitcoin/constants'
|
|
70
65
|
require_relative 'bitcoin/ext/ecdsa'
|