bitcoinrb 0.5.0 → 0.6.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +5 -4
  4. data/README.md +10 -0
  5. data/bitcoinrb.gemspec +4 -4
  6. data/lib/bitcoin.rb +29 -16
  7. data/lib/bitcoin/block_filter.rb +14 -0
  8. data/lib/bitcoin/chain_params.rb +9 -0
  9. data/lib/bitcoin/chainparams/signet.yml +39 -0
  10. data/lib/bitcoin/constants.rb +43 -3
  11. data/lib/bitcoin/descriptor.rb +1 -1
  12. data/lib/bitcoin/errors.rb +19 -0
  13. data/lib/bitcoin/ext/ecdsa.rb +31 -0
  14. data/lib/bitcoin/ext_key.rb +35 -19
  15. data/lib/bitcoin/key.rb +43 -26
  16. data/lib/bitcoin/message/cfcheckpt.rb +2 -2
  17. data/lib/bitcoin/message/cfheaders.rb +1 -1
  18. data/lib/bitcoin/message/cfilter.rb +1 -1
  19. data/lib/bitcoin/message/fee_filter.rb +1 -1
  20. data/lib/bitcoin/message/filter_load.rb +3 -3
  21. data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
  22. data/lib/bitcoin/message/inventory.rb +1 -1
  23. data/lib/bitcoin/message/merkle_block.rb +1 -1
  24. data/lib/bitcoin/message/network_addr.rb +3 -3
  25. data/lib/bitcoin/message/ping.rb +1 -1
  26. data/lib/bitcoin/message/pong.rb +1 -1
  27. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  28. data/lib/bitcoin/mnemonic.rb +2 -2
  29. data/lib/bitcoin/network/peer_discovery.rb +1 -3
  30. data/lib/bitcoin/node/configuration.rb +3 -1
  31. data/lib/bitcoin/node/spv.rb +8 -0
  32. data/lib/bitcoin/opcodes.rb +14 -1
  33. data/lib/bitcoin/payment_code.rb +2 -2
  34. data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
  35. data/lib/bitcoin/psbt/input.rb +3 -3
  36. data/lib/bitcoin/psbt/output.rb +1 -1
  37. data/lib/bitcoin/psbt/tx.rb +4 -4
  38. data/lib/bitcoin/rpc/bitcoin_core_client.rb +1 -1
  39. data/lib/bitcoin/script/script.rb +52 -19
  40. data/lib/bitcoin/script/script_error.rb +27 -1
  41. data/lib/bitcoin/script/script_interpreter.rb +161 -62
  42. data/lib/bitcoin/script/tx_checker.rb +64 -14
  43. data/lib/bitcoin/secp256k1/native.rb +138 -25
  44. data/lib/bitcoin/secp256k1/ruby.rb +78 -19
  45. data/lib/bitcoin/sighash_generator.rb +156 -0
  46. data/lib/bitcoin/tx.rb +13 -80
  47. data/lib/bitcoin/tx_in.rb +1 -1
  48. data/lib/bitcoin/tx_out.rb +2 -3
  49. data/lib/bitcoin/util.rb +15 -6
  50. data/lib/bitcoin/version.rb +1 -1
  51. data/lib/bitcoin/wallet/account.rb +1 -1
  52. metadata +19 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5da63d0663778eba1816d008ebf369348bc75d214965b68c2ff7ce564e95ac3
4
- data.tar.gz: 2a7e31c9f5b29b72b14e93103f1b69111a2283e390fb61fdad517a5aa764f9f4
3
+ metadata.gz: aa485194b6a1f5ea2ba0a4ed3355cae8cc8cdd4252475a78dc811ec54a93dfa4
4
+ data.tar.gz: 5c2c91ab2041b88e0ada77a1099df5e4d564f2d1da0a2963e3ff1beec0b63172
5
5
  SHA512:
6
- metadata.gz: b062f7c6e944cac7787fdca7be284224961b9a675467bd6f55fa0d49ed502157507e098aee9b28998f600826d677ddc3f8b26947134591240b5d8ada5e004686
7
- data.tar.gz: b32d9705400ac5bfb1cc252e4a79c66ff9fb2d8dcba1f530060b5c130e14726e412a5434d5769dd6824588374cbae99c797ae65a2478a92d53cc5fa664ab5a4e
6
+ metadata.gz: c84550004b92167af33a7832c69cafeb44e24a98d491865976194614eed2ea6c831f5ac47a716ec989d1f759786741a26cb81fa6b5da33850a2e2b4dc0fa331b
7
+ data.tar.gz: fb8bdfea37eb452b565f826cb2a12c56865fd80410376f0d444cddf339a1e4eb423988ef617a1dc84b573bb114db3ddae89ae49f60afd882349e92169bac6031
@@ -1 +1 @@
1
- 2.7.0
1
+ ruby-3.0.0
@@ -1,9 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.4.6
4
- - 2.5.5
5
- - 2.6.3
6
- - 2.7.0
3
+ - 2.4.10
4
+ - 2.5.8
5
+ - 2.6.6
6
+ - 2.7.2
7
+ - 3.0.0
7
8
  addons:
