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/out_point.rb
CHANGED
@@ -3,6 +3,8 @@ module Bitcoin
|
|
3
3
|
# outpoint class
|
4
4
|
class OutPoint
|
5
5
|
|
6
|
+
include Bitcoin::HexConverter
|
7
|
+
|
6
8
|
COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
|
7
9
|
COINBASE_INDEX = 4294967295
|
8
10
|
|
@@ -39,6 +41,11 @@ module Bitcoin
|
|
39
41
|
tx_hash.rhex
|
40
42
|
end
|
41
43
|
|
44
|
+
def to_s
|
45
|
+
return "[#{index}]" unless tx_hash
|
46
|
+
"#{txid}[#{index}]"
|
47
|
+
end
|
48
|
+
|
42
49
|
end
|
43
50
|
|
44
51
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# BIP47 payment code
|
4
|
+
class PaymentCode < ExtKey
|
5
|
+
|
6
|
+
include Bitcoin::HexConverter
|
7
|
+
|
8
|
+
attr_accessor :x_value
|
9
|
+
attr_accessor :sign
|
10
|
+
|
11
|
+
VERSION_BYTE = '47'
|
12
|
+
SUPPORT_VERSIONS = ['01']
|
13
|
+
SUPPORT_SIGNS = ['02', '03']
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@version = '01'
|
17
|
+
@features_bits = '00'
|
18
|
+
@reserve_field = '0' * 26
|
19
|
+
end
|
20
|
+
|
21
|
+
# generate master key from seed.
|
22
|
+
# @params [String] seed a seed data with hex format.
|
23
|
+
def self.generate_master(seed)
|
24
|
+
master_ext_key = super.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)
|
25
|
+
compressed_pubkey = master_ext_key.pub
|
26
|
+
|
27
|
+
payment_code = PaymentCode.new
|
28
|
+
payment_code.depth = master_ext_key.depth
|
29
|
+
payment_code.key = master_ext_key.key
|
30
|
+
payment_code.sign = compressed_pubkey[0..1]
|
31
|
+
payment_code.x_value = compressed_pubkey[2..-1]
|
32
|
+
payment_code.chain_code = master_ext_key.chain_code
|
33
|
+
payment_code
|
34
|
+
end
|
35
|
+
|
36
|
+
# Base58 encoded payment code
|
37
|
+
def to_base58
|
38
|
+
payment_code_with_version_byte = VERSION_BYTE + to_hex
|
39
|
+
Bitcoin::Base58.encode(payment_code_with_version_byte + Bitcoin.calc_checksum(payment_code_with_version_byte))
|
40
|
+
end
|
41
|
+
|
42
|
+
# serialize payment code
|
43
|
+
def to_payload
|
44
|
+
@version.htb << @features_bits.htb << @sign.htb << @x_value.htb << @chain_code << @reserve_field.htb
|
45
|
+
end
|
46
|
+
|
47
|
+
# get notification address
|
48
|
+
def notification_address
|
49
|
+
ext_pubkey.derive(0).addr
|
50
|
+
end
|
51
|
+
|
52
|
+
# decode base58 encoded payment code
|
53
|
+
# @params [String] base58_payment_code base58 encoded payment code
|
54
|
+
def self.from_base58(base58_payment_code)
|
55
|
+
hex = Bitcoin::Base58.decode(base58_payment_code)
|
56
|
+
version = hex[2..3]
|
57
|
+
sign = hex[6..7]
|
58
|
+
public_key = hex[8..71]
|
59
|
+
payment_code = hex[0...-8]
|
60
|
+
|
61
|
+
raise ArgumentError, 'invalid version byte' unless hex[0..1] == VERSION_BYTE
|
62
|
+
raise ArgumentError, 'invalid version' unless PaymentCode.support_version?(version)
|
63
|
+
raise ArgumentError, 'invalid sign' unless PaymentCode.support_sign?(sign)
|
64
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless Bitcoin::Key.new(priv_key: nil, pubkey: sign + public_key).fully_valid_pubkey?
|
65
|
+
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(payment_code) == hex[-8..-1]
|
66
|
+
|
67
|
+
x_value = payment_code[8..71]
|
68
|
+
chain_code_hex = payment_code[72..135]
|
69
|
+
|
70
|
+
payment_code_pubkey = PaymentCode.new
|
71
|
+
payment_code_pubkey.depth = 3
|
72
|
+
payment_code_pubkey.sign = sign
|
73
|
+
payment_code_pubkey.x_value = x_value
|
74
|
+
payment_code_pubkey.chain_code = [chain_code_hex].pack('H*')
|
75
|
+
|
76
|
+
payment_code_pubkey.to_payload
|
77
|
+
end
|
78
|
+
|
79
|
+
# check whether +version+ is supported version bytes.
|
80
|
+
def self.support_version?(version)
|
81
|
+
SUPPORT_VERSIONS.include?(version)
|
82
|
+
end
|
83
|
+
|
84
|
+
# check whether +sign+ is supported version bytes.
|
85
|
+
def self.support_sign?(sign)
|
86
|
+
SUPPORT_SIGNS.include?(sign)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
@@ -12,7 +12,7 @@ module Bitcoin
|
|
12
12
|
pubkey = pubkey.encoding == Encoding::ASCII_8BIT ? pubkey : pubkey.htb
|
13
13
|
raise ArgumentError, 'Size of key was not the expected size for the type BIP32 keypath.' unless [Bitcoin::Key::PUBLIC_KEY_SIZE, Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE].include?(pubkey.bytesize)
|
14
14
|
pubkey = Bitcoin::Key.new(pubkey: pubkey.bth)
|
15
|
-
raise ArgumentError,
|
15
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless pubkey.fully_valid_pubkey?
|
16
16
|
@pubkey = pubkey.pubkey
|
17
17
|
@info = info
|
18
18
|
end
|
data/lib/bitcoin/psbt/input.rb
CHANGED
@@ -36,7 +36,7 @@ module Bitcoin
|
|
36
36
|
found_sep = true
|
37
37
|
break
|
38
38
|
end
|
39
|
-
key_type = buf.read(1).
|
39
|
+
key_type = buf.read(1).unpack1('C')
|
40
40
|
key = buf.read(key_len - 1)
|
41
41
|
value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
|
42
42
|
|
@@ -54,13 +54,13 @@ module Bitcoin
|
|
54
54
|
raise ArgumentError, 'Size of key was not the expected size for the type partial signature pubkey.'
|
55
55
|
end
|
56
56
|
pubkey = Bitcoin::Key.new(pubkey: key.bth)
|
57
|
-
raise ArgumentError,
|
57
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless pubkey.fully_valid_pubkey?
|
58
58
|
raise ArgumentError, 'Duplicate Key, input partial signature for pubkey already provided.' if input.partial_sigs[pubkey.pubkey]
|
59
59
|
input.partial_sigs[pubkey.pubkey] = value
|
60
60
|
when PSBT_IN_TYPES[:sighash]
|
61
61
|
raise ArgumentError, 'Invalid input sighash type typed key.' unless key_len == 1
|
62
62
|
raise ArgumentError 'Duplicate Key, input sighash type already provided.' if input.sighash_type
|
63
|
-
input.sighash_type = value.
|
63
|
+
input.sighash_type = value.unpack1('I')
|
64
64
|
when PSBT_IN_TYPES[:redeem_script]
|
65
65
|
raise ArgumentError, 'Invalid redeemscript typed key.' unless key_len == 1
|
66
66
|
raise ArgumentError, 'Duplicate Key, input redeemScript already provided.' if input.redeem_script
|
@@ -93,7 +93,8 @@ module Bitcoin
|
|
93
93
|
|
94
94
|
def to_payload
|
95
95
|
payload = ''
|
96
|
-
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value:
|
96
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value:
|
97
|
+
(witness_utxo && valid_witness_input?) ? non_witness_utxo.serialize_old_format : non_witness_utxo.to_payload) if non_witness_utxo
|
97
98
|
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_utxo], value: witness_utxo.to_payload) if witness_utxo
|
98
99
|
if final_script_sig.nil? && final_script_witness.nil?
|
99
100
|
payload << partial_sigs.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:partial_sig], key: k.htb, value: v)}.join
|
@@ -109,15 +110,6 @@ module Bitcoin
|
|
109
110
|
payload
|
110
111
|
end
|
111
112
|
|
112
|
-
# Sanity check
|
113
|
-
# @return [Boolean]
|
114
|
-
def sane?
|
115
|
-
return false if non_witness_utxo && witness_utxo
|
116
|
-
return false if witness_script && witness_utxo.nil?
|
117
|
-
return false if final_script_witness && witness_utxo.nil?
|
118
|
-
true
|
119
|
-
end
|
120
|
-
|
121
113
|
# Check whether input's scriptPubkey is correct witness.
|
122
114
|
# @return [Boolean]
|
123
115
|
def valid_witness_input?
|
@@ -141,7 +133,6 @@ module Bitcoin
|
|
141
133
|
# @param [Bitcoin::TxOut] utxo utxo object which input refers.
|
142
134
|
# @return [Boolean]
|
143
135
|
def ready_to_sign?(utxo)
|
144
|
-
return false unless sane?
|
145
136
|
return valid_witness_input? if witness_utxo
|
146
137
|
valid_non_witness_input?(utxo) # non_witness_utxo
|
147
138
|
end
|
@@ -177,8 +168,8 @@ module Bitcoin
|
|
177
168
|
combined.witness_script = witness_script
|
178
169
|
combined.sighash_type = sighash_type
|
179
170
|
sigs = Hash[partial_sigs.merge(psbi.partial_sigs)]
|
180
|
-
redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script
|
181
|
-
witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script
|
171
|
+
redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script&.multisig?
|
172
|
+
witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script&.multisig?
|
182
173
|
combined.hd_key_paths = hd_key_paths.merge(psbi.hd_key_paths)
|
183
174
|
combined
|
184
175
|
end
|
@@ -190,7 +181,7 @@ module Bitcoin
|
|
190
181
|
if non_witness_utxo
|
191
182
|
self.final_script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 if redeem_script.multisig?
|
192
183
|
partial_sigs.values.each {|sig|final_script_sig << sig}
|
193
|
-
final_script_sig << redeem_script.
|
184
|
+
final_script_sig << redeem_script.to_hex
|
194
185
|
self.partial_sigs = {}
|
195
186
|
self.hd_key_paths = {}
|
196
187
|
self.redeem_script = nil
|
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
|
|
@@ -78,13 +81,13 @@ module Bitcoin
|
|
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}",
|
@@ -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
|
|
@@ -109,25 +110,28 @@ module Bitcoin
|
|
109
110
|
if opcode.pushdata?
|
110
111
|
pushcode = opcode.ord
|
111
112
|
packed_size = nil
|
113
|
+
if buf.eof?
|
114
|
+
s.chunks << opcode
|
115
|
+
return s
|
116
|
+
end
|
112
117
|
len = case pushcode
|
113
118
|
when OP_PUSHDATA1
|
114
119
|
packed_size = buf.read(1)
|
115
|
-
packed_size.
|
120
|
+
packed_size.unpack1('C')
|
116
121
|
when OP_PUSHDATA2
|
117
122
|
packed_size = buf.read(2)
|
118
|
-
packed_size.
|
123
|
+
packed_size.unpack1('v')
|
119
124
|
when OP_PUSHDATA4
|
120
125
|
packed_size = buf.read(4)
|
121
|
-
packed_size.
|
126
|
+
packed_size.unpack1('V')
|
122
127
|
else
|
123
|
-
pushcode
|
128
|
+
pushcode < OP_PUSHDATA1 ? pushcode : 0
|
124
129
|
end
|
125
|
-
if
|
126
|
-
s.chunks << [len].pack('C')
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
130
|
+
if buf.eof?
|
131
|
+
s.chunks << [len].pack('C')
|
132
|
+
else buf.eof?
|
133
|
+
chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
|
134
|
+
s.chunks << chunk
|
131
135
|
end
|
132
136
|
else
|
133
137
|
if Opcodes.defined?(opcode.ord)
|
@@ -140,15 +144,21 @@ module Bitcoin
|
|
140
144
|
s
|
141
145
|
end
|
142
146
|
|
143
|
-
|
144
|
-
|
147
|
+
# Output script payload.
|
148
|
+
# @param [Boolean] length_prefixed Flag whether the length of the pyrode should be given at the beginning.(default: false)
|
149
|
+
# @return [String] payload
|
150
|
+
def to_payload(length_prefixed = false)
|
151
|
+
p = chunks.join
|
152
|
+
length_prefixed ? (Bitcoin.pack_var_int(p.length) << p) : p
|
145
153
|
end
|
146
154
|
|
147
155
|
def empty?
|
148
156
|
chunks.size == 0
|
149
157
|
end
|
150
158
|
|
159
|
+
# @deprecated
|
151
160
|
def addresses
|
161
|
+
puts "WARNING: Bitcoin::Script#addresses is deprecated. Use Bitcoin::Script#to_addr instead."
|
152
162
|
return [p2pkh_addr] if p2pkh?
|
153
163
|
return [p2sh_addr] if p2sh?
|
154
164
|
return [bech32_addr] if witness_program?
|
@@ -156,6 +166,15 @@ module Bitcoin
|
|
156
166
|
[]
|
157
167
|
end
|
158
168
|
|
169
|
+
# convert to address
|
170
|
+
# @return [String] if script type is p2pkh or p2sh or witness program, return address, otherwise nil.
|
171
|
+
def to_addr
|
172
|
+
return p2pkh_addr if p2pkh?
|
173
|
+
return p2sh_addr if p2sh?
|
174
|
+
return bech32_addr if witness_program?
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
|
159
178
|
# check whether standard script.
|
160
179
|
def standard?
|
161
180
|
p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
|
@@ -230,7 +249,7 @@ module Bitcoin
|
|
230
249
|
return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
|
231
250
|
return false unless chunks[1].pushdata?
|
232
251
|
|
233
|
-
if size == (chunks[1][0].
|
252
|
+
if size == (chunks[1][0].unpack1('C') + 2)
|
234
253
|
program_size = chunks[1].pushed_data.bytesize
|
235
254
|
return program_size >= 2 && program_size <= 40
|
236
255
|
end
|
@@ -324,16 +343,21 @@ module Bitcoin
|
|
324
343
|
when Integer
|
325
344
|
opcode_to_name(c)
|
326
345
|
when String
|
346
|
+
return c if c.empty?
|
327
347
|
if c.pushdata?
|
328
348
|
v = Opcodes.opcode_to_small_int(c.ord)
|
329
349
|
if v
|
330
350
|
v
|
331
351
|
else
|
332
352
|
data = c.pushed_data
|
333
|
-
if data
|
334
|
-
|
353
|
+
if data
|
354
|
+
if data.bytesize <= 4
|
355
|
+
Script.decode_number(data.bth) # for scriptnum
|
356
|
+
else
|
357
|
+
data.bth
|
358
|
+
end
|
335
359
|
else
|
336
|
-
|
360
|
+
c.bth
|
337
361
|
end
|
338
362
|
end
|
339
363
|
else
|
@@ -351,7 +375,7 @@ module Bitcoin
|
|
351
375
|
|
352
376
|
# generate hash160 hash for payload
|
353
377
|
def to_hash160
|
354
|
-
Bitcoin.hash160(
|
378
|
+
Bitcoin.hash160(to_hex)
|
355
379
|
end
|
356
380
|
|
357
381
|
# script size
|
@@ -380,8 +404,8 @@ module Bitcoin
|
|
380
404
|
hex = '0' + hex unless (hex.length % 2).zero?
|
381
405
|
v = hex.htb.reverse # change endian
|
382
406
|
|
383
|
-
v = v << (negative ? 0x80 : 0x00) unless (v[-1].
|
384
|
-
v[-1] = [v[-1].
|
407
|
+
v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack1('C') & 0x80) == 0
|
408
|
+
v[-1] = [v[-1].unpack1('C') | 0x80].pack('C') if negative
|
385
409
|
v.bth
|
386
410
|
end
|
387
411
|
|
@@ -389,7 +413,7 @@ module Bitcoin
|
|
389
413
|
def self.decode_number(s)
|
390
414
|
v = s.htb.reverse
|
391
415
|
return 0 if v.length.zero?
|
392
|
-
mbs = v[0].
|
416
|
+
mbs = v[0].unpack1('C')
|
393
417
|
v[0] = [mbs - 0x80].pack('C') unless (mbs & 0x80) == 0
|
394
418
|
result = v.bth.to_i(16)
|
395
419
|
result = -result unless (mbs & 0x80) == 0
|
@@ -488,7 +512,7 @@ module Bitcoin
|
|
488
512
|
end
|
489
513
|
|
490
514
|
def to_h
|
491
|
-
h = {asm: to_s, hex:
|
515
|
+
h = {asm: to_s, hex: to_hex, type: type}
|
492
516
|
addrs = addresses
|
493
517
|
unless addrs.empty?
|
494
518
|
h[:req_sigs] = multisig? ? Bitcoin::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
|
@@ -504,12 +528,6 @@ module Bitcoin
|
|
504
528
|
(size > 0 && op_return?) || size > Bitcoin::MAX_SCRIPT_SIZE
|
505
529
|
end
|
506
530
|
|
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
531
|
private
|
514
532
|
|
515
533
|
# generate p2pkh address. if script dose not p2pkh, return nil.
|
@@ -542,10 +560,32 @@ module Bitcoin
|
|
542
560
|
def bech32_addr
|
543
561
|
segwit_addr = Bech32::SegwitAddr.new
|
544
562
|
segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
|
545
|
-
segwit_addr.script_pubkey =
|
563
|
+
segwit_addr.script_pubkey = to_hex
|
546
564
|
segwit_addr.addr
|
547
565
|
end
|
548
566
|
|
567
|
+
# Check whether push data length is valid.
|
568
|
+
# @return [Boolean] if valid return true, otherwise false.
|
569
|
+
def valid_pushdata_length?(chunk)
|
570
|
+
buf = StringIO.new(chunk)
|
571
|
+
opcode = buf.read(1).ord
|
572
|
+
offset = 1
|
573
|
+
len = case opcode
|
574
|
+
when OP_PUSHDATA1
|
575
|
+
offset += 1
|
576
|
+
buf.read(1).unpack1('C')
|
577
|
+
when OP_PUSHDATA2
|
578
|
+
offset += 2
|
579
|
+
buf.read(2).unpack1('v')
|
580
|
+
when OP_PUSHDATA4
|
581
|
+
offset += 4
|
582
|
+
buf.read(4).unpack1('V')
|
583
|
+
else
|
584
|
+
opcode
|
585
|
+
end
|
586
|
+
chunk.bytesize == len + offset
|
587
|
+
end
|
588
|
+
|
549
589
|
end
|
550
590
|
|
551
591
|
end
|