bitcoinrb 0.3.2 → 0.8.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 +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
|