8
9
  apt:
9
10
  packages:
data/README.md CHANGED
@@ -18,6 +18,8 @@ Bitcoinrb supports following feature:
18
18
  * [BIP-173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) Bech32 address support
19
19
  * [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) PSBT(Partially Signed Bitcoin Transaction) support
20
20
  * [BIP-85](https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki) Deterministic Entropy From BIP32 Keychains support by `Bitcoin::BIP85Entropy` class.
21
+ * Schnorr signature([BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki))
22
+ * Taproot consensus([BIP-341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) and [BIP-342](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki))
21
23
  * [WIP] SPV node
22
24
  * [WIP] 0ff-chain protocol
23
25
 
@@ -93,6 +95,14 @@ Bitcoin.chain_params = :regtest
93
95
 
94
96
  This parameter is described in https://github.com/chaintope/bitcoinrb/blob/master/lib/bitcoin/chainparams/regtest.yml.
95
97
 
98
+ * default signet
99
+
100
+ ```ruby
101
+ Bitcoin.chain_params = :signet
102
+ ```
103
+
104
+ This parameter is described in https://github.com/chaintope/bitcoinrb/blob/master/lib/bitcoin/chainparams/signet.yml.
105
+
96
106
  ## Contributing
97
107
 
98
108
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bitcoinrb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ["azuchi"]
11
11
  spec.email = ["azuchi@chaintope.com"]
12
12
 
13
- spec.summary = %q{[WIP]The implementation of Bitcoin Protocol for Ruby.}
14
- spec.description = %q{[WIP]The implementation of Bitcoin Protocol for Ruby.}
13
+ spec.summary = %q{The implementation of Bitcoin Protocol for Ruby.}
14
+ spec.description = %q{The implementation of Bitcoin Protocol for Ruby.}
15
15
  spec.homepage = 'https://github.com/chaintope/bitcoinrb'
16
16
  spec.license = "MIT"
17
17
 
@@ -32,8 +32,8 @@ Gem::Specification.new do |spec|
32
32
  spec.add_runtime_dependency 'iniparse'
33
33
  spec.add_runtime_dependency 'siphash'
34
34
  spec.add_runtime_dependency 'protobuf', '3.8.5'
35
- spec.add_runtime_dependency 'scrypt'
36
35
  spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
36
+ spec.add_runtime_dependency 'bip-schnorr', '>= 0.3.2'
37
37
 
38
38
  # for options
39
39
  spec.add_development_dependency 'leveldb-native'
@@ -42,6 +42,6 @@ Gem::Specification.new do |spec|
42
42
  spec.add_development_dependency 'rake', '>= 12.3.3'
43
43
  spec.add_development_dependency 'rspec', '~> 3.0'
44
44
  spec.add_development_dependency 'timecop'
45
- spec.add_development_dependency 'webmock', '~> 3.0'
45
+ spec.add_development_dependency 'webmock', '>= 3.11.1'
46
46
 
47
47
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  require 'bitcoin/version'
5
5
  require 'eventmachine'
6
- require 'ecdsa'
6
+ require 'schnorr'
7
7
  require 'securerandom'
8
8
  require 'json'
9
9
  require 'bech32'
@@ -58,8 +58,11 @@ module Bitcoin
58
58
  autoload :Aezeed, 'bitcoin/aezeed'
59
59
  autoload :PaymentCode, 'bitcoin/payment_code'
60
60
  autoload :BIP85Entropy, 'bitcoin/bip85_entropy'
61
+ autoload :Errors, 'bitcoin/errors'
62
+ autoload :SigHashGenerator, 'bitcoin/sighash_generator'
61
63
 
62
64
  require_relative 'bitcoin/constants'
65
+ require_relative 'bitcoin/ext/ecdsa'
63
66
 
64
67
  extend Util
65
68
 
@@ -67,7 +70,7 @@ module Bitcoin
67
70
 
68
71
  # set bitcoin network chain params
69
72
  def self.chain_params=(name)
70
- raise "chain params for #{name} is not defined." unless %i(mainnet testnet regtest).include?(name.to_sym)
73
+ raise "chain params for #{name} is not defined." unless %i(mainnet testnet regtest signet).include?(name.to_sym)
71
74
  @current_chain = nil
72
75
  @chain_param = name.to_sym
73
76
  end
@@ -82,6 +85,8 @@ module Bitcoin
82
85
  @current_chain = Bitcoin::ChainParams.testnet
83
86
  when :regtest
