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
@@ -9,7 +9,7 @@ privkey_version: "ef"
|
|
9
9
|
extended_privkey_version: "04358394"
|
10
10
|
extended_pubkey_version: "043587cf"
|
11
11
|
bip49_pubkey_p2wpkh_p2sh_version: "044a5262"
|
12
|
-
bip49_pubkey_p2wsh_p2sh_version: "
|
12
|
+
bip49_pubkey_p2wsh_p2sh_version: "024289ef"
|
13
13
|
bip49_privkey_p2wpkh_p2sh_version: "044a4e28"
|
14
14
|
bip49_privkey_p2wsh_p2sh_version: "024285b5"
|
15
15
|
bip84_pubkey_p2wpkh_version: "045f1cf6"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
--- !ruby/object:Bitcoin::ChainParams
|
2
|
+
network: "signet"
|
3
|
+
magic_head: "0a03cf40"
|
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: 38333
|
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: 227931
|
26
|
+
genesis_hash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
|
27
|
+
proof_of_work_limit: 0x1d00ffff
|
28
|
+
dns_seeds:
|
29
|
+
- "178.128.221.177"
|
30
|
+
- "2a01:7c8:d005:390::5"
|
31
|
+
genesis:
|
32
|
+
hash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
|
33
|
+
merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
34
|
+
time: 1598918400
|
35
|
+
nonce: 52613770
|
36
|
+
bits: 0x1e0377ae
|
37
|
+
version: 1
|
38
|
+
prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
39
|
+
bip44_coin_type: 1
|
@@ -9,7 +9,7 @@ privkey_version: "ef"
|
|
9
9
|
extended_privkey_version: "04358394"
|
10
10
|
extended_pubkey_version: "043587cf"
|
11
11
|
bip49_pubkey_p2wpkh_p2sh_version: "044a5262"
|
12
|
-
bip49_pubkey_p2wsh_p2sh_version: "
|
12
|
+
bip49_pubkey_p2wsh_p2sh_version: "024289ef"
|
13
13
|
bip49_privkey_p2wpkh_p2sh_version: "044a4e28"
|
14
14
|
bip49_privkey_p2wsh_p2sh_version: "024285b5"
|
15
15
|
bip84_pubkey_p2wpkh_version: "045f1cf6"
|
data/lib/bitcoin/constants.rb
CHANGED
@@ -44,6 +44,11 @@ module Bitcoin
|
|
44
44
|
SCRIPT_VERIFY_NULLFAIL = (1 << 14) # Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
|
45
45
|
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1 << 15) # Public keys in segregated witness scripts must be compressed
|
46
46
|
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1 << 16) # Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
|
47
|
+
SCRIPT_VERIFY_TAPROOT = (1 << 17) # Taproot/Tapscript validation (BIPs 341 & 342)
|
48
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1 << 18) # Making unknown Taproot leaf versions non-standard
|
49
|
+
SCRIPT_VERIFY_DISCOURAGE_UNKNOWN_ANNEX = (1 << 19) # Making the use of (unknown) annexes non-standard (currently no annexes are known)
|
50
|
+
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1 << 20) # Making unknown OP_SUCCESS non-standard
|
51
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1 << 21) # Making unknown public key versions (in BIP 342 scripts) non-standard
|
47
52
|
|
48
53
|
MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
|
49
54
|
|
@@ -88,15 +93,19 @@ module Bitcoin
|
|
88
93
|
# Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
|
89
94
|
LOCKTIME_THRESHOLD = 500000000
|
90
95
|
|
91
|
-
#
|
92
|
-
|
96
|
+
# Tag for input annex. If there are at least two witness elements for a transaction input,
|
97
|
+
# and the first byte of the last element is 0x50, this last element is called annex, and
|
98
|
+
# has meanings independent of the script
|
99
|
+
ANNEX_TAG = 0x50
|
100
|
+
|
101
|
+
# Validation weight per passing signature (Tapscript only, see BIP 342).
|
102
|
+
VALIDATION_WEIGHT_PER_SIGOP_PASSED = 50
|
93
103
|
|
94
|
-
#
|
95
|
-
|
104
|
+
# How much weight budget is added to the witness size (Tapscript only, see BIP 342).
|
105
|
+
VALIDATION_WEIGHT_OFFSET = 50
|
96
106
|
|
97
|
-
#
|
98
|
-
|
99
|
-
FORK_ID_GOLD = 79
|
107
|
+
# Signature hash types/flags
|
108
|
+
SIGHASH_TYPE = { all: 0x01, none: 0x02, single: 0x3, anyonecanpay: 0x80 , default: 0}
|
100
109
|
|
101
110
|
# Maximum number length in bytes
|
102
111
|
DEFAULT_MAX_NUM_SIZE = 4
|
@@ -104,8 +113,6 @@ module Bitcoin
|
|
104
113
|
# 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
|
105
114
|
MAX_OP_RETURN_RELAY = 83
|
106
115
|
|
107
|
-
SIG_VERSION = [:base, :witness_v0]
|
108
|
-
|
109
116
|
# for script error
|
110
117
|
SCRIPT_ERR_OK = 0
|
111
118
|
SCRIPT_ERR_UNKNOWN_ERROR = 1
|
@@ -146,13 +153,17 @@ module Bitcoin
|
|
146
153
|
SCRIPT_ERR_SIG_HIGH_S = 54
|
147
154
|
SCRIPT_ERR_SIG_NULLDUMMY = 55
|
148
155
|
SCRIPT_ERR_PUBKEYTYPE = 56
|
149
|
-
SCRIPT_ERR_CLEANSTACK =
|
150
|
-
SCRIPT_ERR_MINIMALIF =
|
151
|
-
SCRIPT_ERR_SIG_NULLFAIL =
|
156
|
+
SCRIPT_ERR_CLEANSTACK = 57
|
157
|
+
SCRIPT_ERR_MINIMALIF = 58
|
158
|
+
SCRIPT_ERR_SIG_NULLFAIL = 59
|
152
159
|
|
153
160
|
# softfork safeness
|
154
161
|
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
|
155
162
|
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 61
|
163
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = 62
|
164
|
+
SCRIPT_ERR_DISCOURAGE_UNKNOWN_ANNEX = 63
|
165
|
+
SCRIPT_ERR_DISCOURAGE_OP_SUCCESS = 64
|
166
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = 65
|
156
167
|
|
157
168
|
# segregated witness
|
158
169
|
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
|
@@ -169,6 +180,15 @@ module Bitcoin
|
|
169
180
|
|
170
181
|
SCRIPT_ERR_ERROR_COUNT = 80
|
171
182
|
|
183
|
+
# Taproot
|
184
|
+
SCRIPT_ERR_SCHNORR_SIG_SIZE = 90
|
185
|
+
SCRIPT_ERR_SCHNORR_SIG_HASHTYPE = 91
|
186
|
+
SCRIPT_ERR_SCHNORR_SIG = 92
|
187
|
+
SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE = 93
|
188
|
+
SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT = 94
|
189
|
+
SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG = 95
|
190
|
+
SCRIPT_ERR_TAPSCRIPT_MINIMALIF = 96
|
191
|
+
|
172
192
|
ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
|
173
193
|
NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
|
174
194
|
|
@@ -192,4 +212,17 @@ module Bitcoin
|
|
192
212
|
BIP32_EXTKEY_WITH_VERSION_SIZE = 78
|
193
213
|
|
194
214
|
HARDENED_THRESHOLD = 2147483648 # 2**31
|
215
|
+
|
216
|
+
# Signature hash sizes
|
217
|
+
WITNESS_V0_SCRIPTHASH_SIZE = 32
|
218
|
+
WITNESS_V0_KEYHASH_SIZE = 20
|
219
|
+
WITNESS_V1_TAPROOT_SIZE = 32
|
220
|
+
|
221
|
+
TAPROOT_LEAF_MASK = 0xfe
|
222
|
+
TAPROOT_LEAF_TAPSCRIPT = 0xc0
|
223
|
+
TAPROOT_CONTROL_BASE_SIZE = 33
|
224
|
+
TAPROOT_CONTROL_NODE_SIZE = 32
|
225
|
+
TAPROOT_CONTROL_MAX_NODE_COUNT = 128
|
226
|
+
TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT
|
227
|
+
|
195
228
|
end
|
data/lib/bitcoin/descriptor.rb
CHANGED
@@ -116,7 +116,7 @@ module Bitcoin
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
key = key.is_a?(Bitcoin::Key) ? key : key.key
|
119
|
-
raise ArgumentError,
|
119
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless key.fully_valid_pubkey?
|
120
120
|
key.pubkey
|
121
121
|
end
|
122
122
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Errors
|
3
|
+
|
4
|
+
module Messages
|
5
|
+
|
6
|
+
INVALID_PUBLIC_KEY = 'Invalid public key.'
|
7
|
+
INVALID_BIP32_PRIV_PREFIX = 'Invalid BIP32 private key prefix. prefix must be 0x00.'
|
8
|
+
INVALID_BIP32_FINGERPRINT = 'Invalid parent fingerprint.'
|
9
|
+
INVALID_BIP32_ZERO_INDEX = 'Invalid index. Depth 0 must have 0 index.'
|
10
|
+
INVALID_BIP32_ZERO_DEPTH = 'Invalid depth. Master key must have 0 depth.'
|
11
|
+
INVALID_BIP32_VERSION = 'An unsupported version byte was specified.'
|
12
|
+
|
13
|
+
INVALID_PRIV_KEY = 'Private key is not in range [1..n-1].'
|
14
|
+
INVALID_CHECKSUM = 'Invalid checksum.'
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/bitcoin/ext.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
class ::ECDSA::Signature
|
2
|
+
# convert signature to der string.
|
3
|
+
def to_der
|
4
|
+
ECDSA::Format::SignatureDerString.encode(self)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class ::ECDSA::Point
|
9
|
+
def to_hex(compression = true)
|
10
|
+
ECDSA::Format::PointOctetString.encode(self, compression: compression).bth
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ::ECDSA::Format::PointOctetString
|
15
|
+
|
16
|
+
class << self
|
17
|
+
alias_method :base_decode, :decode
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.decode(string, group, allow_hybrid: false)
|
21
|
+
string = string.dup.force_encoding('BINARY')
|
22
|
+
raise ECDSA::Format::DecodeError, 'Point octet string is empty.' if string.empty?
|
23
|
+
if [6, 7].include?(string[0].ord)
|
24
|
+
raise ECDSA::Format::DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord unless allow_hybrid
|
25
|
+
decode_uncompressed string, group if allow_hybrid
|
26
|
+
else
|
27
|
+
base_decode(string, group)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
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
|
data/lib/bitcoin/ext_key.rb
CHANGED
@@ -6,6 +6,11 @@ module Bitcoin
|
|
6
6
|
# BIP32 Extended private key
|
7
7
|
class ExtKey
|
8
8
|
|
9
|
+
include Bitcoin::HexConverter
|
10
|
+
|
11
|
+
MAX_DEPTH = 255
|
12
|
+
MASTER_FINGERPRINT = '00000000'
|
13
|
+
|
9
14
|
attr_accessor :ver
|
10
15
|
attr_accessor :depth
|
11
16
|
attr_accessor :number
|
@@ -18,7 +23,7 @@ module Bitcoin
|
|
18
23
|
def self.generate_master(seed)
|
19
24
|
ext_key = ExtKey.new
|
20
25
|
ext_key.depth = ext_key.number = 0
|
21
|
-
ext_key.parent_fingerprint =
|
26
|
+
ext_key.parent_fingerprint = MASTER_FINGERPRINT
|
22
27
|
l = Bitcoin.hmac_sha512('Bitcoin seed', seed.htb)
|
23
28
|
left = l[0..31].bth.to_i(16)
|
24
29
|
raise 'invalid key' if left >= CURVE_ORDER || left == 0
|
@@ -47,9 +52,7 @@ module Bitcoin
|
|
47
52
|
|
48
53
|
# Base58 encoded extended private key
|
49
54
|
def to_base58
|
50
|
-
|
51
|
-
hex = h + Bitcoin.calc_checksum(h)
|
52
|
-
Base58.encode(hex)
|
55
|
+
ExtPubkey.encode_base58(to_hex)
|
53
56
|
end
|
54
57
|
|
55
58
|
# get private key(hex)
|
@@ -94,6 +97,7 @@ module Bitcoin
|
|
94
97
|
number += Bitcoin::HARDENED_THRESHOLD if harden
|
95
98
|
new_key = ExtKey.new
|
96
99
|
new_key.depth = depth + 1
|
100
|
+
raise IndexError, 'Depth over 255.' if new_key.depth > MAX_DEPTH
|
97
101
|
new_key.number = number
|
98
102
|
new_key.parent_fingerprint = fingerprint
|
99
103
|
if number > (Bitcoin::HARDENED_THRESHOLD - 1)
|
@@ -140,19 +144,24 @@ module Bitcoin
|
|
140
144
|
buf = StringIO.new(payload)
|
141
145
|
ext_key = ExtKey.new
|
142
146
|
ext_key.ver = buf.read(4).bth # version
|
143
|
-
raise
|
144
|
-
ext_key.depth = buf.read(1).
|
147
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtKey.support_version?(ext_key.ver)
|
148
|
+
ext_key.depth = buf.read(1).unpack1('C')
|
145
149
|
ext_key.parent_fingerprint = buf.read(4).bth
|
146
|
-
ext_key.number = buf.read(4).
|
150
|
+
ext_key.number = buf.read(4).unpack1('N')
|
151
|
+
if ext_key.depth == 0
|
152
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT unless ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
153
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_INDEX if ext_key.number > 0
|
154
|
+
end
|
155
|
+
raise ArgumentError, Errors::Messages:: INVALID_BIP32_ZERO_DEPTH if ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_key.depth > 0
|
147
156
|
ext_key.chain_code = buf.read(32)
|
148
|
-
buf.read(1) # 0x00
|
157
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth == '00' # 0x00
|
149
158
|
ext_key.key = Bitcoin::Key.new(priv_key: buf.read(32).bth, key_type: Bitcoin::Key::TYPES[:compressed])
|
150
159
|
ext_key
|
151
160
|
end
|
152
161
|
|
153
162
|
# import private key from Base58 private key address
|
154
|
-
def self.from_base58(
|
155
|
-
ExtKey.parse_from_payload(
|
163
|
+
def self.from_base58(base58)
|
164
|
+
ExtKey.parse_from_payload(ExtPubkey.validate_base58(base58))
|
156
165
|
end
|
157
166
|
|
158
167
|
# get version bytes from purpose' value.
|
@@ -191,6 +200,8 @@ module Bitcoin
|
|
191
200
|
# BIP-32 Extended public key
|
192
201
|
class ExtPubkey
|
193
202
|
|
203
|
+
include Bitcoin::HexConverter
|
204
|
+
|
194
205
|
attr_accessor :ver
|
195
206
|
attr_accessor :depth
|
196
207
|
attr_accessor :number
|
@@ -242,9 +253,14 @@ module Bitcoin
|
|
242
253
|
|
243
254
|
# Base58 encoded extended pubkey
|
244
255
|
def to_base58
|
245
|
-
|
246
|
-
|
247
|
-
|
256
|
+
ExtPubkey.encode_base58(to_hex)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Generate Base58 encoded key from BIP32 payload with hex format.
|
260
|
+
# @param [String] hex BIP32 payload with hex format.
|
261
|
+
# @return [String] Base58 encoded extended key.
|
262
|
+
def self.encode_base58(hex)
|
263
|
+
Base58.encode(hex + Bitcoin.calc_checksum(hex))
|
248
264
|
end
|
249
265
|
|
250
266
|
# whether hardened key.
|
@@ -256,6 +272,7 @@ module Bitcoin
|
|
256
272
|
def derive(number)
|
257
273
|
new_key = ExtPubkey.new
|
258
274
|
new_key.depth = depth + 1
|
275
|
+
raise IndexError, 'Depth over 255.' if new_key.depth > Bitcoin::ExtKey::MAX_DEPTH
|
259
276
|
new_key.number = number
|
260
277
|
new_key.parent_fingerprint = fingerprint
|
261
278
|
raise 'hardened key is not support' if number > (Bitcoin::HARDENED_THRESHOLD - 1)
|
@@ -265,7 +282,7 @@ module Bitcoin
|
|
265
282
|
raise 'invalid key' if left >= CURVE_ORDER
|
266
283
|
p1 = Bitcoin::Secp256k1::GROUP.generator.multiply_by_scalar(left)
|
267
284
|
p2 = Bitcoin::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
268
|
-
new_key.pubkey =
|
285
|
+
new_key.pubkey = (p1 + p2).to_hex
|
269
286
|
new_key.chain_code = l[32..-1]
|
270
287
|
new_key.ver = version
|
271
288
|
new_key
|
@@ -298,19 +315,33 @@ module Bitcoin
|
|
298
315
|
buf = StringIO.new(payload)
|
299
316
|
ext_pubkey = ExtPubkey.new
|
300
317
|
ext_pubkey.ver = buf.read(4).bth # version
|
301
|
-
raise
|
302
|
-
ext_pubkey.depth = buf.read(1).
|
318
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtPubkey.support_version?(ext_pubkey.ver)
|
319
|
+
ext_pubkey.depth = buf.read(1).unpack1('C')
|
303
320
|
ext_pubkey.parent_fingerprint = buf.read(4).bth
|
304
|
-
ext_pubkey.number = buf.read(4).
|
321
|
+
ext_pubkey.number = buf.read(4).unpack1('N')
|
322
|
+
if ext_pubkey.depth == 0
|
323
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT unless ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
324
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_INDEX if ext_pubkey.number > 0
|
325
|
+
end
|
326
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_DEPTH if ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_pubkey.depth > 0
|
305
327
|
ext_pubkey.chain_code = buf.read(32)
|
306
|
-
ext_pubkey.pubkey = buf.read(33).bth
|
328
|
+
ext_pubkey.pubkey = Bitcoin::Key.new(pubkey: buf.read(33).bth).pubkey
|
307
329
|
ext_pubkey
|
308
330
|
end
|
309
331
|
|
310
332
|
|
311
333
|
# import pub key from Base58 private key address
|
312
334
|
def self.from_base58(address)
|
313
|
-
ExtPubkey.parse_from_payload(
|
335
|
+
ExtPubkey.parse_from_payload(ExtPubkey.validate_base58(address))
|
336
|
+
end
|
337
|
+
|
338
|
+
# Validate address checksum and return payload.
|
339
|
+
# @param [String] BIP32 Base58 address
|
340
|
+
# @return [String] BIP32 payload with binary format
|
341
|
+
def self.validate_base58(address)
|
342
|
+
raw = Base58.decode(address)
|
343
|
+
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(raw[0...-8]) == raw[-8..-1]
|
344
|
+
raw[0...-8].htb
|
314
345
|
end
|
315
346
|
|
316
347
|
# get version bytes from purpose' value.
|
data/lib/bitcoin/key.rb
CHANGED
@@ -28,7 +28,7 @@ module Bitcoin
|
|
28
28
|
# @param [Integer] key_type a key type which determine address type.
|
29
29
|
# @param [Boolean] compressed [Deprecated] whether public key is compressed.
|
30
30
|
# @return [Bitcoin::Key] a key object.
|
31
|
-
def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true)
|
31
|
+
def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false)
|
32
32
|
puts "[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future." if key_type.nil? && !compressed.nil? && pubkey.nil?
|
33
33
|
if key_type
|
34
34
|
@key_type = key_type
|
@@ -39,13 +39,14 @@ module Bitcoin
|
|
39
39
|
@secp256k1_module = Bitcoin.secp_impl
|
40
40
|
@priv_key = priv_key
|
41
41
|
if @priv_key
|
42
|
-
raise ArgumentError,
|
42
|
+
raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
|
43
43
|
end
|
44
44
|
if pubkey
|
45
45
|
@pubkey = pubkey
|
46
46
|
else
|
47
47
|
@pubkey = generate_pubkey(priv_key, compressed: compressed) if priv_key
|
48
48
|
end
|
49
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless fully_valid_pubkey?(allow_hybrid)
|
49
50
|
end
|
50
51
|
|
51
52
|
# generate key pair
|
@@ -63,9 +64,9 @@ module Bitcoin
|
|
63
64
|
data = hex[2...-8].htb
|
64
65
|
checksum = hex[-8..-1]
|
65
66
|
raise ArgumentError, 'invalid version' unless version == Bitcoin.chain_params.privkey_version
|
66
|
-
raise ArgumentError,
|
67
|
+
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(version + data.bth) == checksum
|
67
68
|
key_len = data.bytesize
|
68
|
-
if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].
|
69
|
+
if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack1('C') == 1
|
69
70
|
key_type = TYPES[:compressed]
|
70
71
|
data = data[0..-2]
|
71
72
|
elsif key_len == 32
|
@@ -88,30 +89,46 @@ module Bitcoin
|
|
88
89
|
# sign +data+ with private key
|
89
90
|
# @param [String] data a data to be signed with binary format
|
90
91
|
# @param [Boolean] low_r flag to apply low-R.
|
91
|
-
# @param [String] extra_entropy the extra entropy for rfc6979.
|
92
|
+
# @param [String] extra_entropy the extra entropy with binary format for rfc6979.
|
93
|
+
# @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
|
92
94
|
# @return [String] signature data with binary format
|
93
|
-
def sign(data, low_r = true, extra_entropy = nil)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
def sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa)
|
96
|
+
case algo
|
97
|
+
when :ecdsa
|
98
|
+
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
|
99
|
+
if low_r && !sig_has_low_r?(sig)
|
100
|
+
counter = 1
|
101
|
+
until sig_has_low_r?(sig)
|
102
|
+
extra_entropy = [counter].pack('I*').bth.ljust(64, '0').htb
|
103
|
+
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
|
104
|
+
counter += 1
|
105
|
+
end
|
101
106
|
end
|
107
|
+
sig
|
108
|
+
when :schnorr
|
109
|
+
secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :schnorr)
|
110
|
+
else
|
111
|
+
raise ArgumentError "Unsupported algo specified: #{algo}"
|
102
112
|
end
|
103
|
-
sig
|
104
113
|
end
|
105
114
|
|
106
115
|
# verify signature using public key
|
107
116
|
# @param [String] sig signature data with binary format
|
108
|
-
# @param [String]
|
117
|
+
# @param [String] data original message
|
118
|
+
# @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
|
109
119
|
# @return [Boolean] verify result
|
110
|
-
def verify(sig,
|
120
|
+
def verify(sig, data, algo: :ecdsa)
|
111
121
|
return false unless valid_pubkey?
|
112
122
|
begin
|
113
|
-
|
114
|
-
|
123
|
+
case algo
|
124
|
+
when :ecdsa
|
125
|
+
sig = ecdsa_signature_parse_der_lax(sig)
|
126
|
+
secp256k1_module.verify_sig(data, sig, pubkey)
|
127
|
+
when :schnorr
|
128
|
+
secp256k1_module.verify_sig(data, sig, xonly_pubkey, algo: :schnorr)
|
129
|
+
else
|
130
|
+
false
|
131
|
+
end
|
115
132
|
rescue Exception
|
116
133
|
false
|
117
134
|
end
|
@@ -125,19 +142,19 @@ module Bitcoin
|
|
125
142
|
# get pay to pubkey hash address
|
126
143
|
# @deprecated
|
127
144
|
def to_p2pkh
|
128
|
-
Bitcoin::Script.to_p2pkh(hash160).
|
145
|
+
Bitcoin::Script.to_p2pkh(hash160).to_addr
|
129
146
|
end
|
130
147
|
|
131
148
|
# get pay to witness pubkey hash address
|
132
149
|
# @deprecated
|
133
150
|
def to_p2wpkh
|
134
|
-
Bitcoin::Script.to_p2wpkh(hash160).
|
151
|
+
Bitcoin::Script.to_p2wpkh(hash160).to_addr
|
135
152
|
end
|
136
153
|
|
137
154
|
# get p2wpkh address nested in p2sh.
|
138
155
|
# @deprecated
|
139
156
|
def to_nested_p2wpkh
|
140
|
-
Bitcoin::Script.to_p2wpkh(hash160).to_p2sh.
|
157
|
+
Bitcoin::Script.to_p2wpkh(hash160).to_p2sh.to_addr
|
141
158
|
end
|
142
159
|
|
143
160
|
def compressed?
|
@@ -152,6 +169,12 @@ module Bitcoin
|
|
152
169
|
ECDSA::Format::PointOctetString.decode(p.htb, Bitcoin::Secp256k1::GROUP)
|
153
170
|
end
|
154
171
|
|
172
|
+
# get xonly public key (32 bytes).
|
173
|
+
# @return [String] xonly public key with hex format
|
174
|
+
def xonly_pubkey
|
175
|
+
pubkey[2..65]
|
176
|
+
end
|
177
|
+
|
155
178
|
# check +pubkey+ (hex) is compress or uncompress pubkey.
|
156
179
|
def self.compress_or_uncompress_pubkey?(pubkey)
|
157
180
|
p = pubkey.htb
|
@@ -225,14 +248,8 @@ module Bitcoin
|
|
225
248
|
end
|
226
249
|
|
227
250
|
# fully validate whether this is a valid public key (more expensive than IsValid())
|
228
|
-
def fully_valid_pubkey?
|
229
|
-
|
230
|
-
begin
|
231
|
-
point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1)
|
232
|
-
ECDSA::Group::Secp256k1.valid_public_key?(point)
|
233
|
-
rescue ECDSA::Format::DecodeError
|
234
|
-
false
|
235
|
-
end
|
251
|
+
def fully_valid_pubkey?(allow_hybrid = false)
|
252
|
+
valid_pubkey? && secp256k1_module.parse_ec_pubkey?(pubkey, allow_hybrid)
|
236
253
|
end
|
237
254
|
|
238
255
|
private
|