bitcoinrb 0.3.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.rspec_parallel +2 -0
- data/.ruby-version +1 -1
- data/README.md +17 -6
- data/bitcoinrb.gemspec +9 -8
- data/exe/bitcoinrbd +5 -0
- data/lib/bitcoin.rb +37 -19
- data/lib/bitcoin/bip85_entropy.rb +111 -0
- data/lib/bitcoin/block_filter.rb +14 -0
- data/lib/bitcoin/block_header.rb +2 -0
- data/lib/bitcoin/chain_params.rb +9 -8
- data/lib/bitcoin/chainparams/regtest.yml +1 -1
- data/lib/bitcoin/chainparams/signet.yml +39 -0
- data/lib/bitcoin/chainparams/testnet.yml +1 -1
- data/lib/bitcoin/constants.rb +44 -10
- data/lib/bitcoin/descriptor.rb +1 -1
- data/lib/bitcoin/errors.rb +19 -0
- data/lib/bitcoin/ext.rb +6 -0
- data/lib/bitcoin/ext/array_ext.rb +22 -0
- data/lib/bitcoin/ext/ecdsa.rb +36 -0
- data/lib/bitcoin/ext/json_parser.rb +46 -0
- data/lib/bitcoin/ext_key.rb +51 -20
- data/lib/bitcoin/key.rb +89 -30
- data/lib/bitcoin/key_path.rb +12 -5
- data/lib/bitcoin/message.rb +79 -0
- data/lib/bitcoin/message/addr_v2.rb +34 -0
- data/lib/bitcoin/message/base.rb +17 -0
- data/lib/bitcoin/message/cf_parser.rb +16 -0
- data/lib/bitcoin/message/cfcheckpt.rb +36 -0
- data/lib/bitcoin/message/cfheaders.rb +40 -0
- data/lib/bitcoin/message/cfilter.rb +35 -0
- data/lib/bitcoin/message/fee_filter.rb +1 -1
- data/lib/bitcoin/message/filter_load.rb +3 -3
- data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
- data/lib/bitcoin/message/get_cfheaders.rb +24 -0
- data/lib/bitcoin/message/get_cfilters.rb +25 -0
- data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
- data/lib/bitcoin/message/inventory.rb +1 -1
- data/lib/bitcoin/message/merkle_block.rb +1 -1
- data/lib/bitcoin/message/network_addr.rb +141 -18
- data/lib/bitcoin/message/ping.rb +1 -1
- data/lib/bitcoin/message/pong.rb +1 -1
- data/lib/bitcoin/message/send_addr_v2.rb +13 -0
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/message/tx.rb +1 -1
- data/lib/bitcoin/message/version.rb +7 -0
- data/lib/bitcoin/message_sign.rb +47 -0
- data/lib/bitcoin/mnemonic.rb +7 -7
- data/lib/bitcoin/network/peer.rb +9 -4
- data/lib/bitcoin/network/peer_discovery.rb +1 -1
- data/lib/bitcoin/node/cli.rb +14 -10
- data/lib/bitcoin/node/configuration.rb +3 -1
- data/lib/bitcoin/node/spv.rb +9 -1
- data/lib/bitcoin/opcodes.rb +14 -1
- data/lib/bitcoin/out_point.rb +2 -0
- data/lib/bitcoin/payment_code.rb +92 -0
- data/lib/bitcoin/payments/payment.pb.rb +1 -1
- data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
- data/lib/bitcoin/psbt/input.rb +9 -18
- data/lib/bitcoin/psbt/output.rb +1 -1
- data/lib/bitcoin/psbt/tx.rb +12 -17
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
- data/lib/bitcoin/rpc/request_handler.rb +5 -5
- data/lib/bitcoin/script/script.rb +96 -39
- data/lib/bitcoin/script/script_error.rb +27 -1
- data/lib/bitcoin/script/script_interpreter.rb +166 -66
- data/lib/bitcoin/script/tx_checker.rb +62 -14
- data/lib/bitcoin/secp256k1.rb +1 -0
- data/lib/bitcoin/secp256k1/native.rb +184 -17
- data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
- data/lib/bitcoin/secp256k1/ruby.rb +112 -56
- data/lib/bitcoin/sighash_generator.rb +156 -0
- data/lib/bitcoin/store.rb +1 -0
- data/lib/bitcoin/store/chain_entry.rb +1 -0
- data/lib/bitcoin/store/utxo_db.rb +226 -0
- data/lib/bitcoin/taproot.rb +9 -0
- data/lib/bitcoin/taproot/leaf_node.rb +23 -0
- data/lib/bitcoin/taproot/simple_builder.rb +139 -0
- data/lib/bitcoin/tx.rb +34 -104
- data/lib/bitcoin/tx_in.rb +4 -5
- data/lib/bitcoin/tx_out.rb +2 -3
- data/lib/bitcoin/util.rb +22 -6
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet.rb +1 -0
- data/lib/bitcoin/wallet/account.rb +2 -1
- data/lib/bitcoin/wallet/base.rb +2 -2
- data/lib/bitcoin/wallet/master_key.rb +1 -0
- data/lib/bitcoin/wallet/utxo.rb +37 -0
- metadata +86 -32
- data/.travis.yml +0 -11
@@ -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
|
|
@@ -68,7 +73,8 @@ module Bitcoin
|
|
68
73
|
# for script
|
69
74
|
|
70
75
|
# witness version
|
71
|
-
|
76
|
+
WITNESS_VERSION_V0 = 0x00
|
77
|
+
WITNESS_VERSION_V1 = Bitcoin::Opcodes::OP_1
|
72
78
|
|
73
79
|
# Maximum script length in bytes
|
74
80
|
MAX_SCRIPT_SIZE = 10000
|
@@ -88,15 +94,19 @@ module Bitcoin
|
|
88
94
|
# Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
|
89
95
|
LOCKTIME_THRESHOLD = 500000000
|
90
96
|
|
91
|
-
#
|
92
|
-
|
97
|
+
# Tag for input annex. If there are at least two witness elements for a transaction input,
|
98
|
+
# and the first byte of the last element is 0x50, this last element is called annex, and
|
99
|
+
# has meanings independent of the script
|
100
|
+
ANNEX_TAG = 0x50
|
101
|
+
|
102
|
+
# Validation weight per passing signature (Tapscript only, see BIP 342).
|
103
|
+
VALIDATION_WEIGHT_PER_SIGOP_PASSED = 50
|
93
104
|
|
94
|
-
#
|
95
|
-
|
105
|
+
# How much weight budget is added to the witness size (Tapscript only, see BIP 342).
|
106
|
+
VALIDATION_WEIGHT_OFFSET = 50
|
96
107
|
|
97
|
-
#
|
98
|
-
|
99
|
-
FORK_ID_GOLD = 79
|
108
|
+
# Signature hash types/flags
|
109
|
+
SIGHASH_TYPE = { all: 0x01, none: 0x02, single: 0x3, anyonecanpay: 0x80 , default: 0}
|
100
110
|
|
101
111
|
# Maximum number length in bytes
|
102
112
|
DEFAULT_MAX_NUM_SIZE = 4
|
@@ -104,8 +114,6 @@ module Bitcoin
|
|
104
114
|
# 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
|
105
115
|
MAX_OP_RETURN_RELAY = 83
|
106
116
|
|
107
|
-
SIG_VERSION = [:base, :witness_v0]
|
108
|
-
|
109
117
|
# for script error
|
110
118
|
SCRIPT_ERR_OK = 0
|
111
119
|
SCRIPT_ERR_UNKNOWN_ERROR = 1
|
@@ -153,6 +161,10 @@ module Bitcoin
|
|
153
161
|
# softfork safeness
|
154
162
|
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
|
155
163
|
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 61
|
164
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = 62
|
165
|
+
SCRIPT_ERR_DISCOURAGE_UNKNOWN_ANNEX = 63
|
166
|
+
SCRIPT_ERR_DISCOURAGE_OP_SUCCESS = 64
|
167
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = 65
|
156
168
|
|
157
169
|
# segregated witness
|
158
170
|
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
|
@@ -169,6 +181,15 @@ module Bitcoin
|
|
169
181
|
|
170
182
|
SCRIPT_ERR_ERROR_COUNT = 80
|
171
183
|
|
184
|
+
# Taproot
|
185
|
+
SCRIPT_ERR_SCHNORR_SIG_SIZE = 90
|
186
|
+
SCRIPT_ERR_SCHNORR_SIG_HASHTYPE = 91
|
187
|
+
SCRIPT_ERR_SCHNORR_SIG = 92
|
188
|
+
SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE = 93
|
189
|
+
SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT = 94
|
190
|
+
SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG = 95
|
191
|
+
SCRIPT_ERR_TAPSCRIPT_MINIMALIF = 96
|
192
|
+
|
172
193
|
ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
|
173
194
|
NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
|
174
195
|
|
@@ -192,4 +213,17 @@ module Bitcoin
|
|
192
213
|
BIP32_EXTKEY_WITH_VERSION_SIZE = 78
|
193
214
|
|
194
215
|
HARDENED_THRESHOLD = 2147483648 # 2**31
|
216
|
+
|
217
|
+
# Signature hash sizes
|
218
|
+
WITNESS_V0_SCRIPTHASH_SIZE = 32
|
219
|
+
WITNESS_V0_KEYHASH_SIZE = 20
|
220
|
+
WITNESS_V1_TAPROOT_SIZE = 32
|
221
|
+
|
222
|
+
TAPROOT_LEAF_MASK = 0xfe
|
223
|
+
TAPROOT_LEAF_TAPSCRIPT = 0xc0
|
224
|
+
TAPROOT_CONTROL_BASE_SIZE = 33
|
225
|
+
TAPROOT_CONTROL_NODE_SIZE = 32
|
226
|
+
TAPROOT_CONTROL_MAX_NODE_COUNT = 128
|
227
|
+
TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT
|
228
|
+
|
195
229
|
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,22 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Ext
|
3
|
+
module ArrayExt
|
4
|
+
|
5
|
+
refine Array do
|
6
|
+
|
7
|
+
# resize array content with +initial_value+.
|
8
|
+
# expect to behave like vec#resize in c++.
|
9
|
+
def resize!(new_size, initial_value = 0)
|
10
|
+
if size < new_size
|
11
|
+
(new_size - size).times{self.<< initial_value}
|
12
|
+
elsif size > new_size
|
13
|
+
(size - new_size).times{delete_at(-1)}
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class ::ECDSA::Signature
|
2
|
+
# convert signature to der string.
|
3
|
+
def to_der
|
4
|
+
ECDSA::Format::SignatureDerString.encode(self)
|
5
|
+
end
|
6
|
+
|
7
|
+
def ==(other)
|
8
|
+
return false unless other.is_a?(ECDSA::Signature)
|
9
|
+
r == other.r && s == other.s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ::ECDSA::Point
|
14
|
+
def to_hex(compression = true)
|
15
|
+
ECDSA::Format::PointOctetString.encode(self, compression: compression).bth
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ::ECDSA::Format::PointOctetString
|
20
|
+
|
21
|
+
class << self
|
22
|
+
alias_method :base_decode, :decode
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.decode(string, group, allow_hybrid: false)
|
26
|
+
string = string.dup.force_encoding('BINARY')
|
27
|
+
raise ECDSA::Format::DecodeError, 'Point octet string is empty.' if string.empty?
|
28
|
+
if [6, 7].include?(string[0].ord)
|
29
|
+
raise ECDSA::Format::DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord unless allow_hybrid
|
30
|
+
decode_uncompressed string, group if allow_hybrid
|
31
|
+
else
|
32
|
+
base_decode(string, group)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
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)
|
@@ -263,9 +280,9 @@ module Bitcoin
|
|
263
280
|
l = Bitcoin.hmac_sha512(chain_code, data)
|
264
281
|
left = l[0..31].bth.to_i(16)
|
265
282
|
raise 'invalid key' if left >= CURVE_ORDER
|
266
|
-
p1 = Bitcoin::
|
283
|
+
p1 = Bitcoin::Key.new(priv_key: left.to_s(16), key_type: Bitcoin::Key::TYPES[:uncompressed]).to_point
|
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.
|