84
87
  @current_chain = Bitcoin::ChainParams.regtest
88
+ when :signet
89
+ @current_chain = Bitcoin::ChainParams.signet
85
90
  end
86
91
  @current_chain
87
92
  end
@@ -108,7 +113,7 @@ module Bitcoin
108
113
  class ::String
109
114
  # binary convert to hex string
110
115
  def bth
111
- unpack('H*').first
116
+ unpack1('H*')
112
117
  end
113
118
 
114
119
  # hex string convert to binary
@@ -165,6 +170,27 @@ module Bitcoin
165
170
  self[offset..-1]
166
171
  end
167
172
 
173
+ def valid_pushdata_length?
174
+ buf = StringIO.new(self)
175
+ opcode = buf.read(1).ord
176
+ offset = 1
177
+ return false if buf.eof?
178
+ len = case opcode
179
+ when Bitcoin::Opcodes::OP_PUSHDATA1
180
+ offset += 1
181
+ buf.read(1).unpack1('C')
182
+ when Bitcoin::Opcodes::OP_PUSHDATA2
183
+ offset += 2
184
+ buf.read(2).unpack1('v')
185
+ when Bitcoin::Opcodes::OP_PUSHDATA4
186
+ offset += 4
187
+ buf.read(4).unpack1('V')
188
+ else
189
+ opcode
190
+ end
191
+ self.bytesize == len + offset
192
+ end
193
+
168
194
  # whether value is hex or not hex
169
195
  # @return [Boolean] return true if data is hex
170
196
  def valid_hex?
@@ -219,17 +245,4 @@ module Bitcoin
219
245
  end
220
246
  end
221
247
 
222
- class ::ECDSA::Signature
223
- # convert signature to der string.
224
- def to_der
225
- ECDSA::Format::SignatureDerString.encode(self)
226
- end
227
- end
228
-
229
- class ::ECDSA::Point
230
- def to_hex(compression = true)
231
- ECDSA::Format::PointOctetString.encode(self, compression: compression).bth
232
- end
233
- end
234
-
235
248
  end
@@ -43,6 +43,20 @@ module Bitcoin
43
43
  BlockFilter.new(filter_type, filter, block.block_hash)
44
44
  end
45
45
 
46
+ # Decode Block Filter from encoded filter
47
+ # @param [Integer] filter_type filter type.
48
+ # @param [String] block_hash block hash with hex format. not little endian.
49
+ # @param [String] encoded encoded_filter with hex format.
50
+ # @return [Bitcoin::BlockFilter] block filter object.
51
+ def self.decode(filter_type, block_hash, encoded)
52
+ filter = case filter_type
53
+ when TYPE[:basic]
54
+ GCSFilter.new(block_hash.htb[0...16], BASIC_FILTER_P, BASIC_FILTER_M, encoded_filter: encoded)
55
+ else
56
+ raise "unknown filter type: #{filter_type}."
57
+ end
58
+ BlockFilter.new(filter_type, filter, block_hash)
59
+ end
46
60
 
47
61
  # calculate filter hash.
48
62
  # @return [String] this filter hash with hex format.
@@ -51,6 +51,11 @@ module Bitcoin
51
51
  init('regtest')
52
52
  end
53
53
 
54
+ # signet genesis
55
+ def self.signet
56
+ init('signet')
57
+ end
58
+
54
59
  def mainnet?
55
60
  network == 'mainnet'
56
61
  end
@@ -63,6 +68,10 @@ module Bitcoin
63
68
  network == 'regtest'
64
69
  end
65
70
 
71
+ def signet?
72
+ network == 'signet'
73
+ end
74
+
66
75
  def genesis_block
