bitcoinrb 1.2.1 → 1.4.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 +1 -1
- data/.ruby-version +1 -1
- data/README.md +2 -0
- data/bitcoinrb.gemspec +3 -4
- data/lib/bitcoin/constants.rb +2 -3
- data/lib/bitcoin/ext_key.rb +6 -4
- data/lib/bitcoin/key.rb +8 -1
- data/lib/bitcoin/message_sign.rb +90 -15
- data/lib/bitcoin/node/cli.rb +6 -0
- data/lib/bitcoin/payment_code.rb +1 -1
- data/lib/bitcoin/psbt/input.rb +26 -0
- data/lib/bitcoin/psbt/output.rb +12 -1
- data/lib/bitcoin/psbt/proprietary.rb +4 -0
- data/lib/bitcoin/psbt/tx.rb +12 -0
- data/lib/bitcoin/script/script.rb +20 -6
- data/lib/bitcoin/secp256k1/ruby.rb +8 -5
- data/lib/bitcoin/taproot.rb +3 -1
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin.rb +0 -1
- metadata +14 -35
- data/lib/bitcoin/payments/output.pb.rb +0 -20
- data/lib/bitcoin/payments/payment.pb.rb +0 -26
- data/lib/bitcoin/payments/payment_ack.pb.rb +0 -17
- data/lib/bitcoin/payments/payment_details.pb.rb +0 -24
- data/lib/bitcoin/payments/payment_request.pb.rb +0 -79
- data/lib/bitcoin/payments/x509_certificates.pb.rb +0 -18
- data/lib/bitcoin/payments.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 544382f715d87eb79e185e310c827b2349c0af75c1bd89887a653510e226cd1f
|
4
|
+
data.tar.gz: c807d0965da7c06f71b248d5a543ccfff2375d702a6ab4db70698c4be245720d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22ea36535fb045c35d28809f87c60f4451e2d37801e7a231772068c268d6e58cdeb086456ffc649091a5a28a82815af77f6d35ef286a7a101e53fa3f97e55a9a
|
7
|
+
data.tar.gz: 7ca7b637b77bdde57d2af3faf056184397b1d87fd98dd5c46933dfb40e716d5bf8ef56890691771b97d8e66dde1c9684cb1d03e9f74f99d4f8b5bbaeb6a37e2c
|
data/.github/workflows/ruby.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-3.
|
1
|
+
ruby-3.2.0
|
data/README.md
CHANGED
@@ -67,6 +67,8 @@ And then add to your .rb file:
|
|
67
67
|
|
68
68
|
## Usage
|
69
69
|
|
70
|
+
Examples can be found on the [wiki](https://github.com/chaintope/bitcoinrb/wiki).
|
71
|
+
|
70
72
|
### Chain selection
|
71
73
|
|
72
74
|
The parameters of the blockchain are managed by `Bitcoin::ChainParams`. Switch chain parameters as follows:
|
data/bitcoinrb.gemspec
CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_runtime_dependency '
|
23
|
+
spec.add_runtime_dependency 'ecdsa_ext', '~> 0.5.0'
|
24
24
|
spec.add_runtime_dependency 'eventmachine'
|
25
|
-
spec.add_runtime_dependency 'murmurhash3'
|
25
|
+
spec.add_runtime_dependency 'murmurhash3', '~> 0.1.7'
|
26
26
|
spec.add_runtime_dependency 'bech32', '>= 1.3.0'
|
27
27
|
spec.add_runtime_dependency 'daemon-spawn'
|
28
28
|
spec.add_runtime_dependency 'thor'
|
@@ -31,9 +31,8 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_runtime_dependency 'eventmachine_httpserver'
|
32
32
|
spec.add_runtime_dependency 'iniparse'
|
33
33
|
spec.add_runtime_dependency 'siphash'
|
34
|
-
spec.add_runtime_dependency 'protobuf', '3.8.5'
|
35
34
|
spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
|
36
|
-
spec.add_runtime_dependency 'bip-schnorr', '>= 0.
|
35
|
+
spec.add_runtime_dependency 'bip-schnorr', '>= 0.5.0'
|
37
36
|
spec.add_runtime_dependency 'base32', '>= 0.3.4'
|
38
37
|
|
39
38
|
# for options
|
data/lib/bitcoin/constants.rb
CHANGED
@@ -57,9 +57,8 @@ module Bitcoin
|
|
57
57
|
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1 << 16) # Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
|
58
58
|
SCRIPT_VERIFY_TAPROOT = (1 << 17) # Taproot/Tapscript validation (BIPs 341 & 342)
|
59
59
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1 << 18) # Making unknown Taproot leaf versions non-standard
|
60
|
-
|
61
|
-
|
62
|
-
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1 << 21) # Making unknown public key versions (in BIP 342 scripts) non-standard
|
60
|
+
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1 << 19) # Making unknown OP_SUCCESS non-standard
|
61
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1 << 20) # Making unknown public key versions (in BIP 342 scripts) non-standard
|
63
62
|
|
64
63
|
MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
|
65
64
|
|
data/lib/bitcoin/ext_key.rb
CHANGED
@@ -27,7 +27,8 @@ module Bitcoin
|
|
27
27
|
l = Bitcoin.hmac_sha512('Bitcoin seed', seed.htb)
|
28
28
|
left = l[0..31].bth.to_i(16)
|
29
29
|
raise 'invalid key' if left >= CURVE_ORDER || left == 0
|
30
|
-
|
30
|
+
l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
|
31
|
+
ext_key.key = Bitcoin::Key.new(priv_key: l_priv.bth, key_type: Bitcoin::Key::TYPES[:compressed])
|
31
32
|
ext_key.chain_code = l[32..-1]
|
32
33
|
ext_key
|
33
34
|
end
|
@@ -110,8 +111,8 @@ module Bitcoin
|
|
110
111
|
raise 'invalid key' if left >= CURVE_ORDER
|
111
112
|
child_priv = (left + key.priv_key.to_i(16)) % CURVE_ORDER
|
112
113
|
raise 'invalid key ' if child_priv >= CURVE_ORDER
|
113
|
-
|
114
|
-
|
114
|
+
child_priv = ECDSA::Format::IntegerOctetString.encode(child_priv, 32)
|
115
|
+
new_key.key = Bitcoin::Key.new(priv_key: child_priv.bth, key_type: key_type)
|
115
116
|
new_key.chain_code = l[32..-1]
|
116
117
|
new_key.ver = version
|
117
118
|
new_key
|
@@ -280,7 +281,8 @@ module Bitcoin
|
|
280
281
|
l = Bitcoin.hmac_sha512(chain_code, data)
|
281
282
|
left = l[0..31].bth.to_i(16)
|
282
283
|
raise 'invalid key' if left >= CURVE_ORDER
|
283
|
-
|
284
|
+
l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
|
285
|
+
p1 = Bitcoin::Key.new(priv_key: l_priv.bth, key_type: Bitcoin::Key::TYPES[:uncompressed]).to_point
|
284
286
|
p2 = Bitcoin::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
285
287
|
new_key.pubkey = (p1 + p2).to_hex
|
286
288
|
new_key.chain_code = l[32..-1]
|
data/lib/bitcoin/key.rb
CHANGED
@@ -39,6 +39,7 @@ module Bitcoin
|
|
39
39
|
@secp256k1_module = Bitcoin.secp_impl
|
40
40
|
@priv_key = priv_key
|
41
41
|
if @priv_key
|
42
|
+
raise ArgumentError, 'Private key must be 32 bytes.' unless priv_key.htb.bytesize == 32
|
42
43
|
raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
|
43
44
|
end
|
44
45
|
if pubkey
|
@@ -51,7 +52,7 @@ module Bitcoin
|
|
51
52
|
|
52
53
|
# generate key pair
|
53
54
|
def self.generate(key_type = TYPES[:compressed])
|
54
|
-
priv_key, pubkey = Bitcoin.secp_impl.generate_key_pair
|
55
|
+
priv_key, pubkey = Bitcoin.secp_impl.generate_key_pair(compressed: key_type != TYPES[:uncompressed])
|
55
56
|
new(priv_key: priv_key, pubkey: pubkey, key_type: key_type)
|
56
57
|
end
|
57
58
|
|
@@ -260,6 +261,12 @@ module Bitcoin
|
|
260
261
|
pubkey[2..65]
|
261
262
|
end
|
262
263
|
|
264
|
+
# Convert this key to decompress key.
|
265
|
+
# @return [String] decompress public key with hex format.
|
266
|
+
def decompress_pubkey
|
267
|
+
pubkey.htb.bytesize == PUBLIC_KEY_SIZE ? pubkey : to_point.to_hex(false)
|
268
|
+
end
|
269
|
+
|
263
270
|
# check +pubkey+ (hex) is compress or uncompress pubkey.
|
264
271
|
def self.compress_or_uncompress_pubkey?(pubkey)
|
265
272
|
p = pubkey.htb
|
data/lib/bitcoin/message_sign.rb
CHANGED
@@ -6,14 +6,42 @@ module Bitcoin
|
|
6
6
|
|
7
7
|
module_function
|
8
8
|
|
9
|
+
FORMAT_LEGACY = :legacy
|
10
|
+
FORMAT_SIMPLE = :simple
|
11
|
+
FORMAT_FULL = :full
|
12
|
+
|
9
13
|
# Sign a message.
|
10
|
-
# @param [Bitcoin::Key] key Private key to sign
|
11
|
-
# @param [String] message The message to
|
14
|
+
# @param [Bitcoin::Key] key Private key to sign.
|
15
|
+
# @param [String] message The message to be signed.
|
16
|
+
# @param [String] address An address of the key used for signing (required for full or simple format).
|
17
|
+
# @param [String] format Format of signature data. Default is +FORMAT_LEGACY+.
|
18
|
+
# @param [String] prefix (Optional) Prefix used in legacy format.
|
12
19
|
# @return [String] Signature, base64 encoded.
|
13
|
-
def sign_message(key, message, prefix: Bitcoin.chain_params.message_magic)
|
14
|
-
|
15
|
-
|
16
|
-
|
20
|
+
def sign_message(key, message, prefix: Bitcoin.chain_params.message_magic, format: FORMAT_LEGACY, address: nil)
|
21
|
+
validate_format!(format)
|
22
|
+
digest = message_hash(message, prefix: prefix, legacy: format == FORMAT_LEGACY)
|
23
|
+
sig = case format
|
24
|
+
when FORMAT_LEGACY
|
25
|
+
key.sign_compact(digest)
|
26
|
+
else
|
27
|
+
validate_address!(address)
|
28
|
+
addr = Bitcoin::Script.parse_from_addr(address)
|
29
|
+
sig_ver, algo = if addr.p2wpkh?
|
30
|
+
[:witness_v0, :ecdsa]
|
31
|
+
elsif addr.p2tr?
|
32
|
+
[:taproot, :schnorr]
|
33
|
+
else
|
34
|
+
raise ArgumentError "#{address} dose not supported."
|
35
|
+
end
|
36
|
+
tx = to_sign_tx(digest, address)
|
37
|
+
prev_out = Bitcoin::TxOut.new(script_pubkey: addr)
|
38
|
+
sighash = tx.sighash_for_input(0, addr, sig_version: sig_ver, amount: 0, prevouts: [prev_out])
|
39
|
+
sig = key.sign(sighash, algo: algo) + [Bitcoin::SIGHASH_TYPE[:all]].pack('C')
|
40
|
+
tx.in[0].script_witness.stack << sig
|
41
|
+
tx.in[0].script_witness.stack << key.pubkey.htb
|
42
|
+
format == FORMAT_SIMPLE ? tx.in[0].script_witness.to_payload : tx.to_payload
|
43
|
+
end
|
44
|
+
Base64.strict_encode64(sig)
|
17
45
|
end
|
18
46
|
|
19
47
|
# Verify a signed message.
|
@@ -23,25 +51,72 @@ module Bitcoin
|
|
23
51
|
# @return [Boolean] Verification result.
|
24
52
|
def verify_message(address, signature, message, prefix: Bitcoin.chain_params.message_magic)
|
25
53
|
validate_address!(address)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
54
|
+
begin
|
55
|
+
sig = Base64.strict_decode64(signature)
|
56
|
+
rescue ArgumentError
|
57
|
+
raise ArgumentError, 'Invalid signature'
|
58
|
+
end
|
59
|
+
begin
|
60
|
+
# Legacy verification
|
61
|
+
pubkey = Bitcoin::Key.recover_compact(message_hash(message, prefix: prefix, legacy: true), sig)
|
62
|
+
return false unless pubkey
|
63
|
+
pubkey.to_p2pkh == address
|
64
|
+
rescue ArgumentError
|
65
|
+
# BIP322 verification
|
66
|
+
tx = to_sign_tx(message_hash(message, prefix: prefix, legacy: false), address)
|
67
|
+
tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig)
|
68
|
+
script_pubkey = Bitcoin::Script.parse_from_addr(address)
|
69
|
+
tx_out = Bitcoin::TxOut.new(script_pubkey: script_pubkey)
|
70
|
+
interpreter = Bitcoin::ScriptInterpreter.new(checker: Bitcoin::TxChecker.new(tx: tx, input_index: 0, prevouts: [tx_out]))
|
71
|
+
interpreter.verify_script(Bitcoin::Script.new, script_pubkey, tx.in[0].script_witness)
|
72
|
+
end
|
32
73
|
end
|
33
74
|
|
34
75
|
# Hashes a message for signing and verification.
|
35
|
-
def message_hash(message, prefix: Bitcoin.chain_params.message_magic)
|
36
|
-
|
76
|
+
def message_hash(message, prefix: Bitcoin.chain_params.message_magic, legacy: true)
|
77
|
+
if legacy
|
78
|
+
Bitcoin.double_sha256(Bitcoin.pack_var_string(prefix) << Bitcoin.pack_var_string(message))
|
79
|
+
else
|
80
|
+
Bitcoin.tagged_hash('BIP0322-signed-message', message)
|
81
|
+
end
|
37
82
|
end
|
38
83
|
|
39
84
|
def validate_address!(address)
|
40
85
|
raise ArgumentError, 'Invalid address' unless Bitcoin.valid_address?(address)
|
41
86
|
script = Bitcoin::Script.parse_from_addr(address)
|
42
|
-
raise ArgumentError, '
|
87
|
+
raise ArgumentError, 'This address unsupported' if script.p2sh? || script.p2wsh?
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_format!(format)
|
91
|
+
unless [FORMAT_LEGACY, FORMAT_FULL, FORMAT_SIMPLE].include?(format)
|
92
|
+
raise ArgumentError "Invalid format specified."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_spend_tx(digest, addr)
|
97
|
+
validate_address!(addr)
|
98
|
+
message_challenge = Bitcoin::Script.parse_from_addr(addr)
|
99
|
+
tx = Bitcoin::Tx.new
|
100
|
+
tx.version = 0
|
101
|
+
tx.lock_time = 0
|
102
|
+
prev_out = Bitcoin::OutPoint.create_coinbase_outpoint
|
103
|
+
script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 << digest
|
104
|
+
tx.in << Bitcoin::TxIn.new(out_point: prev_out, sequence: 0, script_sig: script_sig)
|
105
|
+
tx.out << Bitcoin::TxOut.new(script_pubkey: message_challenge)
|
106
|
+
tx
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_sign_tx(digest, addr)
|
110
|
+
tx = Bitcoin::Tx.new
|
111
|
+
tx.version = 0
|
112
|
+
tx.lock_time = 0
|
113
|
+
prev_out = Bitcoin::OutPoint.from_txid(to_spend_tx(digest, addr).txid, 0)
|
114
|
+
tx.in << Bitcoin::TxIn.new(out_point: prev_out, sequence: 0)
|
115
|
+
tx.out << Bitcoin::TxOut.new(script_pubkey: Bitcoin::Script.new << Bitcoin::Opcodes::OP_RETURN)
|
116
|
+
tx
|
43
117
|
end
|
44
118
|
|
45
119
|
private_class_method :validate_address!
|
120
|
+
private_class_method :validate_format!
|
46
121
|
end
|
47
122
|
end
|
data/lib/bitcoin/node/cli.rb
CHANGED
@@ -9,6 +9,12 @@ module Bitcoin
|
|
9
9
|
|
10
10
|
class_option :network, aliases: '-n', default: :mainnet
|
11
11
|
|
12
|
+
desc 'decodepsbt <base64 psbt string>', "Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction."
|
13
|
+
def decodepsbt(base64)
|
14
|
+
psbt = Bitcoin::PSBT::Tx.parse_from_base64(base64)
|
15
|
+
puts JSON.pretty_generate(psbt.to_h)
|
16
|
+
end
|
17
|
+
|
12
18
|
desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
|
13
19
|
def getblockchaininfo
|
14
20
|
request('getblockchaininfo')
|
data/lib/bitcoin/payment_code.rb
CHANGED
@@ -61,7 +61,7 @@ module Bitcoin
|
|
61
61
|
raise ArgumentError, 'invalid version byte' unless hex[0..1] == VERSION_BYTE
|
62
62
|
raise ArgumentError, 'invalid version' unless PaymentCode.support_version?(version)
|
63
63
|
raise ArgumentError, 'invalid sign' unless PaymentCode.support_sign?(sign)
|
64
|
-
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless Bitcoin::Key.new(
|
64
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless Bitcoin::Key.new(pubkey: sign + public_key).fully_valid_pubkey?
|
65
65
|
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(payment_code) == hex[-8..-1]
|
66
66
|
|
67
67
|
x_value = payment_code[8..71]
|
data/lib/bitcoin/psbt/input.rb
CHANGED
@@ -283,6 +283,32 @@ module Bitcoin
|
|
283
283
|
self
|
284
284
|
end
|
285
285
|
|
286
|
+
def to_h
|
287
|
+
h = {}
|
288
|
+
h[:non_witness_utxo] = non_witness_utxo.to_h if non_witness_utxo
|
289
|
+
h[:witness_utxo] = witness_utxo.to_h if witness_utxo
|
290
|
+
h[:redeem_script] = redeem_script.to_h if redeem_script
|
291
|
+
h[:witness_script] = witness_script.to_h if redeem_script
|
292
|
+
h[:final_script_sig] = final_script_sig.to_h if final_script_sig
|
293
|
+
h[:final_script_witness] = final_script_witness.to_h if final_script_witness
|
294
|
+
h[:bip32_derivs] = hd_key_paths.values.map(&:to_h) unless hd_key_paths.empty?
|
295
|
+
h[:partial_signatures] = partial_sigs.map {|k, v| {"#{k}": v.bth}} unless partial_sigs.empty?
|
296
|
+
h[:sighash_type] = sighash_type if sighash_type
|
297
|
+
h[:ripemd160_preimages] = ripemd160_preimages.map {|k, v| {"#{k}": v}} unless ripemd160_preimages.empty?
|
298
|
+
h[:sha256_preimages] = sha256_preimages.map {|k, v| {"#{k}": v}} unless sha256_preimages.empty?
|
299
|
+
h[:hash160_preimages] = hash160_preimages.map {|k, v| {"#{k}": v}} unless hash160_preimages.empty?
|
300
|
+
h[:hash256_preimages] = hash256_preimages.map {|k, v| {"#{k}": v}} unless hash256_preimages.empty?
|
301
|
+
h[:proprietary] = proprietaries.map(&:to_h) unless proprietaries.empty?
|
302
|
+
h[:tap_key_sig] = tap_key_sig if tap_key_sig
|
303
|
+
h[:tap_script_sig] = tap_script_sigs.map {|k, v| {"#{k}": v}} unless tap_script_sigs.empty?
|
304
|
+
h[:tap_leaf_script] = tap_leaf_scripts.map {|k, v| {"#{k}": v}} unless tap_leaf_scripts.empty?
|
305
|
+
h[:tap_bip32_derivs] = tap_bip32_derivations.map{|k, v| {"#{k}": v}} unless tap_bip32_derivations.empty?
|
306
|
+
h[:tap_internal_key] = tap_internal_key if tap_internal_key
|
307
|
+
h[:tap_merkle_root] = tap_merkle_root if tap_merkle_root
|
308
|
+
h[:unknown] = unknowns.map {|k, v| {"#{k}": v.bth}} unless unknowns.empty?
|
309
|
+
h
|
310
|
+
end
|
311
|
+
|
286
312
|
end
|
287
313
|
|
288
314
|
end
|
data/lib/bitcoin/psbt/output.rb
CHANGED
@@ -100,7 +100,18 @@ module Bitcoin
|
|
100
100
|
combined
|
101
101
|
end
|
102
102
|
|
103
|
+
def to_h
|
104
|
+
h = {}
|
105
|
+
h[:redeem_script] = redeem_script.bth if redeem_script
|
106
|
+
h[:witness_script] = witness_script.bth if witness_script
|
107
|
+
h[:bip32_derivs] = hd_key_paths.values.map(&:to_h) unless hd_key_paths.empty?
|
108
|
+
h[:proprietary] = proprietaries.map(&:to_h) unless proprietaries.empty?
|
109
|
+
h[:tap_internal_key] = tap_internal_key if tap_internal_key
|
110
|
+
h[:tap_tree] = tap_tree if tap_tree
|
111
|
+
h[:tap_bip32_derivs] = tap_bip32_derivations.map{|k, v| {"#{k}": v}} unless tap_bip32_derivations.empty?
|
112
|
+
h[:unknown] = unknowns.map {|k, v| {"#{k}": v.bth}} unless unknowns.empty?
|
113
|
+
h
|
114
|
+
end
|
103
115
|
end
|
104
|
-
|
105
116
|
end
|
106
117
|
end
|
data/lib/bitcoin/psbt/tx.rb
CHANGED
@@ -177,6 +177,18 @@ module Bitcoin
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
+
def to_h
|
181
|
+
{
|
182
|
+
tx: tx.to_h,
|
183
|
+
global_xpubs: xpubs.map(&:to_h),
|
184
|
+
psbt_version: version,
|
185
|
+
proprietary: proprietaries.map(&:to_h),
|
186
|
+
unknown: unknowns.map {|k, v| {"#{k}": v}},
|
187
|
+
inputs: inputs.map(&:to_h),
|
188
|
+
outputs: outputs.map(&:to_h)
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
180
192
|
# update input key-value maps.
|
181
193
|
# @param [Bitcoin::Tx] prev_tx previous tx reference by input.
|
182
194
|
# @param [Bitcoin::Script] redeem_script redeem script to set input.
|
@@ -233,11 +233,28 @@ module Bitcoin
|
|
233
233
|
OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
|
234
234
|
end
|
235
235
|
|
236
|
+
# Check whether this script is a P2PK format script.
|
237
|
+
# @return [Boolean] if P2PK return true, otherwise false
|
238
|
+
def p2pk?
|
239
|
+
return false unless chunks.size == 2
|
240
|
+
return false unless chunks[0].pushdata?
|
241
|
+
key_type = chunks[0].pushed_data[0].ord
|
242
|
+
case key_type
|
243
|
+
when 0x02, 0x03
|
244
|
+
return false unless chunks[0].pushed_data.bytesize == 33
|
245
|
+
when 0x04
|
246
|
+
return false unless chunks[0].pushed_data.bytesize == 65
|
247
|
+
else
|
248
|
+
return false
|
249
|
+
end
|
250
|
+
chunks[1].ord == OP_CHECKSIG
|
251
|
+
end
|
252
|
+
|
236
253
|
def multisig?
|
237
254
|
return false if chunks.size < 4 || chunks.last.ord != OP_CHECKMULTISIG
|
238
255
|
pubkey_count = Opcodes.opcode_to_small_int(chunks[-2].opcode)
|
239
256
|
sig_count = Opcodes.opcode_to_small_int(chunks[0].opcode)
|
240
|
-
return false
|
257
|
+
return false if pubkey_count.nil? || sig_count.nil?
|
241
258
|
sig_count <= pubkey_count
|
242
259
|
end
|
243
260
|
|
@@ -543,11 +560,8 @@ module Bitcoin
|
|
543
560
|
|
544
561
|
def to_h
|
545
562
|
h = {asm: to_s, hex: to_hex, type: type}
|
546
|
-
|
547
|
-
|
548
|
-
h[:req_sigs] = multisig? ? Bitcoin::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
|
549
|
-
h[:addresses] = addrs
|
550
|
-
end
|
563
|
+
addr = to_addr
|
564
|
+
h[:address] = addr if addr
|
551
565
|
h
|
552
566
|
end
|
553
567
|
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'ecdsa_ext'
|
2
|
+
require 'ecdsa/ext/sign_verify'
|
3
|
+
|
1
4
|
module Bitcoin
|
2
5
|
module Secp256k1
|
3
6
|
|
@@ -10,9 +13,9 @@ module Bitcoin
|
|
10
13
|
# generate ec private key and public key
|
11
14
|
def generate_key_pair(compressed: true)
|
12
15
|
private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
|
13
|
-
public_key = GROUP.generator.
|
16
|
+
public_key = GROUP.generator.to_jacobian * private_key
|
14
17
|
privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
|
15
|
-
pubkey = public_key.to_hex(compressed)
|
18
|
+
pubkey = public_key.to_affine.to_hex(compressed)
|
16
19
|
[privkey.bth, pubkey]
|
17
20
|
end
|
18
21
|
|
@@ -23,8 +26,8 @@ module Bitcoin
|
|
23
26
|
end
|
24
27
|
|
25
28
|
def generate_pubkey(privkey, compressed: true)
|
26
|
-
public_key =
|
27
|
-
public_key.to_hex(compressed)
|
29
|
+
public_key = GROUP.generator.to_jacobian * privkey.to_i(16)
|
30
|
+
public_key.to_affine.to_hex(compressed)
|
28
31
|
end
|
29
32
|
|
30
33
|
# Check whether valid x-only public key or not.
|
@@ -132,7 +135,7 @@ module Bitcoin
|
|
132
135
|
nonce = RFC6979.generate_rfc6979_nonce(privkey + data, extra_entropy)
|
133
136
|
|
134
137
|
# port form ecdsa gem.
|
135
|
-
r_point = GROUP.
|
138
|
+
r_point = (GROUP.generator.to_jacobian * nonce).to_affine
|
136
139
|
|
137
140
|
point_field = ECDSA::PrimeField.new(GROUP.order)
|
138
141
|
r = point_field.mod(r_point.x)
|
data/lib/bitcoin/taproot.rb
CHANGED
@@ -40,7 +40,9 @@ module Bitcoin
|
|
40
40
|
private_key = p.has_even_y? ? internal_private_key.priv_key.to_i(16) :
|
41
41
|
ECDSA::Group::Secp256k1.order - internal_private_key.priv_key.to_i(16)
|
42
42
|
t = tweak(internal_private_key, merkle_root)
|
43
|
-
|
43
|
+
private_key = ECDSA::Format::IntegerOctetString.encode(
|
44
|
+
(t.bti + private_key) % ECDSA::Group::Secp256k1.order, 32)
|
45
|
+
Bitcoin::Key.new(priv_key: private_key.bth)
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
data/lib/bitcoin/version.rb
CHANGED
data/lib/bitcoin.rb
CHANGED
@@ -46,7 +46,6 @@ module Bitcoin
|
|
46
46
|
autoload :RPC, 'bitcoin/rpc'
|
47
47
|
autoload :Wallet, 'bitcoin/wallet'
|
48
48
|
autoload :BloomFilter, 'bitcoin/bloom_filter'
|
49
|
-
autoload :Payments, 'bitcoin/payments'
|
50
49
|
autoload :PSBT, 'bitcoin/psbt'
|
51
50
|
autoload :GCSFilter, 'bitcoin/gcs_filter'
|
52
51
|
autoload :BlockFilter, 'bitcoin/block_filter'
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitcoinrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: ecdsa_ext
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.5.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.5.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: eventmachine
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: murmurhash3
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.1.7
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.1.7
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bech32
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,20 +164,6 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: protobuf
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - '='
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: 3.8.5
|
174
|
-
type: :runtime
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - '='
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: 3.8.5
|
181
167
|
- !ruby/object:Gem::Dependency
|
182
168
|
name: json_pure
|
183
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,14 +184,14 @@ dependencies:
|
|
198
184
|
requirements:
|
199
185
|
- - ">="
|
200
186
|
- !ruby/object:Gem::Version
|
201
|
-
version: 0.
|
187
|
+
version: 0.5.0
|
202
188
|
type: :runtime
|
203
189
|
prerelease: false
|
204
190
|
version_requirements: !ruby/object:Gem::Requirement
|
205
191
|
requirements:
|
206
192
|
- - ">="
|
207
193
|
- !ruby/object:Gem::Version
|
208
|
-
version: 0.
|
194
|
+
version: 0.5.0
|
209
195
|
- !ruby/object:Gem::Dependency
|
210
196
|
name: base32
|
211
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -439,13 +425,6 @@ files:
|
|
439
425
|
- lib/bitcoin/opcodes.rb
|
440
426
|
- lib/bitcoin/out_point.rb
|
441
427
|
- lib/bitcoin/payment_code.rb
|
442
|
-
- lib/bitcoin/payments.rb
|
443
|
-
- lib/bitcoin/payments/output.pb.rb
|
444
|
-
- lib/bitcoin/payments/payment.pb.rb
|
445
|
-
- lib/bitcoin/payments/payment_ack.pb.rb
|
446
|
-
- lib/bitcoin/payments/payment_details.pb.rb
|
447
|
-
- lib/bitcoin/payments/payment_request.pb.rb
|
448
|
-
- lib/bitcoin/payments/x509_certificates.pb.rb
|
449
428
|
- lib/bitcoin/psbt.rb
|
450
429
|
- lib/bitcoin/psbt/hd_key_path.rb
|
451
430
|
- lib/bitcoin/psbt/input.rb
|
@@ -517,7 +496,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
517
496
|
- !ruby/object:Gem::Version
|
518
497
|
version: '0'
|
519
498
|
requirements: []
|
520
|
-
rubygems_version: 3.
|
499
|
+
rubygems_version: 3.4.1
|
521
500
|
signing_key:
|
522
501
|
specification_version: 4
|
523
502
|
summary: The implementation of Bitcoin Protocol for Ruby.
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Bitcoin
|
2
|
-
module Payments
|
3
|
-
|
4
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#Output
|
5
|
-
class Output < Protobuf::Message
|
6
|
-
|
7
|
-
optional :uint64, :amount, 1, {default: 0}
|
8
|
-
|
9
|
-
required :bytes, :script, 2
|
10
|
-
|
11
|
-
# convert to TxOut object.
|
12
|
-
# @return [Bitcoin::TxOut]
|
13
|
-
def to_tx_out
|
14
|
-
Bitcoin::TxOut.new(value: amount, script_pubkey: Bitcoin::Script.parse_from_payload(script))
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Bitcoin
|
2
|
-
module Payments
|
3
|
-
|
4
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#Payment
|
5
|
-
class Payment < Protobuf::Message
|
6
|
-
|
7
|
-
optional :bytes, :merchant_data, 1
|
8
|
-
|
9
|
-
repeated :bytes, :transactions, 2
|
10
|
-
|
11
|
-
repeated Bitcoin::Payments::Output, :refund_to, 3
|
12
|
-
|
13
|
-
optional :string, :memo, 4
|
14
|
-
|
15
|
-
def self.parse_from_payload(payload)
|
16
|
-
decode(payload)
|
17
|
-
end
|
18
|
-
|
19
|
-
def transactions
|
20
|
-
@values[:transactions].map{|raw_tx|Bitcoin::Tx.parse_from_payload(raw_tx, strict: true)}
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Bitcoin
|
2
|
-
module Payments
|
3
|
-
|
4
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#PaymentACK
|
5
|
-
class PaymentACK < Protobuf::Message
|
6
|
-
|
7
|
-
required Bitcoin::Payments::Payment, :payment, 1
|
8
|
-
|
9
|
-
optional :string, :memo, 2
|
10
|
-
|
11
|
-
def self.parse_from_payload(payload)
|
12
|
-
decode(payload)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module Bitcoin
|
2
|
-
module Payments
|
3
|
-
|
4
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#PaymentDetailsPaymentRequest
|
5
|
-
class PaymentDetails < Protobuf::Message
|
6
|
-
|
7
|
-
optional :string, :network, 1, {default: 'main'}
|
8
|
-
|
9
|
-
repeated Bitcoin::Payments::Output, :outputs, 2
|
10
|
-
|
11
|
-
required :uint64, :time, 3
|
12
|
-
|
13
|
-
optional :uint64, :expires, 4
|
14
|
-
|
15
|
-
optional :string, :memo, 5
|
16
|
-
|
17
|
-
optional :string, :payment_url, 6
|
18
|
-
|
19
|
-
optional :bytes, :merchant_data, 7
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
module Bitcoin
|
2
|
-
module Payments
|
3
|
-
|
4
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#PaymentDetailsPaymentRequest
|
5
|
-
class PaymentRequest < Protobuf::Message
|
6
|
-
|
7
|
-
optional :uint32, :payment_details_version, 1, {default: 1}
|
8
|
-
|
9
|
-
optional :string, :pki_type, 2, {default: 'none'}
|
10
|
-
|
11
|
-
optional :bytes, :pki_data, 3
|
12
|
-
|
13
|
-
required :bytes, :serialized_payment_details, 4
|
14
|
-
|
15
|
-
optional :bytes, :signature, 5
|
16
|
-
|
17
|
-
def self.parse_from_payload(payload)
|
18
|
-
self.decode(payload)
|
19
|
-
end
|
20
|
-
|
21
|
-
# verify +pki_data+.
|
22
|
-
# @return [Struct] pki information.
|
23
|
-
def verify_pki_data
|
24
|
-
d = Struct.new(:display_name, :merchant_sign_key, :root_auth, :root_auth_name)
|
25
|
-
d
|
26
|
-
end
|
27
|
-
|
28
|
-
# get payment details
|
29
|
-
# @return [Bitcoin::Payments:PaymentDetails]
|
30
|
-
def details
|
31
|
-
PaymentDetails.decode(serialized_payment_details)
|
32
|
-
end
|
33
|
-
|
34
|
-
# get certificates
|
35
|
-
# @return [Array[OpenSSL::X509::Certificate]]
|
36
|
-
def certs
|
37
|
-
return [] unless has_pki?
|
38
|
-
X509Certificates.decode(pki_data).certs
|
39
|
-
end
|
40
|
-
|
41
|
-
# whether exist +pki_data+.
|
42
|
-
def has_pki?
|
43
|
-
pki_type != 'none'
|
44
|
-
end
|
45
|
-
|
46
|
-
# verify signature.
|
47
|
-
def valid_sig?
|
48
|
-
return false unless has_pki?
|
49
|
-
digest = case pki_type
|
50
|
-
when 'x509+sha256'
|
51
|
-
OpenSSL::Digest::SHA256.new
|
52
|
-
when 'x509+sha1'
|
53
|
-
OpenSSL::Digest::SHA1.new
|
54
|
-
else
|
55
|
-
raise "pki_type: #{pki_type} is invalid type."
|
56
|
-
end
|
57
|
-
certs.first.public_key.verify(digest, signature, sig_message)
|
58
|
-
end
|
59
|
-
|
60
|
-
# verify expire time for payment request.
|
61
|
-
def valid_time?
|
62
|
-
expires = details.expires
|
63
|
-
return true if expires == 0
|
64
|
-
Time.now.to_i <= expires
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
# Generate data to be signed
|
70
|
-
def sig_message
|
71
|
-
PaymentRequest.new(payment_details_version: payment_details_version,
|
72
|
-
pki_type: pki_type, pki_data: pki_data, signature: '',
|
73
|
-
serialized_payment_details: serialized_payment_details).encode
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Bitcoin
|
2
|
-
module Payments
|
3
|
-
|
4
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#Certificates
|
5
|
-
class X509Certificates < Protobuf::Message
|
6
|
-
|
7
|
-
repeated :bytes, :certificate, 1
|
8
|
-
|
9
|
-
# get certificates
|
10
|
-
# @return [Array[OpenSSL::X509::Certificate]]
|
11
|
-
def certs
|
12
|
-
certificate.map{|v|OpenSSL::X509::Certificate.new(v)}
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
data/lib/bitcoin/payments.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'protobuf'
|
2
|
-
|
3
|
-
module Bitcoin
|
4
|
-
module Payments
|
5
|
-
|
6
|
-
autoload :Output, 'bitcoin/payments/output.pb'
|
7
|
-
autoload :Payment, 'bitcoin/payments/payment.pb'
|
8
|
-
autoload :PaymentACK, 'bitcoin/payments/payment_ack.pb'
|
9
|
-
autoload :PaymentDetails, 'bitcoin/payments/payment_details.pb'
|
10
|
-
autoload :PaymentRequest, 'bitcoin/payments/payment_request.pb'
|
11
|
-
autoload :X509Certificates, 'bitcoin/payments/x509_certificates.pb'
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|