tapyrus 0.1.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -15
  3. data/exe/tapyrusrbd +2 -2
  4. data/lib/openassets/util.rb +2 -4
  5. data/lib/schnorr.rb +83 -0
  6. data/lib/schnorr/signature.rb +38 -0
  7. data/lib/tapyrus.rb +11 -18
  8. data/lib/tapyrus/block.rb +3 -38
  9. data/lib/tapyrus/block_header.rb +70 -41
  10. data/lib/tapyrus/chain_params.rb +13 -36
  11. data/lib/tapyrus/chainparams/{regtest.yml → dev.yml} +10 -19
  12. data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -19
  13. data/lib/tapyrus/constants.rb +12 -34
  14. data/lib/tapyrus/errors.rb +17 -0
  15. data/lib/tapyrus/ext.rb +5 -0
  16. data/lib/tapyrus/ext/ecdsa.rb +39 -0
  17. data/lib/tapyrus/ext/json_parser.rb +47 -0
  18. data/lib/tapyrus/ext_key.rb +42 -23
  19. data/lib/tapyrus/key.rb +47 -40
  20. data/lib/tapyrus/message.rb +2 -2
  21. data/lib/tapyrus/message/base.rb +1 -0
  22. data/lib/tapyrus/message/block.rb +4 -4
  23. data/lib/tapyrus/message/cmpct_block.rb +3 -5
  24. data/lib/tapyrus/message/header_and_short_ids.rb +1 -1
  25. data/lib/tapyrus/message/headers.rb +1 -1
  26. data/lib/tapyrus/message/merkle_block.rb +1 -1
  27. data/lib/tapyrus/message/tx.rb +2 -2
  28. data/lib/tapyrus/network/peer.rb +1 -15
  29. data/lib/tapyrus/node/cli.rb +15 -11
  30. data/lib/tapyrus/node/configuration.rb +1 -1
  31. data/lib/tapyrus/node/spv.rb +1 -1
  32. data/lib/tapyrus/opcodes.rb +5 -0
  33. data/lib/tapyrus/out_point.rb +1 -1
  34. data/lib/tapyrus/rpc/request_handler.rb +7 -6
  35. data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
  36. data/lib/tapyrus/script/color.rb +79 -0
  37. data/lib/tapyrus/script/multisig.rb +0 -27
  38. data/lib/tapyrus/script/script.rb +74 -89
  39. data/lib/tapyrus/script/script_error.rb +8 -14
  40. data/lib/tapyrus/script/script_interpreter.rb +65 -86
  41. data/lib/tapyrus/script/tx_checker.rb +21 -5
  42. data/lib/tapyrus/secp256k1.rb +1 -0
  43. data/lib/tapyrus/secp256k1/native.rb +93 -20
  44. data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
  45. data/lib/tapyrus/secp256k1/ruby.rb +63 -54
  46. data/lib/tapyrus/store/chain_entry.rb +3 -2
  47. data/lib/tapyrus/store/db/level_db.rb +58 -0
  48. data/lib/tapyrus/store/spv_chain.rb +29 -11
  49. data/lib/tapyrus/tx.rb +18 -160
  50. data/lib/tapyrus/tx_in.rb +4 -11
  51. data/lib/tapyrus/tx_out.rb +2 -1
  52. data/lib/tapyrus/util.rb +8 -0
  53. data/lib/tapyrus/validation.rb +1 -6
  54. data/lib/tapyrus/version.rb +1 -1
  55. data/lib/tapyrus/wallet/account.rb +1 -0
  56. data/lib/tapyrus/wallet/master_key.rb +1 -0
  57. data/tapyrusrb.gemspec +3 -3
  58. metadata +44 -39
  59. data/lib/tapyrus/chainparams/testnet.yml +0 -41
  60. data/lib/tapyrus/descriptor.rb +0 -147
  61. data/lib/tapyrus/script_witness.rb +0 -38
@@ -1,3 +1,3 @@
1
1
  module Tapyrus
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -3,6 +3,7 @@ module Tapyrus
3
3
 
4
4
  # the account in BIP-44
5
5
  class Account
6
+ include Tapyrus::HexConverter
6
7
 
7
8
  PURPOSE_TYPE = {legacy: 44, nested_witness: 49, native_segwit: 84}
8
9
 
@@ -3,6 +3,7 @@ module Tapyrus
3
3
 
4
4
  # HD Wallet master seed
5
5
  class MasterKey
6
+ include Tapyrus::HexConverter
6
7
  extend Tapyrus::Util
7
8
  include Tapyrus::Util
8
9
  include Tapyrus::KeyPath
@@ -23,25 +23,25 @@ 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'
27
26
  spec.add_runtime_dependency 'daemon-spawn'
28
27
  spec.add_runtime_dependency 'thor'
29
28
  spec.add_runtime_dependency 'ffi'
30
29
  spec.add_runtime_dependency 'leb128', '~> 1.0.0'
31
30
  spec.add_runtime_dependency 'eventmachine_httpserver'
