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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +5 -4
- data/README.md +10 -0
- data/bitcoinrb.gemspec +4 -4
- data/lib/bitcoin.rb +29 -16
- data/lib/bitcoin/block_filter.rb +14 -0
- data/lib/bitcoin/chain_params.rb +9 -0
- data/lib/bitcoin/chainparams/signet.yml +39 -0
- data/lib/bitcoin/constants.rb +43 -3
- data/lib/bitcoin/descriptor.rb +1 -1
- data/lib/bitcoin/errors.rb +19 -0
- data/lib/bitcoin/ext/ecdsa.rb +31 -0
- data/lib/bitcoin/ext_key.rb +35 -19
- data/lib/bitcoin/key.rb +43 -26
- data/lib/bitcoin/message/cfcheckpt.rb +2 -2
- data/lib/bitcoin/message/cfheaders.rb +1 -1
- data/lib/bitcoin/message/cfilter.rb +1 -1
- data/lib/bitcoin/message/fee_filter.rb +1 -1
- data/lib/bitcoin/message/filter_load.rb +3 -3
- 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 +3 -3
- data/lib/bitcoin/message/ping.rb +1 -1
- data/lib/bitcoin/message/pong.rb +1 -1
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/mnemonic.rb +2 -2
- data/lib/bitcoin/network/peer_discovery.rb +1 -3
- data/lib/bitcoin/node/configuration.rb +3 -1
- data/lib/bitcoin/node/spv.rb +8 -0
- data/lib/bitcoin/opcodes.rb +14 -1
- data/lib/bitcoin/payment_code.rb +2 -2
- data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
- data/lib/bitcoin/psbt/input.rb +3 -3
- data/lib/bitcoin/psbt/output.rb +1 -1
- data/lib/bitcoin/psbt/tx.rb +4 -4
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +1 -1
- data/lib/bitcoin/script/script.rb +52 -19
- data/lib/bitcoin/script/script_error.rb +27 -1
- data/lib/bitcoin/script/script_interpreter.rb +161 -62
- data/lib/bitcoin/script/tx_checker.rb +64 -14
- data/lib/bitcoin/secp256k1/native.rb +138 -25
- data/lib/bitcoin/secp256k1/ruby.rb +78 -19
- data/lib/bitcoin/sighash_generator.rb +156 -0
- data/lib/bitcoin/tx.rb +13 -80
- data/lib/bitcoin/tx_in.rb +1 -1
- data/lib/bitcoin/tx_out.rb +2 -3
- data/lib/bitcoin/util.rb +15 -6
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +1 -1
- metadata +19 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa485194b6a1f5ea2ba0a4ed3355cae8cc8cdd4252475a78dc811ec54a93dfa4
|
4
|
+
data.tar.gz: 5c2c91ab2041b88e0ada77a1099df5e4d564f2d1da0a2963e3ff1beec0b63172
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c84550004b92167af33a7832c69cafeb44e24a98d491865976194614eed2ea6c831f5ac47a716ec989d1f759786741a26cb81fa6b5da33850a2e2b4dc0fa331b
|
7
|
+
data.tar.gz: fb8bdfea37eb452b565f826cb2a12c56865fd80410376f0d444cddf339a1e4eb423988ef617a1dc84b573bb114db3ddae89ae49f60afd882349e92169bac6031
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
ruby-3.0.0
|
data/.travis.yml
CHANGED
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.
|
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{
|
14
|
-
spec.description = %q{
|
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', '
|
45
|
+
spec.add_development_dependency 'webmock', '>= 3.11.1'
|
46
46
|
|
47
47
|
end
|
data/lib/bitcoin.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'bitcoin/version'
|
5
5
|
require 'eventmachine'
|
6
|
-
require '
|
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
|
-
|
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
|
data/lib/bitcoin/block_filter.rb
CHANGED
@@ -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.
|
data/lib/bitcoin/chain_params.rb
CHANGED
@@ -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
|
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,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:
|
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
|
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
|
@@ -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
|
data/lib/bitcoin/ext_key.rb
CHANGED
@@ -52,9 +52,7 @@ module Bitcoin
|
|
52
52
|
|
53
53
|
# Base58 encoded extended private key
|
54
54
|
def to_base58
|
55
|
-
|
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
|
150
|
-
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')
|
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,
|
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.
|
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(
|
164
|
-
ExtKey.parse_from_payload(
|
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
|
-
|
257
|
-
|
258
|
-
|
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
|
314
|
-
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')
|
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,
|
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.
|
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(
|
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.
|