bitcoinrb 0.5.0 → 0.9.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.rspec_parallel +2 -0
  4. data/.ruby-version +1 -1
  5. data/README.md +11 -1
  6. data/bitcoinrb.gemspec +7 -6
  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 +45 -4
  11. data/lib/bitcoin/descriptor.rb +1 -1
  12. data/lib/bitcoin/errors.rb +19 -0
  13. data/lib/bitcoin/ext/array_ext.rb +22 -0
  14. data/lib/bitcoin/ext/ecdsa.rb +36 -0
  15. data/lib/bitcoin/ext.rb +1 -0
  16. data/lib/bitcoin/ext_key.rb +36 -20
  17. data/lib/bitcoin/key.rb +85 -28
  18. data/lib/bitcoin/message/addr_v2.rb +34 -0
  19. data/lib/bitcoin/message/base.rb +16 -0
  20. data/lib/bitcoin/message/cfcheckpt.rb +2 -2
  21. data/lib/bitcoin/message/cfheaders.rb +1 -1
  22. data/lib/bitcoin/message/cfilter.rb +1 -1
  23. data/lib/bitcoin/message/fee_filter.rb +1 -1
  24. data/lib/bitcoin/message/filter_load.rb +3 -3
  25. data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
  26. data/lib/bitcoin/message/inventory.rb +1 -1
  27. data/lib/bitcoin/message/merkle_block.rb +1 -1
  28. data/lib/bitcoin/message/network_addr.rb +141 -18
  29. data/lib/bitcoin/message/ping.rb +1 -1
  30. data/lib/bitcoin/message/pong.rb +1 -1
  31. data/lib/bitcoin/message/send_addr_v2.rb +13 -0
  32. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  33. data/lib/bitcoin/message/tx.rb +1 -1
  34. data/lib/bitcoin/message.rb +72 -0
  35. data/lib/bitcoin/message_sign.rb +47 -0
  36. data/lib/bitcoin/mnemonic.rb +2 -2
  37. data/lib/bitcoin/network/peer_discovery.rb +1 -3
  38. data/lib/bitcoin/node/configuration.rb +3 -1
  39. data/lib/bitcoin/node/spv.rb +8 -0
  40. data/lib/bitcoin/opcodes.rb +14 -1
  41. data/lib/bitcoin/payment_code.rb +2 -2
  42. data/lib/bitcoin/payments/payment.pb.rb +1 -1
  43. data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
  44. data/lib/bitcoin/psbt/input.rb +4 -4
  45. data/lib/bitcoin/psbt/output.rb +1 -1
  46. data/lib/bitcoin/psbt/tx.rb +14 -5
  47. data/lib/bitcoin/psbt.rb +8 -0
  48. data/lib/bitcoin/rpc/bitcoin_core_client.rb +1 -1
  49. data/lib/bitcoin/rpc/request_handler.rb +3 -3
  50. data/lib/bitcoin/script/script.rb +80 -30
  51. data/lib/bitcoin/script/script_error.rb +27 -1
  52. data/lib/bitcoin/script/script_interpreter.rb +164 -62
  53. data/lib/bitcoin/script/tx_checker.rb +62 -14
  54. data/lib/bitcoin/secp256k1/native.rb +184 -17
  55. data/lib/bitcoin/secp256k1/ruby.rb +108 -21
  56. data/lib/bitcoin/sighash_generator.rb +157 -0
  57. data/lib/bitcoin/taproot/leaf_node.rb +23 -0
  58. data/lib/bitcoin/taproot/simple_builder.rb +155 -0
  59. data/lib/bitcoin/taproot.rb +45 -0
  60. data/lib/bitcoin/tx.rb +30 -96
  61. data/lib/bitcoin/tx_in.rb +1 -1
  62. data/lib/bitcoin/tx_out.rb +2 -3
  63. data/lib/bitcoin/util.rb +15 -6
  64. data/lib/bitcoin/version.rb +1 -1
  65. data/lib/bitcoin/wallet/account.rb +1 -1
  66. data/lib/bitcoin.rb +32 -24
  67. metadata +58 -18
  68. data/.travis.yml +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5da63d0663778eba1816d008ebf369348bc75d214965b68c2ff7ce564e95ac3
4
- data.tar.gz: 2a7e31c9f5b29b72b14e93103f1b69111a2283e390fb61fdad517a5aa764f9f4
3
+ metadata.gz: e8103017ba6b480dc45caea8b9544720003c486167c3e867be738ccdc40faef7
4
+ data.tar.gz: 3807ee81a2ed26a526c19903c616ebb7af5b2a9c8434a76398e538a02c34e126
5
5
  SHA512:
6
- metadata.gz: b062f7c6e944cac7787fdca7be284224961b9a675467bd6f55fa0d49ed502157507e098aee9b28998f600826d677ddc3f8b26947134591240b5d8ada5e004686
7
- data.tar.gz: b32d9705400ac5bfb1cc252e4a79c66ff9fb2d8dcba1f530060b5c130e14726e412a5434d5769dd6824588374cbae99c797ae65a2478a92d53cc5fa664ab5a4e
6
+ metadata.gz: 97a9df2fee4c9d2fb7c84555d82e0f1ef692e0889c964fc7615f328467925a5b099be19801b19321a9b12fc55493663f1813be4074e5fc95add8948cd52ca2e4
7
+ data.tar.gz: '0370268e40a85bf6face9592b8dc3cf260b6357e796f62c7e6abddaeeef2d3ec561e4336ba859c5f8426dcedd80eba6d9ec632941e284791df9571ff613182e5'
@@ -0,0 +1,37 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby-version: ['2.6', '2.7', '3.0']
23
+
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Install leveldb
27
+ run: sudo apt-get install libleveldb-dev
28
+ - name: Set up Ruby
29
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
30
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
31
+ # uses: ruby/setup-ruby@v1
32
+ uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
36
+ - name: Run tests
37
+ run: bundle exec rake spec
data/.rspec_parallel ADDED
@@ -0,0 +1,2 @@
1
+ --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
2
+ --format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.0
1
+ ruby-3.0.2
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Bitcoinrb [![Build Status](https://travis-ci.org/chaintope/bitcoinrb.svg?branch=master)](https://travis-ci.org/chaintope/bitcoinrb) [![Gem Version](https://badge.fury.io/rb/bitcoinrb.svg)](https://badge.fury.io/rb/bitcoinrb) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) <img src="http://segwit.co/static/public/images/logo.png" width="100">
1
+ # Bitcoinrb [![Build Status](https://github.com/chaintope/bitcoinrb/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/chaintope/bitcoinrb/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/bitcoinrb.svg)](https://badge.fury.io/rb/bitcoinrb) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) <img src="http://segwit.co/static/public/images/logo.png" width="100">
2
2
 
3
3
 
4
4
  Bitcoinrb is a Ruby implementation of Bitcoin Protocol.
@@ -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.
data/bitcoinrb.gemspec CHANGED
@@ -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
 
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'ecdsa'
24
24
  spec.add_runtime_dependency 'eventmachine'
25
25
  spec.add_runtime_dependency 'murmurhash3'
26
- spec.add_runtime_dependency 'bech32', '~> 1.0.3'
26
+ spec.add_runtime_dependency 'bech32', '~> 1.1.0'
27
27
  spec.add_runtime_dependency 'daemon-spawn'
28
28
  spec.add_runtime_dependency 'thor'
29
29
  spec.add_runtime_dependency 'ffi'
@@ -32,8 +32,9 @@ 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.4.0'
37
+ spec.add_runtime_dependency 'base32', '>= 0.3.4'
37
38
 
38
39
  # for options
39
40
  spec.add_development_dependency 'leveldb-native'
@@ -42,6 +43,6 @@ Gem::Specification.new do |spec|
42
43
  spec.add_development_dependency 'rake', '>= 12.3.3'
43
44
  spec.add_development_dependency 'rspec', '~> 3.0'
44
45
  spec.add_development_dependency 'timecop'
45
- spec.add_development_dependency 'webmock', '~> 3.0'
46
-
46
+ spec.add_development_dependency 'webmock', '>= 3.11.1'
47
+ spec.add_development_dependency 'parallel', '>= 1.20.1'
47
48
  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
 
@@ -68,7 +73,8 @@ module Bitcoin
68
73
  # for script
69
74
 
70
75
  # witness version
71
- WITNESS_VERSION = 0x00
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,8 +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
 
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
104
+
105
+ # How much weight budget is added to the witness size (Tapscript only, see BIP 342).
106
+ VALIDATION_WEIGHT_OFFSET = 50
107
+
91
108
  # Signature hash types/flags
92
- SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
109
+ SIGHASH_TYPE = { all: 0x01, none: 0x02, single: 0x3, anyonecanpay: 0x80 , default: 0}
93
110
 
94
111
  # Maximum number length in bytes
95
112
  DEFAULT_MAX_NUM_SIZE = 4
@@ -97,8 +114,6 @@ module Bitcoin
97
114
  # 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
98
115
  MAX_OP_RETURN_RELAY = 83
99
116
 
100
- SIG_VERSION = [:base, :witness_v0]
101
-
102
117
  # for script error
103
118
  SCRIPT_ERR_OK = 0
104
119
  SCRIPT_ERR_UNKNOWN_ERROR = 1
@@ -146,6 +161,10 @@ module Bitcoin
146
161
  # softfork safeness
147
162
  SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
148
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
149
168
 
150
169
  # segregated witness
151
170
  SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
@@ -162,6 +181,15 @@ module Bitcoin
162
181
 
163
182
  SCRIPT_ERR_ERROR_COUNT = 80
164
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
+
165
193
  ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
166
194
  NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
167
195
 
@@ -185,4 +213,17 @@ module Bitcoin
185
213
  BIP32_EXTKEY_WITH_VERSION_SIZE = 78
186
214
 
187
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
+
188
229
  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,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
data/lib/bitcoin/ext.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module Bitcoin
2
2
  module Ext
3
3
  autoload :JsonParser, 'bitcoin/ext/json_parser'
4
+ autoload :ArrayExt, 'bitcoin/ext/array_ext'
4
5
  end
5
6
  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.
@@ -275,7 +280,7 @@ module Bitcoin
275
280
  l = Bitcoin.hmac_sha512(chain_code, data)
276
281
  left = l[0..31].bth.to_i(16)
277
282
  raise 'invalid key' if left >= CURVE_ORDER
278
- p1 = Bitcoin::Secp256k1::GROUP.generator.multiply_by_scalar(left)
283
+ p1 = Bitcoin::Key.new(priv_key: left.to_s(16), key_type: Bitcoin::Key::TYPES[:uncompressed]).to_point
279
284
  p2 = Bitcoin::Key.new(pubkey: pubkey, key_type: key_type).to_point
280
285
  new_key.pubkey = (p1 + p2).to_hex
281
286
  new_key.chain_code = l[32..-1]
@@ -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.