32
- spec.add_runtime_dependency 'rest-client'
33
31
  spec.add_runtime_dependency 'iniparse'
34
32
  spec.add_runtime_dependency 'siphash'
35
33
  spec.add_runtime_dependency 'protobuf', '3.8.5'
36
34
  spec.add_runtime_dependency 'scrypt'
37
35
  spec.add_runtime_dependency 'activesupport', '>= 5.2.3'
36
+ spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
38
37
 
39
38
  # for options
40
39
  spec.add_development_dependency 'leveldb-native'
41
40
 
42
41
  spec.add_development_dependency 'bundler'
43
- spec.add_development_dependency 'rake', '~> 10.0'
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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapyrus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-05 00:00:00.000000000 Z
11
+ date: 2020-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: bech32
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 1.0.3
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: 1.0.3
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: daemon-spawn
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -136,20 +122,6 @@ dependencies:
136
122
  - - ">="
137
123
  - !ruby/object:Gem::Version
138
124
  version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: rest-client
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :runtime
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
125
  - !ruby/object:Gem::Dependency
154
126
  name: iniparse
155
127
  requirement: !ruby/object:Gem::Requirement
@@ -220,6 +192,20 @@ dependencies:
220
192
  - - ">="
221
193
  - !ruby/object:Gem::Version
222
194
  version: 5.2.3
195
+ - !ruby/object:Gem::Dependency
196
+ name: json_pure
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 2.3.1
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: 2.3.1
223
209
  - !ruby/object:Gem::Dependency
224
210
  name: leveldb-native
225
211
  requirement: !ruby/object:Gem::Requirement
@@ -252,16 +238,16 @@ dependencies:
252
238
  name: rake
253
239
  requirement: !ruby/object:Gem::Requirement
254
240
  requirements:
255
- - - "~>"
241
+ - - ">="
256
242
  - !ruby/object:Gem::Version
257
- version: '10.0'
243
+ version: 12.3.3
258
244
  type: :development
259
245
  prerelease: false
260
246
  version_requirements: !ruby/object:Gem::Requirement
261
247
  requirements:
262
- - - "~>"
248
+ - - ">="
263
249
  - !ruby/object:Gem::Version
264
- version: '10.0'
250
+ version: 12.3.3
265
251
  - !ruby/object:Gem::Dependency
266
252
  name: rspec
267
253
  requirement: !ruby/object:Gem::Requirement
@@ -290,6 +276,20 @@ dependencies:
290
276
  - - ">="
291
277
  - !ruby/object:Gem::Version
292
278
  version: '0'
279
+ - !ruby/object:Gem::Dependency
280
+ name: webmock
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - "~>"
284
+ - !ruby/object:Gem::Version
285
+ version: '3.0'
286
+ type: :development
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - "~>"
291
+ - !ruby/object:Gem::Version
292
+ version: '3.0'
293
293
  description: "[WIP]The implementation of Tapyrus Protocol for Ruby."
294
294
  email:
295
295
  - azuchi@chaintope.com
@@ -317,17 +317,21 @@ files:
317
317
  - lib/openassets/marker_output.rb
318
318
  - lib/openassets/payload.rb
319
319
  - lib/openassets/util.rb
320
+ - lib/schnorr.rb
321
+ - lib/schnorr/signature.rb
320
322
  - lib/tapyrus.rb
321
323
  - lib/tapyrus/base58.rb
322
324
  - lib/tapyrus/block.rb
323
325
  - lib/tapyrus/block_header.rb
324
326
  - lib/tapyrus/bloom_filter.rb
325
327
  - lib/tapyrus/chain_params.rb
326
- - lib/tapyrus/chainparams/mainnet.yml
327
- - lib/tapyrus/chainparams/regtest.yml
328
- - lib/tapyrus/chainparams/testnet.yml
328
+ - lib/tapyrus/chainparams/dev.yml
329
+ - lib/tapyrus/chainparams/prod.yml
329
330
  - lib/tapyrus/constants.rb
330
- - lib/tapyrus/descriptor.rb
331
+ - lib/tapyrus/errors.rb
332
+ - lib/tapyrus/ext.rb
333
+ - lib/tapyrus/ext/ecdsa.rb
334
+ - lib/tapyrus/ext/json_parser.rb
331
335
  - lib/tapyrus/ext_key.rb
332
336
  - lib/tapyrus/key.rb
333
337
  - lib/tapyrus/key_path.rb
@@ -394,14 +398,15 @@ files:
394
398
  - lib/tapyrus/rpc/http_server.rb
395
399
  - lib/tapyrus/rpc/request_handler.rb
396
400
  - lib/tapyrus/rpc/tapyrus_core_client.rb
401
+ - lib/tapyrus/script/color.rb
397
402
  - lib/tapyrus/script/multisig.rb
398
403
  - lib/tapyrus/script/script.rb
399
404
  - lib/tapyrus/script/script_error.rb
400
405
  - lib/tapyrus/script/script_interpreter.rb