67
76
  header = Bitcoin::BlockHeader.new(
68
77
  genesis['version'], genesis['prev_hash'].rhex, genesis['merkle_root'].rhex,
@@ -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
@@ -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,8 +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
 
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
103
+
104
+ # How much weight budget is added to the witness size (Tapscript only, see BIP 342).
105
+ VALIDATION_WEIGHT_OFFSET = 50
106
+
91
107
  # Signature hash types/flags
92
- SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
108
+ SIGHASH_TYPE = { all: 0x01, none: 0x02, single: 0x3, anyonecanpay: 0x80 , default: 0}
93
109
 
94
110
  # Maximum number length in bytes
95
111
  DEFAULT_MAX_NUM_SIZE = 4
@@ -97,8 +113,6 @@ module Bitcoin
97
113
  # 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
98
114
  MAX_OP_RETURN_RELAY = 83
99
115
 
100
- SIG_VERSION = [:base, :witness_v0]
101
-
102
116
  # for script error
103
117
  SCRIPT_ERR_OK = 0
104
118
  SCRIPT_ERR_UNKNOWN_ERROR = 1
@@ -146,6 +160,10 @@ module Bitcoin
146
160
  # softfork safeness
147
161
  SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
148
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
149
167
 
150
168
  # segregated witness
151
169
  SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
@@ -162,6 +180,15 @@ module Bitcoin
162
180
 
163
181
  SCRIPT_ERR_ERROR_COUNT = 80
164
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
+
165
192
  ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
166
193
  NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
167
194
 
@@ -185,4 +212,17 @@ module Bitcoin
185
212
  BIP32_EXTKEY_WITH_VERSION_SIZE = 78
186
213
 
187
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
+
188
228
  end
@@ -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, 'Invalid pubkey.' unless key.fully_valid_pubkey?
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
@@ -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
@@ -52,9 +52,7 @@ module Bitcoin
52
52
 
53
53
  # Base58 encoded extended private key
54
54
  def to_base58
55
- h = to_hex
56
- hex = h + Bitcoin.calc_checksum(h)
57
- Base58.encode(hex)
55
+ ExtPubkey.encode_base58(to_hex)
58
56
  end
59
57
 
60
58
  # get private key(hex)
@@ -146,22 +144,24 @@ module Bitcoin
146
144
  buf = StringIO.new(payload)
147
145
  ext_key = ExtKey.new
148
146
  ext_key.ver = buf.read(4).bth # version
149
- raise 'An unsupported version byte was specified.' unless ExtKey.support_version?(ext_key.ver)
150
- ext_key.depth = buf.read(1).unpack('C').first
147
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtKey.support_version?(ext_key.ver)
148
+ ext_key.depth = buf.read(1).unpack1('C')
151
149
  ext_key.parent_fingerprint = buf.read(4).bth
150
+ ext_key.number = buf.read(4).unpack1('N')
152
151
  if ext_key.depth == 0
153
- raise ArgumentError, 'Invalid parent fingerprint.' unless ext_key.parent_fingerprint == MASTER_FINGERPRINT
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
154
  end
155
- ext_key.number = buf.read(4).unpack('N').first
155
+ raise ArgumentError, Errors::Messages:: INVALID_BIP32_ZERO_DEPTH if ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_key.depth > 0
156
156
  ext_key.chain_code = buf.read(32)
157
- buf.read(1) # 0x00
157
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth == '00' # 0x00
158
158
  ext_key.key = Bitcoin::Key.new(priv_key: buf.read(32).bth, key_type: Bitcoin::Key::TYPES[:compressed])
159
159
  ext_key
160
160
  end
161
161
 
162
162
  # import private key from Base58 private key address
163
- def self.from_base58(address)
164
- ExtKey.parse_from_payload(Base58.decode(address).htb)
163
+ def self.from_base58(base58)
164
+ ExtKey.parse_from_payload(ExtPubkey.validate_base58(base58))
165
165
  end
166
166
 
167
167
  # get version bytes from purpose' value.
@@ -253,9 +253,14 @@ module Bitcoin
253
253
 
254
254
  # Base58 encoded extended pubkey
255
255
  def to_base58
256
- h = to_hex
257
- hex = h + Bitcoin.calc_checksum(h)
258
- Base58.encode(hex)
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))
259
264
  end
260
265
 
261
266
  # whether hardened key.
@@ -310,22 +315,33 @@ module Bitcoin
310
315
  buf = StringIO.new(payload)
311
316
  ext_pubkey = ExtPubkey.new
312
317
  ext_pubkey.ver = buf.read(4).bth # version
313
- raise 'An unsupported version byte was specified.' unless ExtPubkey.support_version?(ext_pubkey.ver)
314
- ext_pubkey.depth = buf.read(1).unpack('C').first
318
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtPubkey.support_version?(ext_pubkey.ver)
319
+ ext_pubkey.depth = buf.read(1).unpack1('C')
315
320
  ext_pubkey.parent_fingerprint = buf.read(4).bth
321
+ ext_pubkey.number = buf.read(4).unpack1('N')
316
322
  if ext_pubkey.depth == 0
317
- raise ArgumentError, 'Invalid parent fingerprint.' unless ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
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
318
325
  end
319
- ext_pubkey.number = buf.read(4).unpack('N').first
326
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_DEPTH if ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_pubkey.depth > 0
320
327
  ext_pubkey.chain_code = buf.read(32)
321
- ext_pubkey.pubkey = buf.read(33).bth
328
+ ext_pubkey.pubkey = Bitcoin::Key.new(pubkey: buf.read(33).bth).pubkey
322
329
  ext_pubkey
323
330
  end
324
331
 
325
332
 
326
333
  # import pub key from Base58 private key address
327
334
  def self.from_base58(address)
328
- ExtPubkey.parse_from_payload(Base58.decode(address).htb)
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
329
345
  end
330
346
 
331
347
  # get version bytes from purpose' value.