bitcoinrb 0.3.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.rspec_parallel +2 -0
- data/.ruby-version +1 -1
- data/README.md +17 -6
- data/bitcoinrb.gemspec +9 -8
- data/exe/bitcoinrbd +5 -0
- data/lib/bitcoin.rb +37 -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 +44 -10
- data/lib/bitcoin/descriptor.rb +1 -1
- data/lib/bitcoin/errors.rb +19 -0
- data/lib/bitcoin/ext.rb +6 -0
- data/lib/bitcoin/ext/array_ext.rb +22 -0
- data/lib/bitcoin/ext/ecdsa.rb +36 -0
- data/lib/bitcoin/ext/json_parser.rb +46 -0
- data/lib/bitcoin/ext_key.rb +51 -20
- data/lib/bitcoin/key.rb +89 -30
- 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/tx.rb +1 -1
- data/lib/bitcoin/message/version.rb +7 -0
- data/lib/bitcoin/message_sign.rb +47 -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 +2 -0
- data/lib/bitcoin/payment_code.rb +92 -0
- data/lib/bitcoin/payments/payment.pb.rb +1 -1
- data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
- data/lib/bitcoin/psbt/input.rb +9 -18
- data/lib/bitcoin/psbt/output.rb +1 -1
- data/lib/bitcoin/psbt/tx.rb +12 -17
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
- data/lib/bitcoin/rpc/request_handler.rb +5 -5
- data/lib/bitcoin/script/script.rb +96 -39
- data/lib/bitcoin/script/script_error.rb +27 -1
- data/lib/bitcoin/script/script_interpreter.rb +166 -66
- data/lib/bitcoin/script/tx_checker.rb +62 -14
- data/lib/bitcoin/secp256k1.rb +1 -0
- data/lib/bitcoin/secp256k1/native.rb +184 -17
- data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
- data/lib/bitcoin/secp256k1/ruby.rb +112 -56
- data/lib/bitcoin/sighash_generator.rb +156 -0
- data/lib/bitcoin/store.rb +1 -0
- data/lib/bitcoin/store/chain_entry.rb +1 -0
- data/lib/bitcoin/store/utxo_db.rb +226 -0
- data/lib/bitcoin/taproot.rb +9 -0
- data/lib/bitcoin/taproot/leaf_node.rb +23 -0
- data/lib/bitcoin/taproot/simple_builder.rb +139 -0
- data/lib/bitcoin/tx.rb +34 -104
- data/lib/bitcoin/tx_in.rb +4 -5
- data/lib/bitcoin/tx_out.rb +2 -3
- data/lib/bitcoin/util.rb +22 -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 +2 -2
- data/lib/bitcoin/wallet/master_key.rb +1 -0
- data/lib/bitcoin/wallet/utxo.rb +37 -0
- metadata +86 -32
- data/.travis.yml +0 -11
data/lib/bitcoin/psbt/output.rb
CHANGED
data/lib/bitcoin/psbt/tx.rb
CHANGED
@@ -2,6 +2,7 @@ module Bitcoin
|
|
2
2
|
module PSBT
|
3
3
|
|
4
4
|
class GlobalXpub
|
5
|
+
include Bitcoin::HexConverter
|
5
6
|
|
6
7
|
attr_reader :xpub # Bitcoin::ExtPubkey
|
7
8
|
attr_reader :info # Bitcoin::PSBT::KeyOriginInfo
|
@@ -16,7 +17,7 @@ module Bitcoin
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def to_h
|
19
|
-
{xpub: xpub.
|
20
|
+
{xpub: xpub.to_hex}.merge(info.to_h)
|
20
21
|
end
|
21
22
|
|
22
23
|
def to_s
|
@@ -25,6 +26,8 @@ module Bitcoin
|
|
25
26
|
end
|
26
27
|
|
27
28
|
class Tx
|
29
|
+
include Bitcoin::HexConverter
|
30
|
+
|
28
31
|
attr_accessor :tx
|
29
32
|
attr_accessor :xpubs
|
30
33
|
attr_reader :inputs
|
@@ -52,7 +55,7 @@ module Bitcoin
|
|
52
55
|
# @return [Bitcoin::PartiallySignedTx]
|
53
56
|
def self.parse_from_payload(payload)
|
54
57
|
buf = StringIO.new(payload)
|
55
|
-
raise ArgumentError, 'Invalid PSBT magic bytes.' unless buf.read(4).
|
58
|
+
raise ArgumentError, 'Invalid PSBT magic bytes.' unless buf.read(4).unpack1('N') == PSBT_MAGIC_BYTES
|
56
59
|
raise ArgumentError, 'Invalid PSBT separator.' unless buf.read(1).bth.to_i(16) == 0xff
|
57
60
|
partial_tx = self.new
|
58
61
|
found_sep = false
|
@@ -63,7 +66,7 @@ module Bitcoin
|
|
63
66
|
found_sep = true
|
64
67
|
break
|
65
68
|
end
|
66
|
-
key_type = buf.read(1).
|
69
|
+
key_type = buf.read(1).unpack1('C')
|
67
70
|
key = buf.read(key_len - 1)
|
68
71
|
value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
|
69
72
|
|
@@ -71,20 +74,20 @@ module Bitcoin
|
|
71
74
|
when PSBT_GLOBAL_TYPES[:unsigned_tx]
|
72
75
|
raise ArgumentError, 'Invalid global transaction typed key.' unless key_len == 1
|
73
76
|
raise ArgumentError, 'Duplicate Key, unsigned tx already provided.' if partial_tx.tx
|
74
|
-
partial_tx.tx = Bitcoin::Tx.parse_from_payload(value, non_witness: true)
|
77
|
+
partial_tx.tx = Bitcoin::Tx.parse_from_payload(value, non_witness: true, strict: true)
|
75
78
|
partial_tx.tx.in.each do |tx_in|
|
76
79
|
raise ArgumentError, 'Unsigned tx does not have empty scriptSigs and scriptWitnesses.' if !tx_in.script_sig.empty? || !tx_in.script_witness.empty?
|
77
80
|
end
|
78
81
|
when PSBT_GLOBAL_TYPES[:xpub]
|
79
82
|
raise ArgumentError, 'Size of key was not the expected size for the type global xpub.' unless key.size == Bitcoin::BIP32_EXTKEY_WITH_VERSION_SIZE
|
80
83
|
xpub = Bitcoin::ExtPubkey.parse_from_payload(key)
|
81
|
-
raise ArgumentError,
|
84
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless xpub.key.fully_valid_pubkey?
|
82
85
|
raise ArgumentError, 'Duplicate key, global xpub already provided' if partial_tx.xpubs.any?{|x|x.xpub == xpub}
|
83
86
|
info = Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value)
|
84
87
|
raise ArgumentError, "global xpub's depth and the number of indexes not matched." unless xpub.depth == info.key_paths.size
|
85
88
|
partial_tx.xpubs << Bitcoin::PSBT::GlobalXpub.new(xpub, info)
|
86
89
|
when PSBT_GLOBAL_TYPES[:ver]
|
87
|
-
partial_tx.version_number = value.
|
90
|
+
partial_tx.version_number = value.unpack1('V')
|
88
91
|
raise ArgumentError, "An unsupported version was detected." if SUPPORT_VERSION < partial_tx.version_number
|
89
92
|
else
|
90
93
|
raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[key]
|
@@ -117,10 +120,6 @@ module Bitcoin
|
|
117
120
|
|
118
121
|
raise ArgumentError, 'Outputs provided does not match the number of outputs in transaction.' unless partial_tx.outputs.size == partial_tx.tx.out.size
|
119
122
|
|
120
|
-
partial_tx.inputs.each do |input|
|
121
|
-
raise ArgumentError, 'PSBT is not sane.' unless input.sane?
|
122
|
-
end
|
123
|
-
|
124
123
|
partial_tx
|
125
124
|
end
|
126
125
|
|
@@ -149,11 +148,9 @@ module Bitcoin
|
|
149
148
|
payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:unsigned_tx], value: tx.to_payload)
|
150
149
|
payload << xpubs.map(&:to_payload).join
|
151
150
|
payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:ver], value: [version_number].pack('V')) if version_number
|
152
|
-
|
153
151
|
payload << unknowns.map {|k,v|Bitcoin.pack_var_int(k.htb.bytesize) << k.htb << Bitcoin.pack_var_int(v.bytesize) << v}.join
|
154
152
|
|
155
153
|
payload << PSBT_SEPARATOR.itb
|
156
|
-
|
157
154
|
payload << inputs.map(&:to_payload).join
|
158
155
|
payload << outputs.map(&:to_payload).join
|
159
156
|
payload
|
@@ -177,14 +174,12 @@ module Bitcoin
|
|
177
174
|
utxo = prev_tx.out[tx_in.out_point.index]
|
178
175
|
raise ArgumentError, 'redeem script does not match utxo.' if redeem_script && !utxo.script_pubkey.include?(redeem_script.to_hash160)
|
179
176
|
raise ArgumentError, 'witness script does not match redeem script.' if redeem_script && witness_script && !redeem_script.include?(witness_script.to_sha256)
|
180
|
-
if utxo.script_pubkey.witness_program? ||
|
181
|
-
|
182
|
-
else
|
183
|
-
inputs[i].non_witness_utxo = prev_tx
|
184
|
-
end
|
177
|
+
inputs[i].witness_utxo = utxo if utxo.script_pubkey.witness_program? || redeem_script&.witness_program?
|
178
|
+
inputs[i].non_witness_utxo = prev_tx
|
185
179
|
inputs[i].redeem_script = redeem_script if redeem_script
|
186
180
|
inputs[i].witness_script = witness_script if witness_script
|
187
181
|
inputs[i].hd_key_paths = hd_key_paths.map(&:pubkey).zip(hd_key_paths).to_h
|
182
|
+
break
|
188
183
|
end
|
189
184
|
end
|
190
185
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'net/http'
|
2
2
|
|
3
3
|
module Bitcoin
|
4
4
|
module RPC
|
@@ -53,20 +53,30 @@ module Bitcoin
|
|
53
53
|
:params => params,
|
54
54
|
:id => 'jsonrpc'
|
55
55
|
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
uri = URI.parse(server_url)
|
57
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
58
|
+
http.use_ssl = uri.scheme === "https"
|
59
|
+
request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
|
60
|
+
request.basic_auth(uri.user, uri.password)
|
61
|
+
request.content_type = 'application/json'
|
62
|
+
request.body = data.to_json
|
63
|
+
response = http.request(request)
|
64
|
+
body = response.body
|
65
|
+
response = Bitcoin::Ext::JsonParser.new(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') }).parse
|
66
|
+
raise response['error'].to_json if response['error']
|
67
|
+
response['result']
|
62
68
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
69
|
+
|
70
|
+
# Call CLI command on Ruby-like method names.
|
71
|
+
# e.g. generate_to_address, send_to_address, get_wallet_info
|
72
|
+
def method_missing(name, *args)
|
73
|
+
if name.to_s.include?('_')
|
74
|
+
send(name.to_s.gsub('_', '').to_sym, args)
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
67
78
|
end
|
68
79
|
|
69
80
|
end
|
70
|
-
|
71
81
|
end
|
72
82
|
end
|
@@ -42,14 +42,14 @@ module Bitcoin
|
|
42
42
|
nextblockhash: node.chain.next_hash(block_hash).rhex
|
43
43
|
}
|
44
44
|
else
|
45
|
-
entry.header.
|
45
|
+
entry.header.to_hex
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns connected peer information.
|
50
50
|
def getpeerinfo
|
51
51
|
node.pool.peers.map do |peer|
|
52
|
-
local_addr = "#{peer.remote_version.remote_addr.
|
52
|
+
local_addr = "#{peer.remote_version.remote_addr.addr_string}:18333"
|
53
53
|
{
|
54
54
|
id: peer.id,
|
55
55
|
addr: "#{peer.host}:#{peer.port}",
|
@@ -75,7 +75,7 @@ module Bitcoin
|
|
75
75
|
|
76
76
|
# broadcast transaction
|
77
77
|
def sendrawtransaction(hex_tx)
|
78
|
-
tx = Bitcoin::Tx.parse_from_payload(hex_tx.htb)
|
78
|
+
tx = Bitcoin::Tx.parse_from_payload(hex_tx.htb, strict: true)
|
79
79
|
# TODO check wether tx is valid
|
80
80
|
node.broadcast(tx)
|
81
81
|
tx.txid
|
@@ -84,7 +84,7 @@ module Bitcoin
|
|
84
84
|
# decode tx data.
|
85
85
|
def decoderawtransaction(hex_tx)
|
86
86
|
begin
|
87
|
-
Bitcoin::Tx.parse_from_payload(hex_tx.htb).to_h
|
87
|
+
Bitcoin::Tx.parse_from_payload(hex_tx.htb, strict: true ).to_h
|
88
88
|
rescue Exception
|
89
89
|
raise ArgumentError.new('TX decode failed')
|
90
90
|
end
|
@@ -96,7 +96,7 @@ module Bitcoin
|
|
96
96
|
script = Bitcoin::Script.parse_from_payload(hex_script.htb)
|
97
97
|
h = script.to_h
|
98
98
|
h.delete(:hex)
|
99
|
-
h[:p2sh] = script.to_p2sh.
|
99
|
+
h[:p2sh] = script.to_p2sh.to_addr unless script.p2sh?
|
100
100
|
h
|
101
101
|
rescue Exception
|
102
102
|
raise ArgumentError.new('Script decode failed')
|
@@ -6,6 +6,7 @@ module Bitcoin
|
|
6
6
|
# bitcoin script
|
7
7
|
class Script
|
8
8
|
include Bitcoin::Opcodes
|
9
|
+
include Bitcoin::HexConverter
|
9
10
|
|
10
11
|
attr_accessor :chunks
|
11
12
|
|
@@ -20,7 +21,7 @@ module Bitcoin
|
|
20
21
|
|
21
22
|
# generate P2WPKH script
|
22
23
|
def self.to_p2wpkh(pubkey_hash)
|
23
|
-
new <<
|
24
|
+
new << WITNESS_VERSION_V0 << pubkey_hash
|
24
25
|
end
|
25
26
|
|
26
27
|
# generate m of n multisig p2sh script
|
@@ -51,7 +52,7 @@ module Bitcoin
|
|
51
52
|
end
|
52
53
|
|
53
54
|
# generate m of n multisig script
|
54
|
-
# @param [
|
55
|
+
# @param [Integer] m the number of signatures required for multisig
|
55
56
|
# @param [Array] pubkeys array of public keys that compose multisig
|
56
57
|
# @return [Script] multisig script.
|
57
58
|
def self.to_multisig_script(m, pubkeys, sort: false)
|
@@ -63,7 +64,7 @@ module Bitcoin
|
|
63
64
|
# @param [Script] redeem_script target redeem script
|
64
65
|
# @param [Script] p2wsh script
|
65
66
|
def self.to_p2wsh(redeem_script)
|
66
|
-
new <<
|
67
|
+
new << WITNESS_VERSION_V0 << redeem_script.to_sha256
|
67
68
|
end
|
68
69
|
|
69
70
|
# generate script from string.
|
@@ -86,17 +87,21 @@ module Bitcoin
|
|
86
87
|
def self.parse_from_addr(addr)
|
87
88
|
begin
|
88
89
|
segwit_addr = Bech32::SegwitAddr.new(addr)
|
89
|
-
raise 'Invalid
|
90
|
+
raise ArgumentError, 'Invalid address.' unless Bitcoin.chain_params.bech32_hrp == segwit_addr.hrp
|
90
91
|
Bitcoin::Script.parse_from_payload(segwit_addr.to_script_pubkey.htb)
|
91
92
|
rescue Exception => e
|
93
|
+
begin
|
92
94
|
hex, addr_version = Bitcoin.decode_base58_address(addr)
|
95
|
+
rescue
|
96
|
+
raise ArgumentError, 'Invalid address.'
|
97
|
+
end
|
93
98
|
case addr_version
|
94
99
|
when Bitcoin.chain_params.address_version
|
95
100
|
Bitcoin::Script.to_p2pkh(hex)
|
96
101
|
when Bitcoin.chain_params.p2sh_version
|
97
102
|
Bitcoin::Script.to_p2sh(hex)
|
98
103
|
else
|
99
|
-
|
104
|
+
raise ArgumentError, 'Invalid address.'
|
100
105
|
end
|
101
106
|
end
|
102
107
|
end
|
@@ -109,25 +114,28 @@ module Bitcoin
|
|
109
114
|
if opcode.pushdata?
|
110
115
|
pushcode = opcode.ord
|
111
116
|
packed_size = nil
|
117
|
+
if buf.eof?
|
118
|
+
s.chunks << opcode
|
119
|
+
return s
|
120
|
+
end
|
112
121
|
len = case pushcode
|
113
122
|
when OP_PUSHDATA1
|
114
123
|
packed_size = buf.read(1)
|
115
|
-
packed_size.
|
124
|
+
packed_size.unpack1('C')
|
116
125
|
when OP_PUSHDATA2
|
117
126
|
packed_size = buf.read(2)
|
118
|
-
packed_size.
|
127
|
+
packed_size.unpack1('v')
|
119
128
|
when OP_PUSHDATA4
|
120
129
|
packed_size = buf.read(4)
|
121
|
-
packed_size.
|
130
|
+
packed_size.unpack1('V')
|
122
131
|
else
|
123
|
-
pushcode
|
132
|
+
pushcode < OP_PUSHDATA1 ? pushcode : 0
|
124
133
|
end
|
125
|
-
if
|
126
|
-
s.chunks << [len].pack('C')
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
134
|
+
if buf.eof?
|
135
|
+
s.chunks << [len].pack('C')
|
136
|
+
else buf.eof?
|
137
|
+
chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
|
138
|
+
s.chunks << chunk
|
131
139
|
end
|
132
140
|
else
|
133
141
|
if Opcodes.defined?(opcode.ord)
|
@@ -140,15 +148,21 @@ module Bitcoin
|
|
140
148
|
s
|
141
149
|
end
|
142
150
|
|
143
|
-
|
144
|
-
|
151
|
+
# Output script payload.
|
152
|
+
# @param [Boolean] length_prefixed Flag whether the length of the payload should be given at the beginning.(default: false)
|
153
|
+
# @return [String] payload
|
154
|
+
def to_payload(length_prefixed = false)
|
155
|
+
p = chunks.join
|
156
|
+
length_prefixed ? (Bitcoin.pack_var_int(p.length) << p) : p
|
145
157
|
end
|
146
158
|
|
147
159
|
def empty?
|
148
160
|
chunks.size == 0
|
149
161
|
end
|
150
162
|
|
163
|
+
# @deprecated
|
151
164
|
def addresses
|
165
|
+
puts "WARNING: Bitcoin::Script#addresses is deprecated. Use Bitcoin::Script#to_addr instead."
|
152
166
|
return [p2pkh_addr] if p2pkh?
|
153
167
|
return [p2sh_addr] if p2sh?
|
154
168
|
return [bech32_addr] if witness_program?
|
@@ -156,29 +170,51 @@ module Bitcoin
|
|
156
170
|
[]
|
157
171
|
end
|
158
172
|
|
173
|
+
# convert to address
|
174
|
+
# @return [String] if script type is p2pkh or p2sh or witness program, return address, otherwise nil.
|
175
|
+
def to_addr
|
176
|
+
return p2pkh_addr if p2pkh?
|
177
|
+
return p2sh_addr if p2sh?
|
178
|
+
return bech32_addr if witness_program?
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
159
182
|
# check whether standard script.
|
160
183
|
def standard?
|
161
|
-
p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
|
184
|
+
p2pkh? | p2sh? | p2wpkh? | p2wsh? | p2tr? | multisig? | standard_op_return?
|
162
185
|
end
|
163
186
|
|
164
|
-
# whether this script is a P2PKH format script.
|
187
|
+
# Check whether this script is a P2PKH format script.
|
188
|
+
# @return [Boolean] if P2PKH return true, otherwise false
|
165
189
|
def p2pkh?
|
166
190
|
return false unless chunks.size == 5
|
167
191
|
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
168
192
|
(chunks[0..1]+ chunks[3..4]).map(&:ord) && chunks[2].bytesize == 21
|
169
193
|
end
|
170
194
|
|
171
|
-
# whether this script is a P2WPKH format script.
|
195
|
+
# Check whether this script is a P2WPKH format script.
|
196
|
+
# @return [Boolean] if P2WPKH return true, otherwise false
|
172
197
|
def p2wpkh?
|
173
198
|
return false unless chunks.size == 2
|
174
|
-
chunks[0].ord ==
|
199
|
+
chunks[0].ord == WITNESS_VERSION_V0 && chunks[1].bytesize == 21
|
175
200
|
end
|
176
201
|
|
202
|
+
# Check whether this script is a P2WPSH format script.
|
203
|
+
# @return [Boolean] if P2WPSH return true, otherwise false
|
177
204
|
def p2wsh?
|
178
205
|
return false unless chunks.size == 2
|
179
|
-
chunks[0].ord ==
|
206
|
+
chunks[0].ord == WITNESS_VERSION_V0 && chunks[1].bytesize == 33
|
207
|
+
end
|
208
|
+
|
209
|
+
# Check whether this script is a P2TR format script.
|
210
|
+
# @return [Boolean] if P2TR return true, otherwise false
|
211
|
+
def p2tr?
|
212
|
+
return false unless chunks.size == 2
|
213
|
+
chunks[0].ord == WITNESS_VERSION_V1 && chunks[1].bytesize == 33
|
180
214
|
end
|
181
215
|
|
216
|
+
# Check whether this script is a P2SH format script.
|
217
|
+
# @return [Boolean] if P2SH return true, otherwise false
|
182
218
|
def p2sh?
|
183
219
|
return false unless chunks.size == 3
|
184
220
|
OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
|
@@ -230,7 +266,7 @@ module Bitcoin
|
|
230
266
|
return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
|
231
267
|
return false unless chunks[1].pushdata?
|
232
268
|
|
233
|
-
if size == (chunks[1][0].
|
269
|
+
if size == (chunks[1][0].unpack1('C') + 2)
|
234
270
|
program_size = chunks[1].pushed_data.bytesize
|
235
271
|
return program_size >= 2 && program_size <= 40
|
236
272
|
end
|
@@ -324,16 +360,21 @@ module Bitcoin
|
|
324
360
|
when Integer
|
325
361
|
opcode_to_name(c)
|
326
362
|
when String
|
363
|
+
return c if c.empty?
|
327
364
|
if c.pushdata?
|
328
365
|
v = Opcodes.opcode_to_small_int(c.ord)
|
329
366
|
if v
|
330
367
|
v
|
331
368
|
else
|
332
369
|
data = c.pushed_data
|
333
|
-
if data
|
334
|
-
|
370
|
+
if data
|
371
|
+
if data.bytesize <= 4
|
372
|
+
Script.decode_number(data.bth) # for scriptnum
|
373
|
+
else
|
374
|
+
data.bth
|
375
|
+
end
|
335
376
|
else
|
336
|
-
|
377
|
+
c.bth
|
337
378
|
end
|
338
379
|
end
|
339
380
|
else
|
@@ -351,7 +392,7 @@ module Bitcoin
|
|
351
392
|
|
352
393
|
# generate hash160 hash for payload
|
353
394
|
def to_hash160
|
354
|
-
Bitcoin.hash160(
|
395
|
+
Bitcoin.hash160(to_hex)
|
355
396
|
end
|
356
397
|
|
357
398
|
# script size
|
@@ -380,8 +421,8 @@ module Bitcoin
|
|
380
421
|
hex = '0' + hex unless (hex.length % 2).zero?
|
381
422
|
v = hex.htb.reverse # change endian
|
382
423
|
|
383
|
-
v = v << (negative ? 0x80 : 0x00) unless (v[-1].
|
384
|
-
v[-1] = [v[-1].
|
424
|
+
v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack1('C') & 0x80) == 0
|
425
|
+
v[-1] = [v[-1].unpack1('C') | 0x80].pack('C') if negative
|
385
426
|
v.bth
|
386
427
|
end
|
387
428
|
|
@@ -389,7 +430,7 @@ module Bitcoin
|
|
389
430
|
def self.decode_number(s)
|
390
431
|
v = s.htb.reverse
|
391
432
|
return 0 if v.length.zero?
|
392
|
-
mbs = v[0].
|
433
|
+
mbs = v[0].unpack1('C')
|
393
434
|
v[0] = [mbs - 0x80].pack('C') unless (mbs & 0x80) == 0
|
394
435
|
result = v.bth.to_i(16)
|
395
436
|
result = -result unless (mbs & 0x80) == 0
|
@@ -474,7 +515,7 @@ module Bitcoin
|
|
474
515
|
end
|
475
516
|
|
476
517
|
def ==(other)
|
477
|
-
return false unless other
|
518
|
+
return false unless other.is_a?(Script)
|
478
519
|
chunks == other.chunks
|
479
520
|
end
|
480
521
|
|
@@ -488,7 +529,7 @@ module Bitcoin
|
|
488
529
|
end
|
489
530
|
|
490
531
|
def to_h
|
491
|
-
h = {asm: to_s, hex:
|
532
|
+
h = {asm: to_s, hex: to_hex, type: type}
|
492
533
|
addrs = addresses
|
493
534
|
unless addrs.empty?
|
494
535
|
h[:req_sigs] = multisig? ? Bitcoin::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
|
@@ -504,12 +545,6 @@ module Bitcoin
|
|
504
545
|
(size > 0 && op_return?) || size > Bitcoin::MAX_SCRIPT_SIZE
|
505
546
|
end
|
506
547
|
|
507
|
-
# convert payload to hex data.
|
508
|
-
# @return [String] script with hex format.
|
509
|
-
def to_hex
|
510
|
-
to_payload.bth
|
511
|
-
end
|
512
|
-
|
513
548
|
private
|
514
549
|
|
515
550
|
# generate p2pkh address. if script dose not p2pkh, return nil.
|
@@ -542,10 +577,32 @@ module Bitcoin
|
|
542
577
|
def bech32_addr
|
543
578
|
segwit_addr = Bech32::SegwitAddr.new
|
544
579
|
segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
|
545
|
-
segwit_addr.script_pubkey =
|
580
|
+
segwit_addr.script_pubkey = to_hex
|
546
581
|
segwit_addr.addr
|
547
582
|
end
|
548
583
|
|
584
|
+
# Check whether push data length is valid.
|
585
|
+
# @return [Boolean] if valid return true, otherwise false.
|
586
|
+
def valid_pushdata_length?(chunk)
|
587
|
+
buf = StringIO.new(chunk)
|
588
|
+
opcode = buf.read(1).ord
|
589
|
+
offset = 1
|
590
|
+
len = case opcode
|
591
|
+
when OP_PUSHDATA1
|
592
|
+
offset += 1
|
593
|
+
buf.read(1).unpack1('C')
|
594
|
+
when OP_PUSHDATA2
|
595
|
+
offset += 2
|
596
|
+
buf.read(2).unpack1('v')
|
597
|
+
when OP_PUSHDATA4
|
598
|
+
offset += 4
|
599
|
+
buf.read(4).unpack1('V')
|
600
|
+
else
|
601
|
+
opcode
|
602
|
+
end
|
603
|
+
chunk.bytesize == len + offset
|
604
|
+
end
|
605
|
+
|
549
606
|
end
|
550
607
|
|
551
608
|
end
|