401
406
  - lib/tapyrus/script/tx_checker.rb
402
- - lib/tapyrus/script_witness.rb
403
407
  - lib/tapyrus/secp256k1.rb
404
408
  - lib/tapyrus/secp256k1/native.rb
409
+ - lib/tapyrus/secp256k1/rfc6979.rb
405
410
  - lib/tapyrus/secp256k1/ruby.rb
406
411
  - lib/tapyrus/slip39.rb
407
412
  - lib/tapyrus/slip39/share.rb
@@ -1,41 +0,0 @@
1
- --- !ruby/object:Tapyrus::ChainParams
2
- network: "testnet"
3
- magic_head: "0b110907"
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: "024285ef"
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: 18333
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: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
27
- proof_of_work_limit: 0x1d00ffff
28
- dns_seeds:
29
- - "testnet-seed.bitcoin.jonasschnelli.ch"
30
- - "seed.tbtc.petertodd.org"
31
- - "testnet-seed.bluematt.me"
32
- - "testnet-seed.bitcoin.schildbach.de"
33
- genesis:
34
- hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
35
- merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
36
- time: 1296688602
37
- nonce: 414098458
38
- bits: 0x1d00ffff
39
- version: 1
40
- prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
41
- bip44_coin_type: 1
@@ -1,147 +0,0 @@
1
- module Tapyrus
2
-
3
- module Descriptor
4
-
5
- include Tapyrus::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 [Tapyrus::Script] P2PK script.
10
- def pk(key)
11
- Tapyrus::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 [Tapyrus::Script] P2PKH script.
17
- def pkh(key)
18
- Tapyrus::Script.to_p2pkh(Tapyrus.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 [Tapyrus::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
- Tapyrus::Script.to_p2wpkh(Tapyrus.hash160(pubkey))
28
- end
29
-
30
- # generate P2SH embed the argument.
31
- # @param [String or Script] script script to be embed.
32
- # @return [Tapyrus::Script] P2SH script.
33
- def sh(script)
34
- script = script.to_hex if script.is_a?(Tapyrus::Script)
35
- raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Tapyrus::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.htb.bytesize > Tapyrus::MAX_SCRIPT_ELEMENT_SIZE
36
- Tapyrus::Script.to_p2sh(Tapyrus.hash160(script))
37
- end
38
-
39
- # generate P2WSH embed the argument.
40
- # @param [String or Script] script script to be embed.
41
- # @return [Tapyrus::Script] P2WSH script.
42
- def wsh(script)
43
- script = Tapyrus::Script(script.htb) if script.is_a?(String)
44
- raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Tapyrus::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.to_payload.bytesize > Tapyrus::MAX_SCRIPT_ELEMENT_SIZE
45
- raise ArgumentError, "Uncompressed key are not allowed." if script.get_pubkeys.any?{|p|!compressed_key?(p)}
46
- Tapyrus::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[Tapyrus::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 [Tapyrus::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
- Tapyrus::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 [Tapyrus::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 = Tapyrus::ExtKey.from_base58(key)
106
- key = derive_path(key, paths, true) if paths
107
- elsif key.start_with?('xpub')
108
- key = Tapyrus::ExtPubkey.from_base58(key)
109
- key = derive_path(key, paths, false) if paths
110
- else
111
- begin
112
- key = Tapyrus::Key.from_wif(key)
113
- rescue ArgumentError
114
- key_type = compressed_key?(key) ? Tapyrus::Key::TYPES[:compressed] : Tapyrus::Key::TYPES[:uncompressed]
115
- key = Tapyrus::Key.new(pubkey: key, key_type: key_type)
116
- end
117
- end
118
- key = key.is_a?(Tapyrus::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 >= Tapyrus::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 >= Tapyrus::HARDENED_THRESHOLD
139
- key = key.derive(path.to_i)
140
- end
141
- end
142
- key
143
- end
144
-
145
- end
146
-
147
- end
@@ -1,38 +0,0 @@
1
- module Tapyrus
2
-
3
- # witness
4
- class ScriptWitness
5
-
6
- attr_reader :stack
7
-
8
- def initialize(stack = [])
9
- @stack = stack
10
- end
11
-
12
- def self.parse_from_payload(payload)
13
- buf = payload.is_a?(StringIO) ? payload : StringIO.new(payload)
14
- size = Tapyrus.unpack_var_int_from_io(buf)
15
- stack = size.times.map do
16
- buf.read(Tapyrus.unpack_var_int_from_io(buf))
17
- end
18
- self.new(stack)
19
- end
20
-
21
- def empty?
22
- stack.empty?
23
- end
24
-
25
- def to_payload
26
- p = Tapyrus.pack_var_int(stack.size)
27
- p << stack.map { |s|
28
- Tapyrus.pack_var_int(s.bytesize) << s
29
- }.join
30
- end
31
-
32
- def to_s
33
- stack.map{|s|s.bth}.join(' ')
34
- end
35
-
36
- end
37
-
38
- end