bitcoinrb 0.2.9 → 0.5.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 +3 -2
- data/README.md +7 -6
- data/bitcoinrb.gemspec +4 -4
- data/exe/bitcoinrbd +5 -0
- data/lib/bitcoin.rb +33 -1
- data/lib/bitcoin/bip85_entropy.rb +111 -0
- data/lib/bitcoin/block_header.rb +2 -0
- data/lib/bitcoin/chain_params.rb +0 -8
- data/lib/bitcoin/chainparams/regtest.yml +1 -1
- data/lib/bitcoin/chainparams/testnet.yml +1 -1
- data/lib/bitcoin/constants.rb +3 -10
- data/lib/bitcoin/descriptor.rb +147 -0
- data/lib/bitcoin/ext.rb +5 -0
- data/lib/bitcoin/ext/json_parser.rb +46 -0
- data/lib/bitcoin/ext_key.rb +19 -4
- data/lib/bitcoin/key.rb +9 -5
- data/lib/bitcoin/key_path.rb +12 -5
- data/lib/bitcoin/message.rb +7 -0
- data/lib/bitcoin/message/base.rb +1 -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/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/network_addr.rb +31 -12
- data/lib/bitcoin/message/version.rb +14 -22
- data/lib/bitcoin/mnemonic.rb +5 -5
- data/lib/bitcoin/network/peer.rb +12 -11
- data/lib/bitcoin/network/peer_discovery.rb +3 -1
- data/lib/bitcoin/node/cli.rb +14 -10
- data/lib/bitcoin/node/spv.rb +1 -1
- data/lib/bitcoin/out_point.rb +14 -7
- data/lib/bitcoin/payment_code.rb +92 -0
- data/lib/bitcoin/psbt.rb +3 -1
- data/lib/bitcoin/psbt/input.rb +7 -16
- data/lib/bitcoin/psbt/tx.rb +18 -12
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
- data/lib/bitcoin/rpc/request_handler.rb +3 -3
- data/lib/bitcoin/script/script.rb +18 -10
- data/lib/bitcoin/script/script_interpreter.rb +3 -5
- data/lib/bitcoin/secp256k1.rb +1 -0
- data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
- data/lib/bitcoin/secp256k1/ruby.rb +4 -35
- data/lib/bitcoin/slip39.rb +93 -0
- data/lib/bitcoin/slip39/share.rb +122 -0
- data/lib/bitcoin/slip39/sss.rb +245 -0
- data/lib/bitcoin/slip39/wordlist/english.txt +1024 -0
- data/lib/bitcoin/store.rb +2 -1
- data/lib/bitcoin/store/chain_entry.rb +1 -0
- data/lib/bitcoin/store/db/level_db.rb +2 -2
- data/lib/bitcoin/store/utxo_db.rb +226 -0
- data/lib/bitcoin/tx.rb +6 -10
- data/lib/bitcoin/tx_in.rb +4 -5
- data/lib/bitcoin/util.rb +29 -1
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet.rb +1 -0
- data/lib/bitcoin/wallet/account.rb +1 -0
- data/lib/bitcoin/wallet/base.rb +3 -3
- data/lib/bitcoin/wallet/db.rb +1 -1
- data/lib/bitcoin/wallet/master_key.rb +1 -0
- data/lib/bitcoin/wallet/utxo.rb +37 -0
- metadata +45 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5da63d0663778eba1816d008ebf369348bc75d214965b68c2ff7ce564e95ac3
|
4
|
+
data.tar.gz: 2a7e31c9f5b29b72b14e93103f1b69111a2283e390fb61fdad517a5aa764f9f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b062f7c6e944cac7787fdca7be284224961b9a675467bd6f55fa0d49ed502157507e098aee9b28998f600826d677ddc3f8b26947134591240b5d8ada5e004686
|
7
|
+
data.tar.gz: b32d9705400ac5bfb1cc252e4a79c66ff9fb2d8dcba1f530060b5c130e14726e412a5434d5769dd6824588374cbae99c797ae65a2478a92d53cc5fa664ab5a4e
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.0
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -9,14 +9,15 @@ NOTE: Bitcoinrb work in progress, and there is a possibility of incompatible cha
|
|
9
9
|
|
10
10
|
Bitcoinrb supports following feature:
|
11
11
|
|
12
|
-
* Bitcoin script interpreter(including [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki), [BIP-68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki), [BIP-112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki))
|
13
|
-
* De/serialization of Bitcoin protocol network messages
|
14
|
-
* De/serialization of blocks and transactions
|
12
|
+
* [Bitcoin script interpreter](https://github.com/chaintope/bitcoinrb/wiki/Script)(including [BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki), [BIP-68](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki), [BIP-112](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki))
|
13
|
+
* [De/serialization of Bitcoin protocol network messages](https://github.com/chaintope/bitcoinrb/wiki/P2P-Message)
|
14
|
+
* De/serialization of blocks and [transactions](https://github.com/chaintope/bitcoinrb/wiki/Transaction)
|
15
15
|
* Key generation and verification for ECDSA, including [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) supports.
|
16
16
|
* ECDSA signature(RFC6979 -Deterministic ECDSA, LOW-S, LOW-R support)
|
17
17
|
* Segwit support (parsing segwit payload, Bech32 address, sign for segwit tx, [BIP-141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki), [BIP-143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki), [BIP-144](https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki))
|
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
|
+
* [BIP-85](https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki) Deterministic Entropy From BIP32 Keychains support by `Bitcoin::BIP85Entropy` class.
|
20
21
|
* [WIP] SPV node
|
21
22
|
* [WIP] 0ff-chain protocol
|
22
23
|
|
@@ -36,10 +37,10 @@ If you use node features, please install level DB as follows.
|
|
36
37
|
|
37
38
|
$ brew install leveldb
|
38
39
|
|
39
|
-
and put `leveldb-
|
40
|
+
and put `leveldb-native` in your Gemfile and run bundle install.
|
40
41
|
|
41
|
-
```
|
42
|
-
gem leveldb-
|
42
|
+
```ruby
|
43
|
+
gem 'leveldb-native'
|
43
44
|
```
|
44
45
|
|
45
46
|
## Installation
|
data/bitcoinrb.gemspec
CHANGED
@@ -29,19 +29,19 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_runtime_dependency 'ffi'
|
30
30
|
spec.add_runtime_dependency 'leb128', '~> 1.0.0'
|
31
31
|
spec.add_runtime_dependency 'eventmachine_httpserver'
|
32
|
-
spec.add_runtime_dependency 'rest-client'
|
33
32
|
spec.add_runtime_dependency 'iniparse'
|
34
33
|
spec.add_runtime_dependency 'siphash'
|
35
34
|
spec.add_runtime_dependency 'protobuf', '3.8.5'
|
36
35
|
spec.add_runtime_dependency 'scrypt'
|
37
|
-
spec.add_runtime_dependency '
|
36
|
+
spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
|
38
37
|
|
39
38
|
# for options
|
40
|
-
spec.add_development_dependency 'leveldb-
|
39
|
+
spec.add_development_dependency 'leveldb-native'
|
41
40
|
|
42
41
|
spec.add_development_dependency 'bundler'
|
43
|
-
spec.add_development_dependency 'rake', '
|
42
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
44
43
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
45
44
|
spec.add_development_dependency 'timecop'
|
45
|
+
spec.add_development_dependency 'webmock', '~> 3.0'
|
46
46
|
|
47
47
|
end
|
data/exe/bitcoinrbd
CHANGED
data/lib/bitcoin.rb
CHANGED
@@ -14,6 +14,7 @@ require_relative 'openassets'
|
|
14
14
|
|
15
15
|
module Bitcoin
|
16
16
|
|
17
|
+
autoload :Ext, 'bitcoin/ext'
|
17
18
|
autoload :Util, 'bitcoin/util'
|
18
19
|
autoload :ChainParams, 'bitcoin/chain_params'
|
19
20
|
autoload :Message, 'bitcoin/message'
|
@@ -52,6 +53,11 @@ module Bitcoin
|
|
52
53
|
autoload :BitStreamWriter, 'bitcoin/bit_stream'
|
53
54
|
autoload :BitStreamReader, 'bitcoin/bit_stream'
|
54
55
|
autoload :KeyPath, 'bitcoin/key_path'
|
56
|
+
autoload :Descriptor, 'bitcoin/descriptor'
|
57
|
+
autoload :SLIP39, 'bitcoin/slip39'
|
58
|
+
autoload :Aezeed, 'bitcoin/aezeed'
|
59
|
+
autoload :PaymentCode, 'bitcoin/payment_code'
|
60
|
+
autoload :BIP85Entropy, 'bitcoin/bip85_entropy'
|
55
61
|
|
56
62
|
require_relative 'bitcoin/constants'
|
57
63
|
|
@@ -110,6 +116,11 @@ module Bitcoin
|
|
110
116
|
[self].pack('H*')
|
111
117
|
end
|
112
118
|
|
119
|
+
# binary convert to integer
|
120
|
+
def bti
|
121
|
+
bth.to_i(16)
|
122
|
+
end
|
123
|
+
|
113
124
|
# reverse hex string endian
|
114
125
|
def rhex
|
115
126
|
htb.reverse.bth
|
@@ -154,6 +165,12 @@ module Bitcoin
|
|
154
165
|
self[offset..-1]
|
155
166
|
end
|
156
167
|
|
168
|
+
# whether value is hex or not hex
|
169
|
+
# @return [Boolean] return true if data is hex
|
170
|
+
def valid_hex?
|
171
|
+
!self[/\H/]
|
172
|
+
end
|
173
|
+
|
157
174
|
end
|
158
175
|
|
159
176
|
class ::Object
|
@@ -175,7 +192,7 @@ module Bitcoin
|
|
175
192
|
if value.is_a?(Array)
|
176
193
|
result.update(key => value.map{|v|v.to_h})
|
177
194
|
else
|
178
|
-
result.update(key => value)
|
195
|
+
result.update(key => value.class.to_s.start_with?("Bitcoin::") ? value.to_h : value)
|
179
196
|
end
|
180
197
|
end
|
181
198
|
end
|
@@ -191,6 +208,15 @@ module Bitcoin
|
|
191
208
|
def itb
|
192
209
|
to_even_length_hex.htb
|
193
210
|
end
|
211
|
+
|
212
|
+
# convert bit string
|
213
|
+
def to_bits(length = nil )
|
214
|
+
if length
|
215
|
+
to_s(2).rjust(length, '0')
|
216
|
+
else
|
217
|
+
to_s(2)
|
218
|
+
end
|
219
|
+
end
|
194
220
|
end
|
195
221
|
|
196
222
|
class ::ECDSA::Signature
|
@@ -200,4 +226,10 @@ module Bitcoin
|
|
200
226
|
end
|
201
227
|
end
|
202
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
|
+
|
203
235
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# Deterministic Entropy From BIP32 Keychains
|
4
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki
|
5
|
+
class BIP85Entropy
|
6
|
+
|
7
|
+
BIP85_PATH = 83696968 + HARDENED_THRESHOLD
|
8
|
+
|
9
|
+
include Bitcoin::KeyPath
|
10
|
+
|
11
|
+
attr_reader :root_key #hex format
|
12
|
+
|
13
|
+
# Import root key.
|
14
|
+
# @param [String] base58 master bip32 root key.
|
15
|
+
# @return [Bitcoin::BIP85Entropy]
|
16
|
+
def self.from_base58(base58)
|
17
|
+
key = Bitcoin::ExtKey.from_base58(base58)
|
18
|
+
self.new(key)
|
19
|
+
end
|
20
|
+
|
21
|
+
# derive entropy
|
22
|
+
# @param [String] path derive path.
|
23
|
+
# @return [Tuple(String, Object)] a tuple of entropy with hex format and results depending the application.
|
24
|
+
def derive(path)
|
25
|
+
raise ArgumentError, "Invalid BIP85 path format." unless path.start_with?("m/83696968'")
|
26
|
+
derived_key = root_key
|
27
|
+
parse_key_path(path).each{|num| derived_key = derived_key.derive(num)}
|
28
|
+
derived_key = derived_key.priv
|
29
|
+
entropy = Bitcoin.hmac_sha512("bip-entropy-from-k", derived_key.htb).bth
|
30
|
+
app_no = path.split('/')[2]
|
31
|
+
case app_no
|
32
|
+
when "39'"
|
33
|
+
bip39_entropy(path, entropy)
|
34
|
+
when "2'"
|
35
|
+
hd_seed_entropy(entropy)
|
36
|
+
when "32'"
|
37
|
+
xprv_entropy(entropy)
|
38
|
+
else
|
39
|
+
[entropy, entropy]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def initialize(root_key)
|
46
|
+
@root_key = root_key
|
47
|
+
end
|
48
|
+
|
49
|
+
# derive BIP39 entropy.
|
50
|
+
def bip39_entropy(path, entropy)
|
51
|
+
params = path.split('/')
|
52
|
+
word_len = params[4]
|
53
|
+
language = code_to_language(params[3])
|
54
|
+
entropy = case word_len
|
55
|
+
when "12'"
|
56
|
+
entropy[0...32]
|
57
|
+
when "18'"
|
58
|
+
entropy[0...48]
|
59
|
+
when "24'"
|
60
|
+
entropy[0...64]
|
61
|
+
else
|
62
|
+
raise ArgumentError, "Word length #{word_len} does not supported."
|
63
|
+
end
|
64
|
+
mnemonic = Bitcoin::Mnemonic.new(language)
|
65
|
+
[entropy, mnemonic.to_mnemonic(entropy)]
|
66
|
+
end
|
67
|
+
|
68
|
+
# derive HD-Seed WIF entropy.
|
69
|
+
def hd_seed_entropy(entropy)
|
70
|
+
result = entropy[0...64]
|
71
|
+
[result, Bitcoin::Key.new(priv_key: result).to_wif]
|
72
|
+
end
|
73
|
+
|
74
|
+
# derive xprv entropy
|
75
|
+
def xprv_entropy(entropy)
|
76
|
+
chaincode = entropy[0...64]
|
77
|
+
private_key = Bitcoin::Key.new(priv_key: entropy[64..-1])
|
78
|
+
ext_key = Bitcoin::ExtKey.new
|
79
|
+
ext_key.key = private_key
|
80
|
+
ext_key.chain_code = chaincode.htb
|
81
|
+
ext_key.depth = 0
|
82
|
+
ext_key.number = 0
|
83
|
+
ext_key.parent_fingerprint = Bitcoin::ExtKey::MASTER_FINGERPRINT
|
84
|
+
[entropy, ext_key.to_base58]
|
85
|
+
end
|
86
|
+
|
87
|
+
# convert language code to language string.
|
88
|
+
def code_to_language(code)
|
89
|
+
case code
|
90
|
+
when "0'"
|
91
|
+
"english"
|
92
|
+
when "1'"
|
93
|
+
"japanese"
|
94
|
+
when "3'"
|
95
|
+
"spanish"
|
96
|
+
when "4'"
|
97
|
+
"chinese_simplified"
|
98
|
+
when "5'"
|
99
|
+
"chinese_traditional"
|
100
|
+
when "6'"
|
101
|
+
"french"
|
102
|
+
when "7'"
|
103
|
+
"italian"
|
104
|
+
else
|
105
|
+
raise ArgumentError, "bitcoinrb does not support language: #{code}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
data/lib/bitcoin/block_header.rb
CHANGED
data/lib/bitcoin/chain_params.rb
CHANGED
@@ -36,9 +36,6 @@ module Bitcoin
|
|
36
36
|
|
37
37
|
attr_accessor :dust_relay_fee
|
38
38
|
|
39
|
-
# fork coin id.
|
40
|
-
attr_accessor :fork_id
|
41
|
-
|
42
39
|
# mainnet genesis
|
43
40
|
def self.mainnet
|
44
41
|
init('mainnet')
|
@@ -73,11 +70,6 @@ module Bitcoin
|
|
73
70
|
Bitcoin::Block.new(header)
|
74
71
|
end
|
75
72
|
|
76
|
-
# whether fork coin.
|
77
|
-
def fork_chain?
|
78
|
-
!fork_id.nil?
|
79
|
-
end
|
80
|
-
|
81
73
|
def self.init(name)
|
82
74
|
i = YAML.load(File.open("#{__dir__}/chainparams/#{name}.yml"))
|
83
75
|
i.dust_relay_fee ||= Bitcoin::DUST_RELAY_TX_FEE
|
@@ -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"
|
@@ -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
@@ -91,13 +91,6 @@ module Bitcoin
|
|
91
91
|
# Signature hash types/flags
|
92
92
|
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
93
93
|
|
94
|
-
# SIGHASH_FORK_ID for replay protection of the fork coin
|
95
|
-
SIGHASH_FORK_ID = 0x40
|
96
|
-
|
97
|
-
# fork coin id.
|
98
|
-
FORK_ID_CASH = 0
|
99
|
-
FORK_ID_GOLD = 79
|
100
|
-
|
101
94
|
# Maximum number length in bytes
|
102
95
|
DEFAULT_MAX_NUM_SIZE = 4
|
103
96
|
|
@@ -146,9 +139,9 @@ module Bitcoin
|
|
146
139
|
SCRIPT_ERR_SIG_HIGH_S = 54
|
147
140
|
SCRIPT_ERR_SIG_NULLDUMMY = 55
|
148
141
|
SCRIPT_ERR_PUBKEYTYPE = 56
|
149
|
-
SCRIPT_ERR_CLEANSTACK =
|
150
|
-
SCRIPT_ERR_MINIMALIF =
|
151
|
-
SCRIPT_ERR_SIG_NULLFAIL =
|
142
|
+
SCRIPT_ERR_CLEANSTACK = 57
|
143
|
+
SCRIPT_ERR_MINIMALIF = 58
|
144
|
+
SCRIPT_ERR_SIG_NULLFAIL = 59
|
152
145
|
|
153
146
|
# softfork safeness
|
154
147
|
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
module Descriptor
|
4
|
+
|
5
|
+
include Bitcoin::Opcodes
|
6
|
+
|
7
|
+
# generate P2PK output for the given public key.
|
8
|
+
# @param [String] key private key or public key with hex format
|
9
|
+
# @return [Bitcoin::Script] P2PK script.
|
10
|
+
def pk(key)
|
11
|
+
Bitcoin::Script.new << extract_pubkey(key) << OP_CHECKSIG
|
12
|
+
end
|
13
|
+
|
14
|
+
# generate P2PKH output for the given public key.
|
15
|
+
# @param [String] key private key or public key with hex format.
|
16
|
+
# @return [Bitcoin::Script] P2PKH script.
|
17
|
+
def pkh(key)
|
18
|
+
Bitcoin::Script.to_p2pkh(Bitcoin.hash160(extract_pubkey(key)))
|
19
|
+
end
|
20
|
+
|
21
|
+
# generate P2PKH output for the given public key.
|
22
|
+
# @param [String] key private key or public key with hex format.
|
23
|
+
# @return [Bitcoin::Script] P2WPKH script.
|
24
|
+
def wpkh(key)
|
25
|
+
pubkey = extract_pubkey(key)
|
26
|
+
raise ArgumentError, "Uncompressed key are not allowed." unless compressed_key?(pubkey)
|
27
|
+
Bitcoin::Script.to_p2wpkh(Bitcoin.hash160(pubkey))
|
28
|
+
end
|
29
|
+
|
30
|
+
# generate P2SH embed the argument.
|
31
|
+
# @param [String or Script] script script to be embed.
|
32
|
+
# @return [Bitcoin::Script] P2SH script.
|
33
|
+
def sh(script)
|
34
|
+
script = script.to_hex if script.is_a?(Bitcoin::Script)
|
35
|
+
raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Bitcoin::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.htb.bytesize > Bitcoin::MAX_SCRIPT_ELEMENT_SIZE
|
36
|
+
Bitcoin::Script.to_p2sh(Bitcoin.hash160(script))
|
37
|
+
end
|
38
|
+
|
39
|
+
# generate P2WSH embed the argument.
|
40
|
+
# @param [String or Script] script script to be embed.
|
41
|
+
# @return [Bitcoin::Script] P2WSH script.
|
42
|
+
def wsh(script)
|
43
|
+
script = Bitcoin::Script(script.htb) if script.is_a?(String)
|
44
|
+
raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Bitcoin::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.to_payload.bytesize > Bitcoin::MAX_SCRIPT_ELEMENT_SIZE
|
45
|
+
raise ArgumentError, "Uncompressed key are not allowed." if script.get_pubkeys.any?{|p|!compressed_key?(p)}
|
46
|
+
Bitcoin::Script.to_p2wsh(script)
|
47
|
+
end
|
48
|
+
|
49
|
+
# an alias for the collection of `pk(KEY)` and `pkh(KEY)`.
|
50
|
+
# If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`.
|
51
|
+
# @param [String] key private key or public key with hex format.
|
52
|
+
# @return [Array[Bitcoin::Script]]
|
53
|
+
def combo(key)
|
54
|
+
result = [pk(key), pkh(key)]
|
55
|
+
pubkey = extract_pubkey(key)
|
56
|
+
if compressed_key?(pubkey)
|
57
|
+
result << wpkh(key)
|
58
|
+
result << sh(result.last)
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# generate multisig output for given keys.
|
64
|
+
# @param [Integer] threshold the threshold of multisig.
|
65
|
+
# @param [Array[String]] keys an array of keys.
|
66
|
+
# @return [Bitcoin::Script] multisig script.
|
67
|
+
def multi(threshold, *keys, sort: false)
|
68
|
+
raise ArgumentError, 'Multisig threshold is not valid.' unless threshold.is_a?(Integer)
|
69
|
+
raise ArgumentError, 'Multisig threshold cannot be 0, must be at least 1.' unless threshold > 0
|
70
|
+
raise ArgumentError, 'Multisig threshold cannot be larger than the number of keys.' if threshold > keys.size
|
71
|
+
raise ArgumentError, 'Multisig must have between 1 and 16 keys, inclusive.' if keys.size > 16
|
72
|
+
pubkeys = keys.map{|key| extract_pubkey(key) }
|
73
|
+
Bitcoin::Script.to_multisig_script(threshold, pubkeys, sort: sort)
|
74
|
+
end
|
75
|
+
|
76
|
+
# generate sorted multisig output for given keys.
|
77
|
+
# @param [Integer] threshold the threshold of multisig.
|
78
|
+
# @param [Array[String]] keys an array of keys.
|
79
|
+
# @return [Bitcoin::Script] multisig script.
|
80
|
+
def sortedmulti(threshold, *keys)
|
81
|
+
multi(threshold, *keys, sort: true)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# extract public key from KEY format.
|
87
|
+
# @param [String] key KEY string.
|
88
|
+
# @return [String] public key.
|
89
|
+
def extract_pubkey(key)
|
90
|
+
if key.start_with?('[') # BIP32 fingerprint
|
91
|
+
raise ArgumentError, 'Invalid key origin.' if key.count('[') > 1 || key.count(']') > 1
|
92
|
+
info = key[1...key.index(']')] # TODO
|
93
|
+
fingerprint, *paths = info.split('/')
|
94
|
+
raise ArgumentError, 'Fingerprint is not hex.' unless fingerprint.valid_hex?
|
95
|
+
raise ArgumentError, 'Fingerprint is not 4 bytes.' unless fingerprint.size == 8
|
96
|
+
key = key[(key.index(']') + 1)..-1]
|
97
|
+
else
|
98
|
+
raise ArgumentError, 'Invalid key origin.' if key.include?(']')
|
99
|
+
end
|
100
|
+
|
101
|
+
# check BIP32 derivation path
|
102
|
+
key, *paths = key.split('/')
|
103
|
+
|
104
|
+
if key.start_with?('xprv')
|
105
|
+
key = Bitcoin::ExtKey.from_base58(key)
|
106
|
+
key = derive_path(key, paths, true) if paths
|
107
|
+
elsif key.start_with?('xpub')
|
108
|
+
key = Bitcoin::ExtPubkey.from_base58(key)
|
109
|
+
key = derive_path(key, paths, false) if paths
|
110
|
+
else
|
111
|
+
begin
|
112
|
+
key = Bitcoin::Key.from_wif(key)
|
113
|
+
rescue ArgumentError
|
114
|
+
key_type = compressed_key?(key) ? Bitcoin::Key::TYPES[:compressed] : Bitcoin::Key::TYPES[:uncompressed]
|
115
|
+
key = Bitcoin::Key.new(pubkey: key, key_type: key_type)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
key = key.is_a?(Bitcoin::Key) ? key : key.key
|
119
|
+
raise ArgumentError, 'Invalid pubkey.' unless key.fully_valid_pubkey?
|
120
|
+
key.pubkey
|
121
|
+
end
|
122
|
+
|
123
|
+
def compressed_key?(key)
|
124
|
+
%w(02 03).include?(key[0..1]) && [key].pack("H*").bytesize == 33
|
125
|
+
end
|
126
|
+
|
127
|
+
def derive_path(key, paths, is_private)
|
128
|
+
paths.each do |path|
|
129
|
+
raise ArgumentError, 'xpub can not derive hardened key.' if !is_private && path.end_with?("'")
|
130
|
+
if is_private
|
131
|
+
hardened = path.end_with?("'")
|
132
|
+
path = hardened ? path[0..-2] : path
|
133
|
+
raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
|
134
|
+
raise ArgumentError, 'Key path value is out of range.' if !hardened && path.to_i >= Bitcoin::HARDENED_THRESHOLD
|
135
|
+
key = key.derive(path.to_i, hardened)
|
136
|
+
else
|
137
|
+
raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
|
138
|
+
raise ArgumentError, 'Key path value is out of range.' if path.to_i >= Bitcoin::HARDENED_THRESHOLD
|
139
|
+
key = key.derive(path.to_i)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
key
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|