bitcoinrb 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.ruby-version +1 -1
- data/Gemfile +8 -0
- data/bitcoinrb.gemspec +4 -12
- data/lib/bitcoin/bip324/ell_swift_pubkey.rb +1 -1
- data/lib/bitcoin/bip324.rb +2 -2
- data/lib/bitcoin/chain_params.rb +9 -0
- data/lib/bitcoin/chainparams/mainnet.yml +6 -2
- data/lib/bitcoin/chainparams/regtest.yml +1 -2
- data/lib/bitcoin/chainparams/signet.yml +3 -4
- data/lib/bitcoin/chainparams/testnet.yml +4 -4
- data/lib/bitcoin/chainparams/testnet4.yml +38 -0
- data/lib/bitcoin/ext.rb +0 -1
- data/lib/bitcoin/key.rb +2 -5
- data/lib/bitcoin/message_sign.rb +23 -4
- data/lib/bitcoin/psbt.rb +0 -2
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +15 -1
- data/lib/bitcoin/script/tx_checker.rb +1 -1
- data/lib/bitcoin/secp256k1/native.rb +53 -347
- data/lib/bitcoin/secp256k1/ruby.rb +19 -3
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin.rb +1 -1
- metadata +24 -97
- data/lib/bitcoin/ext/json_parser.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6e5ff6fa694353e122cd0028354edff2aa877debdc36d368503c25f6277c77f
|
4
|
+
data.tar.gz: a6ef9fb214582173d6681dcad6547208b6c606f9d4b82f1c2b3423f388866db1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4b858790060163880d19b8d771383b406facaa755386e7919e8c6d1caa6b7f362eeb611846f13f40b406114cae7d3e100cf4f5cb46adef01e37d40a43c35de6
|
7
|
+
data.tar.gz: a4712b529713ad39bebd6f32d9124d2f07d3c77c22482297df550f384b6f3ebf0ee6000958494684a95cc70354f95a22d8361dd686241c2d67c645fcee20ea5c
|
data/.github/workflows/ruby.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-3.
|
1
|
+
ruby-3.4.1
|
data/Gemfile
CHANGED
data/bitcoinrb.gemspec
CHANGED
@@ -26,22 +26,14 @@ Gem::Specification.new do |spec|
|
|
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'
|
29
|
-
spec.add_runtime_dependency 'ffi'
|
30
29
|
spec.add_runtime_dependency 'leb128', '~> 1.0.0'
|
31
30
|
spec.add_runtime_dependency 'eventmachine_httpserver'
|
32
31
|
spec.add_runtime_dependency 'iniparse'
|
33
32
|
spec.add_runtime_dependency 'siphash'
|
34
|
-
spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
|
35
33
|
spec.add_runtime_dependency 'bip-schnorr', '>= 0.7.0'
|
36
34
|
spec.add_runtime_dependency 'base32', '>= 0.3.4'
|
37
|
-
|
38
|
-
|
39
|
-
spec.
|
40
|
-
|
41
|
-
spec.add_development_dependency 'bundler'
|
42
|
-
spec.add_development_dependency 'rake', '>= 12.3.3'
|
43
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
44
|
-
spec.add_development_dependency 'timecop'
|
45
|
-
spec.add_development_dependency 'webmock', '>= 3.11.1'
|
46
|
-
spec.add_development_dependency 'parallel', '>= 1.20.1'
|
35
|
+
spec.add_runtime_dependency 'base64', '~> 0.2.0'
|
36
|
+
spec.add_runtime_dependency 'observer', '~> 0.1.2'
|
37
|
+
spec.add_runtime_dependency 'secp256k1rb', '0.1.1'
|
38
|
+
spec.add_runtime_dependency 'logger'
|
47
39
|
end
|
@@ -19,7 +19,7 @@ module Bitcoin
|
|
19
19
|
# Decode to public key.
|
20
20
|
# @return [Bitcoin::Key] Decoded public key.
|
21
21
|
def decode
|
22
|
-
if Bitcoin.secp_impl.
|
22
|
+
if Bitcoin.secp_impl.native?
|
23
23
|
pubkey = Bitcoin.secp_impl.ellswift_decode(key)
|
24
24
|
Bitcoin::Key.new(pubkey: pubkey, key_type: Bitcoin::Key::TYPES[:compressed])
|
25
25
|
else
|
data/lib/bitcoin/bip324.rb
CHANGED
@@ -131,8 +131,8 @@ module Bitcoin
|
|
131
131
|
raise ArgumentError, "ellswift_theirs must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_theirs.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
|
132
132
|
raise ArgumentError, "ellswift_ours must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_ours.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
|
133
133
|
|
134
|
-
if Bitcoin.secp_impl.
|
135
|
-
Bitcoin::Secp256k1::Native.ellswift_ecdh_xonly(ellswift_theirs, ellswift_ours, priv_key, initiating)
|
134
|
+
if Bitcoin.secp_impl.native?
|
135
|
+
Bitcoin::Secp256k1::Native.ellswift_ecdh_xonly(ellswift_theirs.key, ellswift_ours.key, priv_key, initiating)
|
136
136
|
else
|
137
137
|
ecdh_point_x32 = ellswift_ecdh_xonly(ellswift_theirs, priv_key).htb
|
138
138
|
content = initiating ? ellswift_ours.key + ellswift_theirs.key + ecdh_point_x32 :
|
data/lib/bitcoin/chain_params.rb
CHANGED
@@ -56,6 +56,11 @@ module Bitcoin
|
|
56
56
|
init('signet')
|
57
57
|
end
|
58
58
|
|
59
|
+
# testnet 4 genesis
|
60
|
+
def self.testnet4
|
61
|
+
init('testnet4')
|
62
|
+
end
|
63
|
+
|
59
64
|
def mainnet?
|
60
65
|
network == 'mainnet'
|
61
66
|
end
|
@@ -72,6 +77,10 @@ module Bitcoin
|
|
72
77
|
network == 'signet'
|
73
78
|
end
|
74
79
|
|
80
|
+
def testnet4?
|
81
|
+
network == 'testnet4'
|
82
|
+
end
|
83
|
+
|
75
84
|
def genesis_block
|
76
85
|
header = Bitcoin::BlockHeader.new(
|
77
86
|
genesis['version'], genesis['prev_hash'].rhex, genesis['merkle_root'].rhex,
|
@@ -27,9 +27,13 @@ proof_of_work_limit: 0x1d00ffff
|
|
27
27
|
dns_seeds:
|
28
28
|
- "seed.bitcoin.sipa.be"
|
29
29
|
- "dnsseed.bluematt.me"
|
30
|
-
- "dnsseed.bitcoin.dashjr.
|
31
|
-
- "seed.bitcoinstats.com"
|
30
|
+
- "dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us"
|
32
31
|
- "seed.bitcoin.jonasschnelli.ch"
|
32
|
+
- "seed.btc.petertodd.net"
|
33
|
+
- "seed.bitcoin.sprovoost.nl"
|
34
|
+
- "dnsseed.emzy.de"
|
35
|
+
- "seed.bitcoin.wiz.biz"
|
36
|
+
- "seed.mainnet.achownodes.xyz"
|
33
37
|
genesis:
|
34
38
|
hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
35
39
|
merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
@@ -22,8 +22,7 @@ retarget_interval: 2016
|
|
22
22
|
retarget_time: 1209600 # 2 weeks
|
23
23
|
target_spacing: 600 # block interval
|
24
24
|
max_money: 21000000
|
25
|
-
bip34_height:
|
26
|
-
genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
|
25
|
+
bip34_height: 1
|
27
26
|
proof_of_work_limit: 0x207fffff
|
28
27
|
dns_seeds:
|
29
28
|
genesis:
|
@@ -22,12 +22,11 @@ retarget_interval: 2016
|
|
22
22
|
retarget_time: 1209600 # 2 weeks
|
23
23
|
target_spacing: 600 # block interval
|
24
24
|
max_money: 21000000
|
25
|
-
bip34_height:
|
26
|
-
genesis_hash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
|
25
|
+
bip34_height: 1
|
27
26
|
proof_of_work_limit: 0x1d00ffff
|
28
27
|
dns_seeds:
|
29
|
-
- "
|
30
|
-
- "
|
28
|
+
- "seed.signet.bitcoin.sprovoost.nl"
|
29
|
+
- "seed.signet.achownodes.xyz"
|
31
30
|
genesis:
|
32
31
|
hash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
|
33
32
|
merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
@@ -22,14 +22,14 @@ retarget_interval: 2016
|
|
22
22
|
retarget_time: 1209600 # 2 weeks
|
23
23
|
target_spacing: 600 # block interval
|
24
24
|
max_money: 21000000
|
25
|
-
bip34_height:
|
26
|
-
genesis_hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
25
|
+
bip34_height: 21111
|
27
26
|
proof_of_work_limit: 0x1d00ffff
|
28
27
|
dns_seeds:
|
29
28
|
- "testnet-seed.bitcoin.jonasschnelli.ch"
|
30
|
-
- "seed.tbtc.petertodd.
|
29
|
+
- "seed.tbtc.petertodd.net"
|
30
|
+
- "seed.testnet.bitcoin.sprovoost.nl"
|
31
31
|
- "testnet-seed.bluematt.me"
|
32
|
-
- "
|
32
|
+
- "seed.testnet.achownodes.xyz"
|
33
33
|
genesis:
|
34
34
|
hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
35
35
|
merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
--- !ruby/object:Bitcoin::ChainParams
|
2
|
+
network: "testnet4"
|
3
|
+
magic_head: "1c163f28"
|
4
|
+
message_magic: "Bitcoin Signed Message:\n"
|
5
|
+
address_version: "6f"
|
6
|
+
p2sh_version: "c4"
|
7
|
+
bech32_hrp: 'tb'
|
8
|
+
privkey_version: "ef"
|
9
|
+
extended_privkey_version: "04358394"
|
10
|
+
extended_pubkey_version: "043587cf"
|
11
|
+
bip49_pubkey_p2wpkh_p2sh_version: "044a5262"
|
12
|
+
bip49_pubkey_p2wsh_p2sh_version: "024289ef"
|
13
|
+
bip49_privkey_p2wpkh_p2sh_version: "044a4e28"
|
14
|
+
bip49_privkey_p2wsh_p2sh_version: "024285b5"
|
15
|
+
bip84_pubkey_p2wpkh_version: "045f1cf6"
|
16
|
+
bip84_pubkey_p2wsh_version: "02575483"
|
17
|
+
bip84_privkey_p2wpkh_version: "045f18bc"
|
18
|
+
bip84_privkey_p2wsh_version: "02575048"
|
19
|
+
default_port: 48333
|
20
|
+
protocol_version: 70013
|
21
|
+
retarget_interval: 2016
|
22
|
+
retarget_time: 1209600 # 2 weeks
|
23
|
+
target_spacing: 600 # block interval
|
24
|
+
max_money: 21000000
|
25
|
+
bip34_height: 1
|
26
|
+
proof_of_work_limit: 0x1d00ffff
|
27
|
+
dns_seeds:
|
28
|
+
- "seed.testnet4.bitcoin.sprovoost.nl"
|
29
|
+
- "seed.testnet4.wiz.biz"
|
30
|
+
genesis:
|
31
|
+
hash: "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043"
|
32
|
+
merkle_root: "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e"
|
33
|
+
time: 1714777860
|
34
|
+
nonce: 393743547
|
35
|
+
bits: 0x1d00ffff
|
36
|
+
version: 1
|
37
|
+
prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
38
|
+
bip44_coin_type: 1
|
data/lib/bitcoin/ext.rb
CHANGED
data/lib/bitcoin/key.rb
CHANGED
@@ -146,11 +146,8 @@ module Bitcoin
|
|
146
146
|
# @return [Bitcoin::Key] Recovered public key.
|
147
147
|
def self.recover_compact(data, signature)
|
148
148
|
rec_id = signature.unpack1('C')
|
149
|
-
rec = (rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE) & 3
|
150
|
-
raise ArgumentError, 'Invalid signature parameter' if rec < 0 || rec > 3
|
151
|
-
|
152
149
|
compressed = (rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE) & 4 != 0
|
153
|
-
Bitcoin.secp_impl.recover_compact(data, signature,
|
150
|
+
Bitcoin.secp_impl.recover_compact(data, signature, compressed)
|
154
151
|
end
|
155
152
|
|
156
153
|
# verify signature using public key
|
@@ -349,7 +346,7 @@ module Bitcoin
|
|
349
346
|
# @raise ArgumentError If ent32 does not 32 bytes.
|
350
347
|
def create_ell_pubkey
|
351
348
|
raise ArgumentError, "private_key required." unless priv_key
|
352
|
-
if secp256k1_module.
|
349
|
+
if secp256k1_module.native?
|
353
350
|
Bitcoin::BIP324::EllSwiftPubkey.new(secp256k1_module.ellswift_create(priv_key))
|
354
351
|
else
|
355
352
|
Bitcoin::BIP324::EllSwiftPubkey.new(Bitcoin::BIP324.xelligatorswift(xonly_pubkey))
|
data/lib/bitcoin/message_sign.rb
CHANGED
@@ -62,13 +62,23 @@ module Bitcoin
|
|
62
62
|
pubkey = Bitcoin::Key.recover_compact(message_hash(message, prefix: prefix, legacy: true), sig)
|
63
63
|
return false unless pubkey
|
64
64
|
pubkey.to_p2pkh == address
|
65
|
-
rescue
|
66
|
-
|
65
|
+
rescue Exception
|
66
|
+
false
|
67
67
|
end
|
68
68
|
elsif addr_script.witness_program?
|
69
69
|
# BIP322 verification
|
70
|
-
|
71
|
-
|
70
|
+
digest = message_hash(message, prefix: prefix, legacy: false)
|
71
|
+
begin
|
72
|
+
# Full
|
73
|
+
tx = Bitcoin::Tx.parse_from_payload(sig)
|
74
|
+
validate_to_sign_tx!(tx)
|
75
|
+
to_spend = to_spend_tx(digest, address)
|
76
|
+
return false unless tx.in[0].out_point.tx_hash == to_spend.tx_hash
|
77
|
+
rescue Exception
|
78
|
+
# Simple
|
79
|
+
tx = to_sign_tx(digest, address)
|
80
|
+
tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig)
|
81
|
+
end
|
72
82
|
script_pubkey = Bitcoin::Script.parse_from_addr(address)
|
73
83
|
tx_out = Bitcoin::TxOut.new(script_pubkey: script_pubkey)
|
74
84
|
flags = Bitcoin::STANDARD_SCRIPT_VERIFY_FLAGS
|
@@ -99,6 +109,14 @@ module Bitcoin
|
|
99
109
|
end
|
100
110
|
end
|
101
111
|
|
112
|
+
def validate_to_sign_tx!(tx)
|
113
|
+
raise ArgumentError, "Multiple inputs (proof of funds) are not supported." unless tx.in.length == 1
|
114
|
+
raise ArgumentError, "vin[0].prevout.n must be 0." unless tx.in[0].out_point.index == 0
|
115
|
+
raise ArgumentError, "Multiple outputs are not supported." unless tx.out.length == 1
|
116
|
+
raise ArgumentError, "vout[0].nValue must be 0." unless tx.out[0].value == 0
|
117
|
+
raise ArgumentError, "vout[0].scriptPubKey must be OP_RETURN." unless tx.out[0].script_pubkey == Bitcoin::Script.new << Bitcoin::Opcodes::OP_RETURN
|
118
|
+
end
|
119
|
+
|
102
120
|
def to_spend_tx(digest, addr)
|
103
121
|
validate_address!(addr)
|
104
122
|
message_challenge = Bitcoin::Script.parse_from_addr(addr)
|
@@ -124,5 +142,6 @@ module Bitcoin
|
|
124
142
|
|
125
143
|
private_class_method :validate_address!
|
126
144
|
private_class_method :validate_format!
|
145
|
+
private_class_method :validate_to_sign_tx!
|
127
146
|
end
|
128
147
|
end
|
data/lib/bitcoin/psbt.rb
CHANGED
@@ -62,7 +62,8 @@ module Bitcoin
|
|
62
62
|
request.body = data.to_json
|
63
63
|
response = http.request(request)
|
64
64
|
body = response.body
|
65
|
-
|
65
|
+
json_data = JSON.parse(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') })
|
66
|
+
response = convert_floats_to_strings(json_data)
|
66
67
|
raise response['error'].to_json if response['error']
|
67
68
|
response['result']
|
68
69
|
end
|
@@ -77,6 +78,19 @@ module Bitcoin
|
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
81
|
+
# Convert float value
|
82
|
+
def convert_floats_to_strings(obj)
|
83
|
+
case obj
|
84
|
+
when Float
|
85
|
+
obj.to_s
|
86
|
+
when Hash
|
87
|
+
obj.transform_values { |v| convert_floats_to_strings(v) }
|
88
|
+
when Array
|
89
|
+
obj.map { |item| convert_floats_to_strings(item) }
|
90
|
+
else
|
91
|
+
obj
|
92
|
+
end
|
93
|
+
end
|
80
94
|
end
|
81
95
|
end
|
82
96
|
end
|
@@ -37,7 +37,7 @@ module Bitcoin
|
|
37
37
|
|
38
38
|
# check schnorr signature.
|
39
39
|
# @param [String] sig schnorr signature with hex format.
|
40
|
-
# @param [String] pubkey a public key with hex
|
40
|
+
# @param [String] pubkey a public key with hex format.
|
41
41
|
# @param [Symbol] sig_version whether :taproot or :tapscript
|
42
42
|
# @return [Boolean] verification result
|
43
43
|
def check_schnorr_sig(sig, pubkey, sig_version, opts = {})
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Porting part of the code from bitcoin-ruby. see the license.
|
2
2
|
# https://github.com/lian/bitcoin-ruby/blob/master/COPYING
|
3
|
+
require 'secp256k1'
|
3
4
|
|
4
5
|
module Bitcoin
|
5
6
|
module Secp256k1
|
@@ -10,91 +11,15 @@ module Bitcoin
|
|
10
11
|
# for linux, ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so' or '/usr/lib64/libsecp256k1.so'
|
11
12
|
# for mac,
|
12
13
|
module Native
|
13
|
-
include ::FFI::Library
|
14
|
-
extend self
|
15
|
-
|
16
|
-
SECP256K1_FLAGS_TYPE_MASK = ((1 << 8) - 1)
|
17
|
-
SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
|
18
|
-
SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1)
|
19
|
-
|
20
|
-
SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
|
21
|
-
SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
|
22
|
-
SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8)
|
23
|
-
|
24
|
-
# Flags to pass to secp256k1_context_create.
|
25
|
-
SECP256K1_CONTEXT_VERIFY = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
|
26
|
-
SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
|
27
|
-
|
28
|
-
# Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export.
|
29
|
-
SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
|
30
|
-
SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
|
31
14
|
|
32
15
|
module_function
|
33
16
|
|
34
|
-
|
35
|
-
raise 'secp256k1 library dose not found.' unless File.exist?(ENV['SECP256K1_LIB_PATH'])
|
36
|
-
ffi_lib(ENV['SECP256K1_LIB_PATH'])
|
37
|
-
load_functions
|
38
|
-
end
|
39
|
-
|
40
|
-
def load_functions
|
41
|
-
attach_function(:secp256k1_context_create, [:uint], :pointer)
|
42
|
-
attach_function(:secp256k1_context_destroy, [:pointer], :void)
|
43
|
-
attach_function(:secp256k1_context_randomize, [:pointer, :pointer], :int)
|
44
|
-
attach_function(:secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int)
|
45
|
-
attach_function(:secp256k1_ec_seckey_verify, [:pointer, :pointer], :int)
|
46
|
-
attach_function(:secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
47
|
-
attach_function(:secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int)
|
48
|
-
attach_function(:secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int)
|
49
|
-
attach_function(:secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int)
|
50
|
-
attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
|
51
|
-
attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
|
52
|
-
attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
|
53
|
-
attach_function(:secp256k1_schnorrsig_sign32, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
54
|
-
attach_function(:secp256k1_schnorrsig_verify, [:pointer, :pointer, :pointer, :size_t, :pointer], :int)
|
55
|
-
attach_function(:secp256k1_keypair_create, [:pointer, :pointer, :pointer], :int)
|
56
|
-
attach_function(:secp256k1_xonly_pubkey_parse, [:pointer, :pointer, :pointer], :int)
|
57
|
-
attach_function(:secp256k1_ecdsa_sign_recoverable, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
58
|
-
attach_function(:secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int)
|
59
|
-
attach_function(:secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int)
|
60
|
-
attach_function(:secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int)
|
61
|
-
attach_function(:secp256k1_ellswift_decode, [:pointer, :pointer, :pointer], :int)
|
62
|
-
attach_function(:secp256k1_ellswift_create, [:pointer, :pointer, :pointer, :pointer], :int)
|
63
|
-
# Define function pointer
|
64
|
-
callback(:secp256k1_ellswift_xdh_hash_function, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
65
|
-
attach_variable(:secp256k1_ellswift_xdh_hash_function_bip324, :secp256k1_ellswift_xdh_hash_function)
|
66
|
-
attach_function(:secp256k1_ellswift_xdh, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :pointer, :pointer], :int)
|
67
|
-
end
|
68
|
-
|
69
|
-
def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
|
70
|
-
init
|
71
|
-
begin
|
72
|
-
context = secp256k1_context_create(flags)
|
73
|
-
ret, tries, max = 0, 0, 20
|
74
|
-
while ret != 1
|
75
|
-
raise 'secp256k1_context_randomize failed.' if tries >= max
|
76
|
-
tries += 1
|
77
|
-
ret = secp256k1_context_randomize(context, FFI::MemoryPointer.from_string(SecureRandom.random_bytes(32)))
|
78
|
-
end
|
79
|
-
yield(context) if block_given?
|
80
|
-
ensure
|
81
|
-
secp256k1_context_destroy(context)
|
82
|
-
end
|
83
|
-
end
|
17
|
+
extend ::Secp256k1
|
84
18
|
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
while ret != 1
|
90
|
-
raise 'secp256k1_ec_seckey_verify in generate_key_pair failed.' if tries >= max
|
91
|
-
tries += 1
|
92
|
-
priv_key = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32))
|
93
|
-
ret = secp256k1_ec_seckey_verify(context, priv_key)
|
94
|
-
end
|
95
|
-
private_key = priv_key.read_string(32).bth
|
96
|
-
[private_key , generate_pubkey_in_context(context, private_key, compressed: compressed) ]
|
97
|
-
end
|
19
|
+
# Whether this module is native c wrapper or not?
|
20
|
+
# @return [Boolean]
|
21
|
+
def native?
|
22
|
+
true
|
98
23
|
end
|
99
24
|
|
100
25
|
# generate bitcoin key object
|
@@ -103,295 +28,76 @@ module Bitcoin
|
|
103
28
|
Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
|
104
29
|
end
|
105
30
|
|
106
|
-
def generate_pubkey(priv_key, compressed: true)
|
107
|
-
with_context do |context|
|
108
|
-
generate_pubkey_in_context(context, priv_key, compressed: compressed)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# sign data.
|
113
|
-
# @param [String] data a data to be signed with binary format
|
114
|
-
# @param [String] privkey a private key with hex format using sign
|
115
|
-
# @param [String] extra_entropy a extra entropy with binary format for rfc6979
|
116
|
-
# @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
|
117
|
-
# @return [String] signature data with binary format
|
118
|
-
def sign_data(data, privkey, extra_entropy = nil, algo: :ecdsa)
|
119
|
-
case algo
|
120
|
-
when :ecdsa
|
121
|
-
sign_ecdsa(data, privkey, extra_entropy)
|
122
|
-
when :schnorr
|
123
|
-
sign_schnorr(data, privkey, extra_entropy)
|
124
|
-
else
|
125
|
-
nil
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
31
|
# Sign data with compact format.
|
130
32
|
# @param [String] data a data to be signed with binary format
|
131
33
|
# @param [String] privkey a private key using sign with hex format
|
132
34
|
# @return [Array[signature, recovery id]]
|
133
35
|
def sign_compact(data, privkey)
|
134
|
-
|
135
|
-
|
136
|
-
hash =FFI::MemoryPointer.new(:uchar, data.bytesize).put_bytes(0, data)
|
137
|
-
priv_key = privkey.htb
|
138
|
-
sec_key = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
|
139
|
-
result = secp256k1_ecdsa_sign_recoverable(context, sig, hash, sec_key, nil, nil)
|
140
|
-
raise 'secp256k1_ecdsa_sign_recoverable failed.' if result == 0
|
141
|
-
|
142
|
-
output = FFI::MemoryPointer.new(:uchar, 64)
|
143
|
-
rec = FFI::MemoryPointer.new(:uint64)
|
144
|
-
result = secp256k1_ecdsa_recoverable_signature_serialize_compact(context, output, rec, sig)
|
145
|
-
raise 'secp256k1_ecdsa_recoverable_signature_serialize_compact failed.' unless result == 1
|
146
|
-
|
147
|
-
raw_sig = output.read_string(64)
|
148
|
-
[ECDSA::Signature.new(raw_sig[0...32].bti, raw_sig[32..-1].bti), rec.read(:int)]
|
149
|
-
end
|
36
|
+
sig, rec_id = sign_recoverable(data, privkey)
|
37
|
+
[ECDSA::Signature.new(sig[0...64].to_i(16), sig[64..-1].to_i(16)), rec_id]
|
150
38
|
end
|
151
39
|
|
152
40
|
# Recover public key from compact signature.
|
153
41
|
# @param [String] data message digest using signature.
|
154
|
-
# @param [String] signature signature with binary format.
|
155
|
-
# @param [Integer] rec recovery id.
|
42
|
+
# @param [String] signature signature with binary format(65 bytes).
|
156
43
|
# @param [Boolean] compressed whether compressed public key or not.
|
157
44
|
# @return [Bitcoin::Key] Recovered public key.
|
158
|
-
def recover_compact(data, signature,
|
159
|
-
|
160
|
-
|
161
|
-
input = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1])
|
162
|
-
result = secp256k1_ecdsa_recoverable_signature_parse_compact(context, sig, input, rec)
|
163
|
-
raise 'secp256k1_ecdsa_recoverable_signature_parse_compact failed.' unless result == 1
|
164
|
-
|
165
|
-
pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
166
|
-
msg = FFI::MemoryPointer.new(:uchar, data.bytesize).put_bytes(0, data)
|
167
|
-
result = secp256k1_ecdsa_recover(context, pubkey, sig, msg)
|
168
|
-
raise 'secp256k1_ecdsa_recover failed.' unless result == 1
|
169
|
-
|
170
|
-
pubkey = serialize_pubkey_internal(context, pubkey.read_string(64), compressed)
|
171
|
-
Bitcoin::Key.new(pubkey: pubkey, compressed: compressed)
|
172
|
-
end
|
45
|
+
def recover_compact(data, signature, compressed)
|
46
|
+
pubkey = recover(data, signature, compressed)
|
47
|
+
Bitcoin::Key.new(pubkey: pubkey, compressed: compressed)
|
173
48
|
end
|
174
49
|
|
175
|
-
#
|
176
|
-
# @param [String] data
|
177
|
-
# @param [String]
|
178
|
-
# @param [String]
|
179
|
-
#
|
180
|
-
# @return [
|
181
|
-
|
50
|
+
# Sign to data.
|
51
|
+
# @param [String] data The 32-byte message hash being signed with binary format.
|
52
|
+
# @param [String] private_key a private key with hex format using sign.
|
53
|
+
# @param [String] extra_entropy a extra entropy with binary format for rfc6979.
|
54
|
+
# @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
|
55
|
+
# @return [String] signature data with binary format. If unsupported algorithm specified, return nil.
|
56
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
57
|
+
def sign_data(data, private_key, extra_entropy = nil, algo: :ecdsa)
|
182
58
|
case algo
|
183
59
|
when :ecdsa
|
184
|
-
|
60
|
+
begin
|
61
|
+
sign_ecdsa(data, private_key, extra_entropy)
|
62
|
+
rescue ArgumentError
|
63
|
+
false
|
64
|
+
end
|
185
65
|
when :schnorr
|
186
|
-
|
66
|
+
begin
|
67
|
+
sign_schnorr(data, private_key, extra_entropy)
|
68
|
+
rescue ArgumentError
|
69
|
+
false
|
70
|
+
end
|
187
71
|
else
|
188
|
-
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
# # validate whether this is a valid public key (more expensive than IsValid())
|
193
|
-
# @param [String] pub_key public key with hex format.
|
194
|
-
# @param [Boolean] allow_hybrid whether support hybrid public key.
|
195
|
-
# @return [Boolean] If valid public key return true, otherwise false.
|
196
|
-
def parse_ec_pubkey?(pub_key, allow_hybrid = false)
|
197
|
-
pub_key = pub_key.htb
|
198
|
-
return false if !allow_hybrid && ![0x02, 0x03, 0x04].include?(pub_key[0].ord)
|
199
|
-
with_context do |context|
|
200
|
-
pubkey = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
|
201
|
-
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
202
|
-
result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pub_key.bytesize)
|
203
|
-
result == 1
|
72
|
+
raise ArgumentError, "unknown algo: #{algo}"
|
204
73
|
end
|
205
74
|
end
|
206
75
|
|
207
|
-
#
|
208
|
-
# @param [String]
|
209
|
-
# @
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
# Check whether valid x-only public key or not.
|
222
|
-
# @param [String] pub_key x-only public key with hex format(32 bytes).
|
223
|
-
# @return [Boolean] result.
|
224
|
-
def valid_xonly_pubkey?(pub_key)
|
225
|
-
begin
|
226
|
-
full_pubkey_from_xonly_pubkey(pub_key)
|
227
|
-
rescue Exception
|
228
|
-
return false
|
229
|
-
end
|
230
|
-
true
|
231
|
-
end
|
232
|
-
|
233
|
-
# Decode ellswift public key.
|
234
|
-
# @param [String] ell_key ElligatorSwift key with binary format.
|
235
|
-
# @return [String] Decoded public key with hex format
|
236
|
-
def ellswift_decode(ell_key)
|
237
|
-
with_context do |context|
|
238
|
-
ell64 = FFI::MemoryPointer.new(:uchar, ell_key.bytesize).put_bytes(0, ell_key)
|
239
|
-
internal = FFI::MemoryPointer.new(:uchar, 64)
|
240
|
-
result = secp256k1_ellswift_decode(context, internal, ell64)
|
241
|
-
raise ArgumentError, 'Decode failed.' unless result == 1
|
242
|
-
serialize_pubkey_internal(context, internal, true)
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
# Compute an ElligatorSwift public key for a secret key.
|
247
|
-
# @param [String] priv_key private key with hex format
|
248
|
-
# @return [String] ElligatorSwift public key with hex format.
|
249
|
-
def ellswift_create(priv_key)
|
250
|
-
with_context(flags: SECP256K1_CONTEXT_SIGN) do |context|
|
251
|
-
ell64 = FFI::MemoryPointer.new(:uchar, 64)
|
252
|
-
seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb)
|
253
|
-
result = secp256k1_ellswift_create(context, ell64, seckey32, nil)
|
254
|
-
raise ArgumentError, 'Failed to create ElligatorSwift public key.' unless result == 1
|
255
|
-
ell64.read_string(64).bth
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
# Compute X coordinate of shared ECDH point between elswift pubkey and privkey.
|
260
|
-
# @param [Bitcoin::BIP324::EllSwiftPubkey] their_ell_pubkey Their EllSwift public key.
|
261
|
-
# @param [Bitcoin::BIP324::EllSwiftPubkey] our_ell_pubkey Our EllSwift public key.
|
262
|
-
# @param [String] priv_key private key with hex format.
|
263
|
-
# @param [Boolean] initiating Whether your initiator or not.
|
264
|
-
# @return [String] x coordinate with hex format.
|
265
|
-
def ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, priv_key, initiating)
|
266
|
-
with_context(flags: SECP256K1_CONTEXT_SIGN) do |context|
|
267
|
-
output = FFI::MemoryPointer.new(:uchar, 32)
|
268
|
-
our_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, our_ell_pubkey.key)
|
269
|
-
their_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, their_ell_pubkey.key)
|
270
|
-
seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb)
|
271
|
-
hashfp = secp256k1_ellswift_xdh_hash_function_bip324
|
272
|
-
result = secp256k1_ellswift_xdh(context, output,
|
273
|
-
initiating ? our_ell_ptr : their_ell_ptr,
|
274
|
-
initiating ? their_ell_ptr : our_ell_ptr,
|
275
|
-
seckey32,
|
276
|
-
initiating ? 0 : 1,
|
277
|
-
hashfp, nil)
|
278
|
-
raise ArgumentError, "secret was invalid or hashfp returned 0" unless result == 1
|
279
|
-
output.read_string(32).bth
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
private
|
284
|
-
|
285
|
-
# Calculate full public key(64 bytes) from public key(32 bytes).
|
286
|
-
# @param [String] pub_key x-only public key with hex format(32 bytes).
|
287
|
-
# @return [String] x-only public key with hex format(64 bytes).
|
288
|
-
def full_pubkey_from_xonly_pubkey(pub_key)
|
289
|
-
with_context do |context|
|
290
|
-
pubkey = pub_key.htb
|
291
|
-
raise ArgumentError, "Pubkey size must be #{X_ONLY_PUBKEY_SIZE} bytes." unless pubkey.bytesize == X_ONLY_PUBKEY_SIZE
|
292
|
-
xonly_pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
|
293
|
-
full_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
294
|
-
raise ArgumentError, 'An invalid public key was specified.' unless secp256k1_xonly_pubkey_parse(context, full_pubkey, xonly_pubkey) == 1
|
295
|
-
full_pubkey.read_string(64).bth
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def generate_pubkey_in_context(context, privkey, compressed: true)
|
300
|
-
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
301
|
-
result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb)
|
302
|
-
raise 'error creating pubkey' unless result
|
303
|
-
serialize_pubkey_internal(context, internal_pubkey, compressed)
|
304
|
-
end
|
305
|
-
|
306
|
-
def sign_ecdsa(data, privkey, extra_entropy)
|
307
|
-
with_context do |context|
|
308
|
-
secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
|
309
|
-
raise 'priv_key is invalid' unless secp256k1_ec_seckey_verify(context, secret)
|
310
|
-
|
311
|
-
internal_signature = FFI::MemoryPointer.new(:uchar, 64)
|
312
|
-
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
|
313
|
-
entropy = extra_entropy ? FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, extra_entropy) : nil
|
314
|
-
|
315
|
-
ret, tries, max = 0, 0, 20
|
316
|
-
|
317
|
-
while ret != 1
|
318
|
-
raise 'secp256k1_ecdsa_sign failed.' if tries >= max
|
319
|
-
tries += 1
|
320
|
-
ret = secp256k1_ecdsa_sign(context, internal_signature, msg32, secret, nil, entropy)
|
76
|
+
# Verify signature.
|
77
|
+
# @param [String] data The 32-byte message hash assumed to be signed.
|
78
|
+
# @param [String] signature signature data with binary format
|
79
|
+
# @param [String] pubkey a public key with hex format using verify.
|
80
|
+
# @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
|
81
|
+
# @return [Boolean] verification result.
|
82
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
83
|
+
def verify_sig(data, signature, pubkey, algo: :ecdsa)
|
84
|
+
case algo
|
85
|
+
when :ecdsa
|
86
|
+
begin
|
87
|
+
verify_ecdsa(data, signature, pubkey)
|
88
|
+
rescue ArgumentError
|
89
|
+
false
|
321
90
|
end
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
end
|
331
|
-
|
332
|
-
def sign_schnorr(data, privkey, aux_rand = nil)
|
333
|
-
with_context do |context|
|
334
|
-
keypair = create_keypair(privkey).htb
|
335
|
-
keypair = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
|
336
|
-
signature = FFI::MemoryPointer.new(:uchar, 64)
|
337
|
-
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
|
338
|
-
aux_rand = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, aux_rand) if aux_rand
|
339
|
-
raise 'Failed to generate schnorr signature.' unless secp256k1_schnorrsig_sign32(context, signature, msg32, keypair, aux_rand) == 1
|
340
|
-
signature.read_string(64)
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
def verify_ecdsa(data, sig, pubkey)
|
345
|
-
with_context do |context|
|
346
|
-
return false if data.bytesize == 0
|
347
|
-
pubkey = pubkey.htb
|
348
|
-
pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
|
349
|
-
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
350
|
-
result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size)
|
351
|
-
return false unless result
|
352
|
-
|
353
|
-
signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
|
354
|
-
internal_signature = FFI::MemoryPointer.new(:uchar, 64)
|
355
|
-
result = secp256k1_ecdsa_signature_parse_der(context, internal_signature, signature, signature.size)
|
356
|
-
return false unless result
|
357
|
-
|
358
|
-
# libsecp256k1's ECDSA verification requires lower-S signatures, which have not historically been enforced in Bitcoin, so normalize them first.
|
359
|
-
secp256k1_ecdsa_signature_normalize(context, internal_signature, internal_signature)
|
360
|
-
|
361
|
-
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
|
362
|
-
result = secp256k1_ecdsa_verify(context, internal_signature, msg32, internal_pubkey)
|
363
|
-
|
364
|
-
result == 1
|
365
|
-
end
|
366
|
-
end
|
367
|
-
|
368
|
-
def verify_schnorr(data, sig, pubkey)
|
369
|
-
with_context do |context|
|
370
|
-
return false if data.bytesize == 0
|
371
|
-
pubkey = full_pubkey_from_xonly_pubkey(pubkey).htb
|
372
|
-
xonly_pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
|
373
|
-
signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
|
374
|
-
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
|
375
|
-
result = secp256k1_schnorrsig_verify(context, signature, msg32, 32, xonly_pubkey)
|
376
|
-
result == 1
|
91
|
+
when :schnorr
|
92
|
+
begin
|
93
|
+
verify_schnorr(data, signature, pubkey)
|
94
|
+
rescue ArgumentError
|
95
|
+
false
|
96
|
+
end
|
97
|
+
else
|
98
|
+
raise ArgumentError, "unknown algo: #{algo}"
|
377
99
|
end
|
378
100
|
end
|
379
|
-
|
380
|
-
# Serialize public key.
|
381
|
-
def serialize_pubkey_internal(context, pubkey_input, compressed)
|
382
|
-
pubkey = FFI::MemoryPointer.new(:uchar, 65)
|
383
|
-
pubkey_len = FFI::MemoryPointer.new(:uint64)
|
384
|
-
result = if compressed
|
385
|
-
pubkey_len.put_uint64(0, 33)
|
386
|
-
secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, pubkey_input, SECP256K1_EC_COMPRESSED)
|
387
|
-
else
|
388
|
-
pubkey_len.put_uint64(0, 65)
|
389
|
-
secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, pubkey_input, SECP256K1_EC_UNCOMPRESSED)
|
390
|
-
end
|
391
|
-
raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
|
392
|
-
pubkey.read_string(pubkey_len.read_uint64).bth
|
393
|
-
end
|
394
|
-
|
395
101
|
end
|
396
102
|
end
|
397
103
|
end
|
@@ -9,6 +9,13 @@ module Bitcoin
|
|
9
9
|
module Ruby
|
10
10
|
|
11
11
|
module_function
|
12
|
+
extend Schnorr::Util
|
13
|
+
|
14
|
+
# Whether this module is native c wrapper or not?
|
15
|
+
# @return [Boolean]
|
16
|
+
def native?
|
17
|
+
false
|
18
|
+
end
|
12
19
|
|
13
20
|
# generate ec private key and public key
|
14
21
|
def generate_key_pair(compressed: true)
|
@@ -72,11 +79,20 @@ module Bitcoin
|
|
72
79
|
|
73
80
|
# Recover public key from compact signature.
|
74
81
|
# @param [String] data message digest using signature.
|
75
|
-
# @param [String] signature signature with binary format.
|
76
|
-
# @param [Integer] rec recovery id.
|
82
|
+
# @param [String] signature signature with binary format(65 bytes).
|
77
83
|
# @param [Boolean] compressed whether compressed public key or not.
|
78
84
|
# @return [Bitcoin::Key] Recovered public key.
|
79
|
-
|
85
|
+
# @raise [ArgumentError] If invalid arguments specified.
|
86
|
+
def recover_compact(data, signature, compressed)
|
87
|
+
raise ArgumentError, "data must be String." unless data.is_a?(String)
|
88
|
+
raise ArgumentError, "signature must be String." unless signature.is_a?(String)
|
89
|
+
signature = hex2bin(signature)
|
90
|
+
raise ArgumentError, "signature must be 64 bytes." unless signature.bytesize == 65
|
91
|
+
data = hex2bin(data)
|
92
|
+
raise ArgumentError, "data must be 32 bytes." unless data.bytesize == 32
|
93
|
+
rec = (signature[0].ord - 0x1b) & 3
|
94
|
+
raise ArgumentError, "rec must be between 0 and 3." if rec < 0 || rec > 3
|
95
|
+
|
80
96
|
group = Bitcoin::Secp256k1::GROUP
|
81
97
|
r = ECDSA::Format::IntegerOctetString.decode(signature[1...33])
|
82
98
|
s = ECDSA::Format::IntegerOctetString.decode(signature[33..-1])
|
data/lib/bitcoin/version.rb
CHANGED
data/lib/bitcoin.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitcoinrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-03-18 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: ecdsa_ext
|
@@ -94,20 +93,6 @@ dependencies:
|
|
94
93
|
- - ">="
|
95
94
|
- !ruby/object:Gem::Version
|
96
95
|
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: ffi
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :runtime
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
96
|
- !ruby/object:Gem::Dependency
|
112
97
|
name: leb128
|
113
98
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,20 +149,6 @@ dependencies:
|
|
164
149
|
- - ">="
|
165
150
|
- !ruby/object:Gem::Version
|
166
151
|
version: '0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: json_pure
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: 2.3.1
|
174
|
-
type: :runtime
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: 2.3.1
|
181
152
|
- !ruby/object:Gem::Dependency
|
182
153
|
name: bip-schnorr
|
183
154
|
requirement: !ruby/object:Gem::Requirement
|
@@ -207,103 +178,61 @@ dependencies:
|
|
207
178
|
- !ruby/object:Gem::Version
|
208
179
|
version: 0.3.4
|
209
180
|
- !ruby/object:Gem::Dependency
|
210
|
-
name:
|
211
|
-
requirement: !ruby/object:Gem::Requirement
|
212
|
-
requirements:
|
213
|
-
- - ">="
|
214
|
-
- !ruby/object:Gem::Version
|
215
|
-
version: '0'
|
216
|
-
type: :development
|
217
|
-
prerelease: false
|
218
|
-
version_requirements: !ruby/object:Gem::Requirement
|
219
|
-
requirements:
|
220
|
-
- - ">="
|
221
|
-
- !ruby/object:Gem::Version
|
222
|
-
version: '0'
|
223
|
-
- !ruby/object:Gem::Dependency
|
224
|
-
name: bundler
|
225
|
-
requirement: !ruby/object:Gem::Requirement
|
226
|
-
requirements:
|
227
|
-
- - ">="
|
228
|
-
- !ruby/object:Gem::Version
|
229
|
-
version: '0'
|
230
|
-
type: :development
|
231
|
-
prerelease: false
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
requirements:
|
234
|
-
- - ">="
|
235
|
-
- !ruby/object:Gem::Version
|
236
|
-
version: '0'
|
237
|
-
- !ruby/object:Gem::Dependency
|
238
|
-
name: rake
|
239
|
-
requirement: !ruby/object:Gem::Requirement
|
240
|
-
requirements:
|
241
|
-
- - ">="
|
242
|
-
- !ruby/object:Gem::Version
|
243
|
-
version: 12.3.3
|
244
|
-
type: :development
|
245
|
-
prerelease: false
|
246
|
-
version_requirements: !ruby/object:Gem::Requirement
|
247
|
-
requirements:
|
248
|
-
- - ">="
|
249
|
-
- !ruby/object:Gem::Version
|
250
|
-
version: 12.3.3
|
251
|
-
- !ruby/object:Gem::Dependency
|
252
|
-
name: rspec
|
181
|
+
name: base64
|
253
182
|
requirement: !ruby/object:Gem::Requirement
|
254
183
|
requirements:
|
255
184
|
- - "~>"
|
256
185
|
- !ruby/object:Gem::Version
|
257
|
-
version:
|
258
|
-
type: :
|
186
|
+
version: 0.2.0
|
187
|
+
type: :runtime
|
259
188
|
prerelease: false
|
260
189
|
version_requirements: !ruby/object:Gem::Requirement
|
261
190
|
requirements:
|
262
191
|
- - "~>"
|
263
192
|
- !ruby/object:Gem::Version
|
264
|
-
version:
|
193
|
+
version: 0.2.0
|
265
194
|
- !ruby/object:Gem::Dependency
|
266
|
-
name:
|
195
|
+
name: observer
|
267
196
|
requirement: !ruby/object:Gem::Requirement
|
268
197
|
requirements:
|
269
|
-
- - "
|
198
|
+
- - "~>"
|
270
199
|
- !ruby/object:Gem::Version
|
271
|
-
version:
|
272
|
-
type: :
|
200
|
+
version: 0.1.2
|
201
|
+
type: :runtime
|
273
202
|
prerelease: false
|
274
203
|
version_requirements: !ruby/object:Gem::Requirement
|
275
204
|
requirements:
|
276
|
-
- - "
|
205
|
+
- - "~>"
|
277
206
|
- !ruby/object:Gem::Version
|
278
|
-
version:
|
207
|
+
version: 0.1.2
|
279
208
|
- !ruby/object:Gem::Dependency
|
280
|
-
name:
|
209
|
+
name: secp256k1rb
|
281
210
|
requirement: !ruby/object:Gem::Requirement
|
282
211
|
requirements:
|
283
|
-
- -
|
212
|
+
- - '='
|
284
213
|
- !ruby/object:Gem::Version
|
285
|
-
version:
|
286
|
-
type: :
|
214
|
+
version: 0.1.1
|
215
|
+
type: :runtime
|
287
216
|
prerelease: false
|
288
217
|
version_requirements: !ruby/object:Gem::Requirement
|
289
218
|
requirements:
|
290
|
-
- -
|
219
|
+
- - '='
|
291
220
|
- !ruby/object:Gem::Version
|
292
|
-
version:
|
221
|
+
version: 0.1.1
|
293
222
|
- !ruby/object:Gem::Dependency
|
294
|
-
name:
|
223
|
+
name: logger
|
295
224
|
requirement: !ruby/object:Gem::Requirement
|
296
225
|
requirements:
|
297
226
|
- - ">="
|
298
227
|
- !ruby/object:Gem::Version
|
299
|
-
version:
|
300
|
-
type: :
|
228
|
+
version: '0'
|
229
|
+
type: :runtime
|
301
230
|
prerelease: false
|
302
231
|
version_requirements: !ruby/object:Gem::Requirement
|
303
232
|
requirements:
|
304
233
|
- - ">="
|
305
234
|
- !ruby/object:Gem::Version
|
306
|
-
version:
|
235
|
+
version: '0'
|
307
236
|
description: The implementation of Bitcoin Protocol for Ruby.
|
308
237
|
email:
|
309
238
|
- azuchi@chaintope.com
|
@@ -348,6 +277,7 @@ files:
|
|
348
277
|
- lib/bitcoin/chainparams/regtest.yml
|
349
278
|
- lib/bitcoin/chainparams/signet.yml
|
350
279
|
- lib/bitcoin/chainparams/testnet.yml
|
280
|
+
- lib/bitcoin/chainparams/testnet4.yml
|
351
281
|
- lib/bitcoin/constants.rb
|
352
282
|
- lib/bitcoin/descriptor.rb
|
353
283
|
- lib/bitcoin/descriptor/addr.rb
|
@@ -372,7 +302,6 @@ files:
|
|
372
302
|
- lib/bitcoin/ext.rb
|
373
303
|
- lib/bitcoin/ext/array_ext.rb
|
374
304
|
- lib/bitcoin/ext/ecdsa.rb
|
375
|
-
- lib/bitcoin/ext/json_parser.rb
|
376
305
|
- lib/bitcoin/ext/object_ext.rb
|
377
306
|
- lib/bitcoin/ext_key.rb
|
378
307
|
- lib/bitcoin/gcs_filter.rb
|
@@ -507,7 +436,6 @@ homepage: https://github.com/chaintope/bitcoinrb
|
|
507
436
|
licenses:
|
508
437
|
- MIT
|
509
438
|
metadata: {}
|
510
|
-
post_install_message:
|
511
439
|
rdoc_options: []
|
512
440
|
require_paths:
|
513
441
|
- lib
|
@@ -522,8 +450,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
522
450
|
- !ruby/object:Gem::Version
|
523
451
|
version: '0'
|
524
452
|
requirements: []
|
525
|
-
rubygems_version: 3.
|
526
|
-
signing_key:
|
453
|
+
rubygems_version: 3.6.3
|
527
454
|
specification_version: 4
|
528
455
|
summary: The implementation of Bitcoin Protocol for Ruby.
|
529
456
|
test_files: []
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'json/pure'
|
2
|
-
|
3
|
-
module Bitcoin
|
4
|
-
module Ext
|
5
|
-
# Extension of JSON::Pure::Parser.
|
6
|
-
# This class convert Float value to String value.
|
7
|
-
class JsonParser < JSON::Pure::Parser
|
8
|
-
|
9
|
-
def parse_value
|
10
|
-
case
|
11
|
-
when scan(FLOAT)
|
12
|
-
self[1].to_s
|
13
|
-
when scan(INTEGER)
|
14
|
-
Integer(self[1])
|
15
|
-
when scan(TRUE)
|
16
|
-
true
|
17
|
-
when scan(FALSE)
|
18
|
-
false
|
19
|
-
when scan(NULL)
|
20
|
-
nil
|
21
|
-
when !UNPARSED.equal?(string = parse_string)
|
22
|
-
string
|
23
|
-
when scan(ARRAY_OPEN)
|
24
|
-
@current_nesting += 1
|
25
|
-
ary = parse_array
|
26
|
-
@current_nesting -= 1
|
27
|
-
ary
|
28
|
-
when scan(OBJECT_OPEN)
|
29
|
-
@current_nesting += 1
|
30
|
-
obj = parse_object
|
31
|
-
@current_nesting -= 1
|
32
|
-
obj
|
33
|
-
when @allow_nan && scan(NAN)
|
34
|
-
NaN
|
35
|
-
when @allow_nan && scan(INFINITY)
|
36
|
-
Infinity
|
37
|
-
when @allow_nan && scan(MINUS_INFINITY)
|
38
|
-
MinusInfinity
|
39
|
-
else
|
40
|
-
UNPARSED
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|