tapyrus 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/{.prettierrc.yaml → .prettierrc.yml} +0 -1
  4. data/.ruby-version +1 -1
  5. data/Gemfile +2 -2
  6. data/README.md +1 -1
  7. data/Rakefile +2 -2
  8. data/exe/tapyrus-script-debugger +5 -5
  9. data/exe/tapyrusrb-cli +1 -1
  10. data/exe/tapyrusrbd +18 -17
  11. data/lib/openassets/payload.rb +2 -2
  12. data/lib/openassets/util.rb +2 -2
  13. data/lib/openassets.rb +4 -4
  14. data/lib/schnorr/sign_to_contract.rb +4 -4
  15. data/lib/schnorr/signature.rb +5 -5
  16. data/lib/schnorr.rb +14 -14
  17. data/lib/tapyrus/base58.rb +8 -8
  18. data/lib/tapyrus/bip175.rb +5 -5
  19. data/lib/tapyrus/block_header.rb +2 -2
  20. data/lib/tapyrus/bloom_filter.rb +1 -1
  21. data/lib/tapyrus/chain_params.rb +5 -5
  22. data/lib/tapyrus/constants.rb +1 -1
  23. data/lib/tapyrus/errors.rb +9 -9
  24. data/lib/tapyrus/ext/ecdsa.rb +4 -4
  25. data/lib/tapyrus/ext/json_parser.rb +1 -1
  26. data/lib/tapyrus/ext.rb +3 -3
  27. data/lib/tapyrus/ext_key.rb +21 -21
  28. data/lib/tapyrus/jws.rb +76 -0
  29. data/lib/tapyrus/key.rb +48 -20
  30. data/lib/tapyrus/key_path.rb +3 -3
  31. data/lib/tapyrus/logger.rb +4 -11
  32. data/lib/tapyrus/merkle_tree.rb +1 -1
  33. data/lib/tapyrus/message/addr.rb +2 -2
  34. data/lib/tapyrus/message/base.rb +2 -2
  35. data/lib/tapyrus/message/block.rb +1 -1
  36. data/lib/tapyrus/message/block_txn.rb +1 -1
  37. data/lib/tapyrus/message/cmpct_block.rb +1 -1
  38. data/lib/tapyrus/message/fee_filter.rb +3 -3
  39. data/lib/tapyrus/message/filter_add.rb +1 -1
  40. data/lib/tapyrus/message/filter_clear.rb +2 -2
  41. data/lib/tapyrus/message/filter_load.rb +7 -7
  42. data/lib/tapyrus/message/get_addr.rb +2 -2
  43. data/lib/tapyrus/message/get_block_txn.rb +1 -1
  44. data/lib/tapyrus/message/get_blocks.rb +1 -1
  45. data/lib/tapyrus/message/get_data.rb +1 -1
  46. data/lib/tapyrus/message/get_headers.rb +1 -1
  47. data/lib/tapyrus/message/header_and_short_ids.rb +6 -6
  48. data/lib/tapyrus/message/headers.rb +1 -1
  49. data/lib/tapyrus/message/headers_parser.rb +2 -2
  50. data/lib/tapyrus/message/inv.rb +1 -1
  51. data/lib/tapyrus/message/inventory.rb +3 -3
  52. data/lib/tapyrus/message/mem_pool.rb +2 -2
  53. data/lib/tapyrus/message/merkle_block.rb +3 -3
  54. data/lib/tapyrus/message/network_addr.rb +9 -9
  55. data/lib/tapyrus/message/not_found.rb +1 -1
  56. data/lib/tapyrus/message/ping.rb +3 -3
  57. data/lib/tapyrus/message/pong.rb +3 -3
  58. data/lib/tapyrus/message/reject.rb +6 -6
  59. data/lib/tapyrus/message/send_cmpct.rb +4 -4
  60. data/lib/tapyrus/message/send_headers.rb +2 -2
  61. data/lib/tapyrus/message/tx.rb +1 -1
  62. data/lib/tapyrus/message/ver_ack.rb +2 -2
  63. data/lib/tapyrus/message/version.rb +7 -7
  64. data/lib/tapyrus/message.rb +37 -37
  65. data/lib/tapyrus/mnemonic.rb +18 -16
  66. data/lib/tapyrus/network/connection.rb +2 -2
  67. data/lib/tapyrus/network/message_handler.rb +11 -11
  68. data/lib/tapyrus/network/peer.rb +1 -1
  69. data/lib/tapyrus/network/peer_discovery.rb +1 -1
  70. data/lib/tapyrus/network/pool.rb +4 -4
  71. data/lib/tapyrus/network.rb +6 -6
  72. data/lib/tapyrus/node/cli.rb +34 -34
  73. data/lib/tapyrus/node/configuration.rb +3 -3
  74. data/lib/tapyrus/node/spv.rb +2 -2
  75. data/lib/tapyrus/node.rb +3 -3
  76. data/lib/tapyrus/opcodes.rb +7 -7
  77. data/lib/tapyrus/out_point.rb +2 -2
  78. data/lib/tapyrus/rpc/http_server.rb +6 -6
  79. data/lib/tapyrus/rpc/request_handler.rb +5 -5
  80. data/lib/tapyrus/rpc/tapyrus_core_client.rb +10 -10
  81. data/lib/tapyrus/rpc.rb +4 -4
  82. data/lib/tapyrus/script/color.rb +3 -3
  83. data/lib/tapyrus/script/debugger.rb +9 -9
  84. data/lib/tapyrus/script/multisig.rb +2 -2
  85. data/lib/tapyrus/script/script.rb +28 -28
  86. data/lib/tapyrus/script/script_error.rb +42 -42
  87. data/lib/tapyrus/script/script_interpreter.rb +9 -9
  88. data/lib/tapyrus/script/tx_checker.rb +2 -2
  89. data/lib/tapyrus/secp256k1/native.rb +23 -23
  90. data/lib/tapyrus/secp256k1/rfc6979.rb +7 -7
  91. data/lib/tapyrus/secp256k1/ruby.rb +2 -2
  92. data/lib/tapyrus/secp256k1.rb +3 -3
  93. data/lib/tapyrus/slip39/share.rb +7 -7
  94. data/lib/tapyrus/slip39/sss.rb +24 -24
  95. data/lib/tapyrus/slip39.rb +514 -37
  96. data/lib/tapyrus/store/db/level_db.rb +2 -2
  97. data/lib/tapyrus/store/db.rb +1 -1
  98. data/lib/tapyrus/store/spv_chain.rb +7 -7
  99. data/lib/tapyrus/store.rb +3 -3
  100. data/lib/tapyrus/tip0137.rb +201 -0
  101. data/lib/tapyrus/tx.rb +12 -12
  102. data/lib/tapyrus/tx_builder.rb +3 -3
  103. data/lib/tapyrus/tx_in.rb +3 -3
  104. data/lib/tapyrus/tx_out.rb +3 -3
  105. data/lib/tapyrus/util.rb +21 -21
  106. data/lib/tapyrus/validation.rb +9 -9
  107. data/lib/tapyrus/version.rb +1 -1
  108. data/lib/tapyrus/wallet/account.rb +12 -12
  109. data/lib/tapyrus/wallet/base.rb +9 -8
  110. data/lib/tapyrus/wallet/db.rb +11 -11
  111. data/lib/tapyrus/wallet/master_key.rb +13 -13
  112. data/lib/tapyrus/wallet.rb +4 -4
  113. data/lib/tapyrus.rb +62 -59
  114. data/tapyrusrb.gemspec +33 -31
  115. metadata +30 -14
@@ -1,4 +1,4 @@
1
- require 'leveldb-native'
1
+ require "leveldb-native"
2
2
 
3
3
  module Tapyrus
4
4
  module Store
@@ -134,7 +134,7 @@ module Tapyrus
134
134
  unless tip_block.block_hash == entry.prev_hash
135
135
  raise "entry(#{entry.block_hash}) does not reference current best block hash(#{tip_block.block_hash})"
136
136
  end
137
- raise 'block height is small than current best block.' unless tip_block.height + 1 == entry.height
137
+ raise "block height is small than current best block." unless tip_block.height + 1 == entry.height
138
138
  end
139
139
  db.put(KEY_PREFIX[:best], entry.block_hash)
140
140
  db.put(KEY_PREFIX[:next] + entry.prev_hash, entry.block_hash)
@@ -1,7 +1,7 @@
1
1
  module Tapyrus
2
2
  module Store
3
3
  module DB
4
- autoload :LevelDB, 'tapyrus/store/db/level_db'
4
+ autoload :LevelDB, "tapyrus/store/db/level_db"
5
5
  end
6
6
  end
7
7
  end
@@ -1,12 +1,12 @@
1
1
  module Tapyrus
2
2
  module Store
3
3
  KEY_PREFIX = {
4
- entry: 'e', # key: block hash, value: Tapyrus::Store::ChainEntry payload
5
- height: 'h', # key: block height, value: block hash.
6
- best: 'B', # value: best block hash.
7
- next: 'n', # key: block hash, value: A hash of the next block of the specified hash
8
- agg_pubkey: 'a', # key: index, value: Activated block height | aggregated public key.
9
- latest_agg_pubkey: 'g' # value: latest agg pubkey index.
4
+ entry: "e", # key: block hash, value: Tapyrus::Store::ChainEntry payload
5
+ height: "h", # key: block height, value: block hash.
6
+ best: "B", # value: best block hash.
7
+ next: "n", # key: block hash, value: A hash of the next block of the specified hash
8
+ agg_pubkey: "a", # key: index, value: Activated block height | aggregated public key.
9
+ latest_agg_pubkey: "g" # value: latest agg pubkey index.
10
10
  }
11
11
 
12
12
  class SPVChain
@@ -17,7 +17,7 @@ module Tapyrus
17
17
  # @param[Tapyrus::Store::DB::LevelDB] db
18
18
  # @param[Tapyrus::Block] genesis genesis block
19
19
  def initialize(db = Tapyrus::Store::DB::LevelDB.new, genesis: nil)
20
- raise ArgumentError, 'genesis block should be specified.' unless genesis
20
+ raise ArgumentError, "genesis block should be specified." unless genesis
21
21
  @db = db # TODO multiple db switch
22
22
  @logger = Tapyrus::Logger.create(:debug)
23
23
  initialize_block(genesis)
data/lib/tapyrus/store.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Tapyrus
2
2
  module Store
3
- autoload :DB, 'tapyrus/store/db'
4
- autoload :SPVChain, 'tapyrus/store/spv_chain'
5
- autoload :ChainEntry, 'tapyrus/store/chain_entry'
3
+ autoload :DB, "tapyrus/store/db"
4
+ autoload :SPVChain, "tapyrus/store/spv_chain"
5
+ autoload :ChainEntry, "tapyrus/store/chain_entry"
6
6
  end
7
7
  end
@@ -0,0 +1,201 @@
1
+ module Tapyrus
2
+ module TIP0137
3
+ # @param key [Tapyrus::Key] A private key
4
+ # @param txid [String] A transaction id string in hexadecimal format (64 characters long)
5
+ # @param index [Integer] Index of the transaction output, a non-negative integer less than 2^32
6
+ # @param color_id [Tapyrus::Color::ColorIdentifier] A valid instance of Tapyrus::Color::ColorIdentifier
7
+ # @param value [Integer] A non-negative integer less than 2^64 representing the value of the transaction output
8
+ # @param script_pubkey [Tapyrus::Script] A script pubkey in the transaction output
9
+ # @param address [String] A valid Tapyrus address string for the transaction output
10
+ # @param message [String] A hexadecimal formatted string
11
+ # @param client [Tapyrus::RPC::TapyrusCoreClient] The RPC client instance. If the client is specified, verify that the transaction associated with the txid and index exists on the blockchain and that a valid script_pubkey exists in the transaction.
12
+ # @return [String] Returns the message signed in JWS Format
13
+ # @raise [ArgumentError] If the txid is not a 64-character hexadecimal string
14
+ # @raise [ArgumentError] If the index is not a non-negative integer less than 2^32
15
+ # @raise [ArgumentError] If the color_id is not a valid Tapyrus::Color::ColorIdentifier object
16
+ # @raise [ArgumentError] If the value is not a non-negative integer less than 2^64
17
+ # @raise [ArgumentError] If the script_pubkey is not a valid Tapyrus::Script object
18
+ # @raise [ArgumentError] If the provided Tapyrus address is invalid
19
+ # @raise [ArgumentError] If the message is not a hexadecimal string
20
+ # @raise [ArgumentError] If the transaction with the given txid and index is not found in the blockchain
21
+ # @raise [ArgumentError] If the script and value do not correspond with those in the blockchain
22
+ # @raise [Tapyrus::RPC::Error] If RPC access fails
23
+ def sign_message!(
24
+ key,
25
+ txid:,
26
+ index:,
27
+ value:,
28
+ script_pubkey:,
29
+ color_id: nil,
30
+ address: nil,
31
+ message: nil,
32
+ client: nil
33
+ )
34
+ validate_payload!(
35
+ key: key,
36
+ txid: txid,
37
+ index: index,
38
+ color_id: color_id,
39
+ value: value,
40
+ script_pubkey: script_pubkey,
41
+ address: address,
42
+ message: message
43
+ )
44
+ if client
45
+ validate_on_blockchain!(
46
+ client: client,
47
+ txid: txid,
48
+ index: index,
49
+ color_id: color_id,
50
+ value: value,
51
+ script_pubkey: script_pubkey
52
+ )
53
+ end
54
+ data = {
55
+ txid: txid,
56
+ index: index,
57
+ color_id: color_id&.to_hex,
58
+ value: value,
59
+ script_pubkey: script_pubkey&.to_hex,
60
+ address: address,
61
+ message: message
62
+ }
63
+ Tapyrus::JWS.encode(data, key.priv_key)
64
+ end
65
+
66
+ # @param jws [String] JWS (JSON Web Signature)
67
+ # @param client [Tapyrus::RPC::TapyrusCoreClient] The RPC client instance. If the client is specified, verify that the transaction associated with the txid and index exists on the blockchain and that a valid script_pubkey exists in the transaction.
68
+ # @return A decoded JSON Object
69
+ # @raise [ArgumentError] If the decoded txid is not a 64-character hexadecimal string
70
+ # @raise [ArgumentError] If the decoded index is not a non-negative integer less than 2^32
71
+ # @raise [ArgumentError] If the decoded color_id is an invalid Tapyrus::Color::ColorIdentifier object
72
+ # @raise [ArgumentError] If the decoded value is not a non-negative integer less than 2^64
73
+ # @raise [ArgumentError] If the decoded script_pubkey is an invalid Tapyrus::Script object
74
+ # @raise [ArgumentError] If the decoded address is an invalid Tapyrus address
75
+ # @raise [ArgumentError] If the decoded message is not a hexadecimal string
76
+ # @raise [ArgumentError] If a transaction is not found with the decoded txid and index in the blockchain
77
+ # @raise [ArgumentError] If the decoded script and value do not match the ones in the blockchain
78
+ # @raise [JWT::DecodeError] If JWS decoding fails
79
+ # @raise [Tapyus::JWS::DecodeError] If the JWK key is invalid
80
+ # @raise [JWT::VerificationError] If the verification of the signature fails
81
+ # @raise [Tapyrus::RPC::Error] If RPC access fails
82
+ def verify_message!(jws, client: nil)
83
+ Tapyrus::JWS
84
+ .decode(jws)
85
+ .tap do |decoded|
86
+ header = decoded[1]
87
+ validate_header!(header)
88
+ jwk = header.dig("jwk", "keys", 0)
89
+ key = to_tapyrus_key(jwk)
90
+ payload = decoded[0]
91
+ color_id =
92
+ begin
93
+ Tapyrus::Color::ColorIdentifier.parse_from_payload(payload["color_id"]&.htb) if payload["color_id"]
94
+ rescue => _e
95
+ raise ArgumentError, "color_id is invalid"
96
+ end
97
+ script_pubkey =
98
+ begin
99
+ Tapyrus::Script.parse_from_payload(payload["script_pubkey"]&.htb)
100
+ rescue => _e
101
+ raise ArgumentError, "script_pubkey is invalid"
102
+ end
103
+ validate_payload!(
104
+ key: key,
105
+ txid: payload["txid"],
106
+ index: payload["index"],
107
+ color_id: color_id,
108
+ value: payload["value"],
109
+ script_pubkey: script_pubkey,
110
+ address: payload["address"],
111
+ message: payload["message"]
112
+ )
113
+ if client
114
+ validate_on_blockchain!(
115
+ client: client,
116
+ txid: payload["txid"],
117
+ index: payload["index"],
118
+ color_id: color_id,
119
+ value: payload["value"],
120
+ script_pubkey: script_pubkey
121
+ )
122
+ end
123
+ end
124
+ end
125
+
126
+ # @param jws [String] JWS (JSON Web Signature)
127
+ # @param client [Tapyrus::RPC::TapyrusCoreClient] The RPC client instance
128
+ # @return [Boolean] true if JWT and decoded object is valid.
129
+ def verify_message(jws, client: nil)
130
+ verify_message!(jws, client: client)
131
+ true
132
+ rescue ArgumentError, JWT::DecodeError, JWT::VerificationError
133
+ return false
134
+ end
135
+
136
+ def validate_header!(header)
137
+ raise ArgumentError, 'type must be "JWT"' if header["typ"] && header["typ"] != "JWT"
138
+ raise ArgumentError, 'alg must be "ES256K"' if header["alg"] && header["alg"] != Tapyrus::JWS::ALGO
139
+ end
140
+
141
+ def validate_payload!(key:, txid:, index:, value:, script_pubkey:, color_id: nil, address: nil, message: nil)
142
+ raise ArgumentError, "txid is invalid" if !txid || !/^[0-9a-fA-F]{64}$/.match(txid)
143
+ raise ArgumentError, "index is invalid" if !index || !/^\d+$/.match(index.to_s) || index < 0 || index >= 2**32
144
+
145
+ raise ArgumentError, "value is invalid" if !value || !/^\d+$/.match(value.to_s) || value < 0 || value >= 2**64
146
+ if !script_pubkey || !script_pubkey.is_a?(Tapyrus::Script) || !(script_pubkey.p2pkh? || script_pubkey.cp2pkh?)
147
+ raise ArgumentError,
148
+ "script_pubkey is invalid. scirpt_pubkey must be a hex string and its type must be p2pkh or cp2pkh"
149
+ end
150
+
151
+ if color_id
152
+ if !color_id.is_a?(Tapyrus::Color::ColorIdentifier) || !color_id.valid?
153
+ raise ArgumentError, "color_id is invalid"
154
+ end
155
+
156
+ raise ArgumentError, "color_id should be equal to colorId in scriptPubkey" if color_id != script_pubkey.color_id
157
+ end
158
+
159
+ begin
160
+ address && Base58.decode(address)
161
+ rescue ArgumentError => e
162
+ raise ArgumentError, "address is invalid"
163
+ end
164
+
165
+ if address && script_pubkey.to_addr != address
166
+ raise ArgumentError, "address is invalid. An address should be derived from scriptPubkey"
167
+ end
168
+
169
+ if (script_pubkey.p2pkh? && key.to_p2pkh != script_pubkey.to_addr) ||
170
+ (script_pubkey.cp2pkh? && key.to_p2pkh != script_pubkey.remove_color.to_addr)
171
+ raise ArgumentError, "key is invalid"
172
+ end
173
+
174
+ if message && !/^([0-9a-fA-F]{2})+$/.match(message)
175
+ raise ArgumentError, "message is invalid. message must be a hex string"
176
+ end
177
+ end
178
+
179
+ def validate_on_blockchain!(client: nil, txid: nil, index: nil, color_id: nil, value: nil, script_pubkey: nil)
180
+ raw_tx = client.getrawtransaction(txid)
181
+ tx = Tapyrus::Tx.parse_from_payload(raw_tx.htb)
182
+ output = tx.outputs[index]
183
+ raise ArgumentError, "output not found in blockchain" unless output
184
+ if color_id != output.color_id
185
+ raise ArgumentError, "color_id of transaction in blockchain is not match to one in the signed message"
186
+ end
187
+ if value != output.value
188
+ raise ArgumentError, "value of transaction in blockchain is not match to one in the signed message"
189
+ end
190
+ if script_pubkey != output.script_pubkey
191
+ raise ArgumentError, "script_pubkey of transaction in blockchain is not match to one in the signed message"
192
+ end
193
+ end
194
+
195
+ def to_tapyrus_key(jwk)
196
+ vefiry_key = JWT::JWK.new(jwk).verify_key
197
+ pubkey = vefiry_key.public_key.to_octet_string(:compressed)
198
+ Tapyrus::Key.new(pubkey: pubkey.bth)
199
+ end
200
+ end
201
+ end
data/lib/tapyrus/tx.rb CHANGED
@@ -29,7 +29,7 @@ module Tapyrus
29
29
  def self.parse_from_payload(payload)
30
30
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
31
31
  tx = new
32
- tx.features = buf.read(4).unpack('V').first
32
+ tx.features = buf.read(4).unpack("V").first
33
33
 
34
34
  in_count = Tapyrus.unpack_var_int_from_io(buf)
35
35
 
@@ -38,7 +38,7 @@ module Tapyrus
38
38
  out_count = Tapyrus.unpack_var_int_from_io(buf)
39
39
  out_count.times { tx.outputs << TxOut.parse_from_payload(buf) }
40
40
 
41
- tx.lock_time = buf.read(4).unpack('V').first
41
+ tx.lock_time = buf.read(4).unpack("V").first
42
42
 
43
43
  tx
44
44
  end
@@ -52,18 +52,18 @@ module Tapyrus
52
52
  end
53
53
 
54
54
  def txid
55
- buf = [features].pack('V')
55
+ buf = [features].pack("V")
56
56
  buf << Tapyrus.pack_var_int(inputs.length) << inputs.map { |i| i.to_payload(use_malfix: true) }.join
57
57
  buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
58
- buf << [lock_time].pack('V')
58
+ buf << [lock_time].pack("V")
59
59
  Tapyrus.double_sha256(buf).reverse.bth
60
60
  end
61
61
 
62
62
  def to_payload
63
- buf = [features].pack('V')
63
+ buf = [features].pack("V")
64
64
  buf << Tapyrus.pack_var_int(inputs.length) << inputs.map(&:to_payload).join
65
65
  buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
66
- buf << [lock_time].pack('V')
66
+ buf << [lock_time].pack("V")
67
67
  buf
68
68
  end
69
69
 
@@ -109,9 +109,9 @@ module Tapyrus
109
109
  # @param [Tapyrus::Script] output_script script pubkey or script code. if script pubkey is P2SH, set redeem script to this.
110
110
  # @return [String] sighash
111
111
  def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all])
112
- raise ArgumentError, 'input_index must be specified.' unless input_index
113
- raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
114
- raise ArgumentError, 'script_pubkey must be specified.' unless output_script
112
+ raise ArgumentError, "input_index must be specified." unless input_index
113
+ raise ArgumentError, "does not exist input corresponding to input_index." if input_index >= inputs.size
114
+ raise ArgumentError, "script_pubkey must be specified." unless output_script
115
115
  sighash_for_legacy(input_index, output_script, hash_type)
116
116
  end
117
117
 
@@ -167,7 +167,7 @@ module Tapyrus
167
167
 
168
168
  case hash_type & 0x1f
169
169
  when SIGHASH_TYPE[:none]
170
- outs = ''
170
+ outs = ""
171
171
  out_size = Tapyrus.pack_var_int(0)
172
172
  when SIGHASH_TYPE[:single]
173
173
  return "\x01".ljust(32, "\x00") if index >= outputs.size
@@ -179,12 +179,12 @@ module Tapyrus
179
179
  ins = [ins[index]] if hash_type & SIGHASH_TYPE[:anyonecanpay] != 0
180
180
 
181
181
  buf = [
182
- [features].pack('V'),
182
+ [features].pack("V"),
183
183
  Tapyrus.pack_var_int(ins.size),
184
184
  ins,
185
185
  out_size,
186
186
  outs,
187
- [lock_time, hash_type].pack('VV')
187
+ [lock_time, hash_type].pack("VV")
188
188
  ].join
189
189
 
190
190
  Tapyrus.double_sha256(buf)
@@ -85,12 +85,12 @@ module Tapyrus
85
85
  script_pubkey = Tapyrus::Script.parse_from_addr(address)
86
86
 
87
87
  unless color_id.default?
88
- raise ArgumentError, 'invalid address' if !script_pubkey.p2pkh? && !script_pubkey.p2sh?
88
+ raise ArgumentError, "invalid address" if !script_pubkey.p2pkh? && !script_pubkey.p2sh?
89
89
  script_pubkey = script_pubkey.add_color(color_id)
90
90
  end
91
91
 
92
92
  output = Tapyrus::TxOut.new(script_pubkey: script_pubkey, value: value)
93
- raise ArgumentError, 'The transaction amount is too small' if color_id.default? && output.dust?
93
+ raise ArgumentError, "The transaction amount is too small" if color_id.default? && output.dust?
94
94
  @outgoings[color_id] ||= 0
95
95
  @outgoings[color_id] += value
96
96
  @outputs << output
@@ -119,7 +119,7 @@ module Tapyrus
119
119
  # @param address [String] p2pkh or p2sh address.
120
120
  def change_address(address)
121
121
  script_pubkey = Tapyrus::Script.parse_from_addr(address)
122
- raise ArgumentError, 'invalid address' if !script_pubkey.p2pkh? && !script_pubkey.p2sh?
122
+ raise ArgumentError, "invalid address" if !script_pubkey.p2pkh? && !script_pubkey.p2sh?
123
123
  @change_script_pubkey = script_pubkey
124
124
  self
125
125
  end
data/lib/tapyrus/tx_in.rb CHANGED
@@ -30,7 +30,7 @@ module Tapyrus
30
30
  def self.parse_from_payload(payload)
31
31
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
32
32
  i = new
33
- hash, index = buf.read(36).unpack('a32V')
33
+ hash, index = buf.read(36).unpack("a32V")
34
34
  i.out_point = OutPoint.new(hash.bth, index)
35
35
  sig_length = Tapyrus.unpack_var_int_from_io(buf)
36
36
  if sig_length == 0
@@ -38,7 +38,7 @@ module Tapyrus
38
38
  else
39
39
  i.script_sig = Script.parse_from_payload(buf.read(sig_length))
40
40
  end
41
- i.sequence = buf.read(4).unpack('V').first
41
+ i.sequence = buf.read(4).unpack("V").first
42
42
  i
43
43
  end
44
44
 
@@ -52,7 +52,7 @@ module Tapyrus
52
52
  p << Tapyrus.pack_var_int(script_sig.to_payload.bytesize)
53
53
  p << script_sig.to_payload
54
54
  end
55
- p << [sequence].pack('V')
55
+ p << [sequence].pack("V")
56
56
  p
57
57
  end
58
58
 
@@ -17,18 +17,18 @@ module Tapyrus
17
17
 
18
18
  def self.parse_from_payload(payload)
19
19
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
20
- value = buf.read(8).unpack('q').first
20
+ value = buf.read(8).unpack("q").first
21
21
  script_size = Tapyrus.unpack_var_int_from_io(buf)
22
22
  new(value: value, script_pubkey: Script.parse_from_payload(buf.read(script_size)))
23
23
  end
24
24
 
25
25
  def to_payload
26
26
  s = script_pubkey.to_payload
27
- [value].pack('Q') << Tapyrus.pack_var_int(s.length) << s
27
+ [value].pack("Q") << Tapyrus.pack_var_int(s.length) << s
28
28
  end
29
29
 
30
30
  def to_empty_payload
31
- 'ffffffffffffffff00'.htb
31
+ "ffffffffffffffff00".htb
32
32
  end
33
33
 
34
34
  # convert satoshi to btc
data/lib/tapyrus/util.rb CHANGED
@@ -17,13 +17,13 @@ module Tapyrus
17
17
 
18
18
  def pack_var_int(i)
19
19
  if i < 0xfd
20
- [i].pack('C')
20
+ [i].pack("C")
21
21
  elsif i <= 0xffff
22
- [0xfd, i].pack('Cv')
22
+ [0xfd, i].pack("Cv")
23
23
  elsif i <= 0xffffffff
24
- [0xfe, i].pack('CV')
24
+ [0xfe, i].pack("CV")
25
25
  elsif i <= 0xffffffffffffffff
26
- [0xff, i].pack('CQ')
26
+ [0xff, i].pack("CQ")
27
27
  else
28
28
  raise "int(#{i}) too large!"
29
29
  end
@@ -31,39 +31,39 @@ module Tapyrus
31
31
 
32
32
  # @return an integer for a valid payload, otherwise nil
33
33
  def unpack_var_int(payload)
34
- case payload.unpack('C').first
34
+ case payload.unpack("C").first
35
35
  when 0xfd
36
- payload.unpack('xva*')
36
+ payload.unpack("xva*")
37
37
  when 0xfe
38
- payload.unpack('xVa*')
38
+ payload.unpack("xVa*")
39
39
  when 0xff
40
- payload.unpack('xQa*')
40
+ payload.unpack("xQa*")
41
41
  else
42
- payload.unpack('Ca*')
42
+ payload.unpack("Ca*")
43
43
  end
44
44
  end
45
45
 
46
46
  # @return an integer for a valid payload, otherwise nil
47
47
  def unpack_var_int_from_io(buf)
48
- uchar = buf.read(1)&.unpack('C')&.first
48
+ uchar = buf.read(1)&.unpack("C")&.first
49
49
  case uchar
50
50
  when 0xfd
51
- buf.read(2)&.unpack('v')&.first
51
+ buf.read(2)&.unpack("v")&.first
52
52
  when 0xfe
53
- buf.read(4)&.unpack('V')&.first
53
+ buf.read(4)&.unpack("V")&.first
54
54
  when 0xff
55
- buf.read(8)&.unpack('Q')&.first
55
+ buf.read(8)&.unpack("Q")&.first
56
56
  else
57
57
  uchar
58
58
  end
59
59
  end
60
60
 
61
61
  def pack_boolean(b)
62
- b ? [0x01].pack('C') : [0x00].pack('C')
62
+ b ? [0x01].pack("C") : [0x00].pack("C")
63
63
  end
64
64
 
65
65
  def unpack_boolean(payload)
66
- data, payload = payload.unpack('Ca*')
66
+ data, payload = payload.unpack("Ca*")
67
67
  [(data.zero? ? false : true), payload]
68
68
  end
69
69
 
@@ -77,7 +77,7 @@ module Tapyrus
77
77
 
78
78
  # byte convert to the sequence of bits packed eight in a byte with the least significant bit first.
79
79
  def byte_to_bit(byte)
80
- byte.unpack('b*').first
80
+ byte.unpack("b*").first
81
81
  end
82
82
 
83
83
  # padding zero to the left of binary string until bytesize.
@@ -86,7 +86,7 @@ module Tapyrus
86
86
  # @return [String] padded binary string.
87
87
  def padding_zero(binary, bytesize)
88
88
  return binary unless binary.bytesize < bytesize
89
- ('00' * (bytesize - binary.bytesize)).htb + binary
89
+ ("00" * (bytesize - binary.bytesize)).htb + binary
90
90
  end
91
91
 
92
92
  # generate sha256-ripemd160 hash for value
@@ -110,16 +110,16 @@ module Tapyrus
110
110
  hex = Base58.decode(addr)
111
111
  if hex.size == 50 && calc_checksum(hex[0...-8]) == hex[-8..-1]
112
112
  unless [Tapyrus.chain_params.address_version, Tapyrus.chain_params.p2sh_version].include?(hex[0..1])
113
- raise 'Invalid version bytes.'
113
+ raise "Invalid version bytes."
114
114
  end
115
115
  [hex[2...-8], hex[0..1]]
116
116
  elsif hex.size == 116 && calc_checksum(hex[0...-8]) == hex[-8..-1]
117
117
  unless [Tapyrus.chain_params.cp2pkh_version, Tapyrus.chain_params.cp2sh_version].include?(hex[0..1])
118
- raise 'Invalid version bytes.'
118
+ raise "Invalid version bytes."
119
119
  end
120
120
  [hex[2...-8], hex[0..1]]
121
121
  else
122
- raise 'Invalid address.'
122
+ raise "Invalid address."
123
123
  end
124
124
  end
125
125
 
@@ -127,7 +127,7 @@ module Tapyrus
127
127
  double_sha256(hex.htb).bth[0..7]
128
128
  end
129
129
 
130
- DIGEST_NAME_SHA256 = 'sha256'
130
+ DIGEST_NAME_SHA256 = "sha256"
131
131
 
132
132
  def hmac_sha256(key, data)
133
133
  OpenSSL::HMAC.digest(DIGEST_NAME_SHA256, key, data)
@@ -4,42 +4,42 @@ module Tapyrus
4
4
  def check_tx(tx, state)
5
5
  # Basic checks that don't depend on any context
6
6
  if tx.inputs.empty?
7
- return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vin-empty')
7
+ return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-vin-empty")
8
8
  end
9
9
 
10
10
  if tx.outputs.empty?
11
- return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-empty')
11
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-vout-empty")
12
12
  end
13
13
 
14
14
  # Check for negative or overflow output values
15
15
  amount = 0
16
16
  tx.outputs.each do |o|
17
17
  if o.value < 0
18
- return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-negative')
18
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-vout-negative")
19
19
  end
20
20
  if MAX_MONEY < o.value
21
- return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-toolarge')
21
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-vout-toolarge")
22
22
  end
23
23
  amount += o.value
24
24
  if MAX_MONEY < amount
25
- return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-toolarge')
25
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-vout-toolarge")
26
26
  end
27
27
  end
28
28
 
29
29
  # Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
30
30
  out_points = tx.inputs.map { |i| i.out_point.to_payload }
31
31
  unless out_points.size == out_points.uniq.size
32
- return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-inputs-duplicate')
32
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-inputs-duplicate")
33
33
  end
34
34
 
35
35
  if tx.coinbase_tx?
36
36
  if tx.inputs[0].out_point.index == 0xffffffff || tx.inputs[0].script_sig.size > 100
37
- return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-cb-length')
37
+ return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-cb-length")
38
38
  end
39
39
  else
40
40
  tx.inputs.each do |i|
41
41
  if i.out_point.nil? || !i.out_point.valid?
42
- return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-prevout-null')
42
+ return state.DoS(10, reject_code: Message::Reject::CODE_INVALID, reject_reason: "bad-txns-prevout-null")
43
43
  end
44
44
  end
45
45
  end
@@ -85,7 +85,7 @@ module Tapyrus
85
85
  @corruption_possible = false
86
86
  end
87
87
 
88
- def DoS(level, ret: false, reject_code: 0, reject_reason: '', corruption_in: false, debug_message: '')
88
+ def DoS(level, ret: false, reject_code: 0, reject_reason: "", corruption_in: false, debug_message: "")
89
89
  @reject_code = reject_code
90
90
  @reject_reason = reject_reason
91
91
  @corruption_possible = corruption_in
@@ -1,3 +1,3 @@
1
1
  module Tapyrus
2
- VERSION = '0.3.4'
2
+ VERSION = "0.3.5"
3
3
  end
@@ -15,7 +15,7 @@ module Tapyrus
15
15
  attr_accessor :lookahead
16
16
  attr_accessor :wallet
17
17
 
18
- def initialize(account_key, purpose = PURPOSE_TYPE[:native_segwit], index = 0, name = '')
18
+ def initialize(account_key, purpose = PURPOSE_TYPE[:native_segwit], index = 0, name = "")
19
19
  validate_params!(account_key, purpose, index)
20
20
  @purpose = purpose
21
21
  @index = index
@@ -31,8 +31,8 @@ module Tapyrus
31
31
  account_key = Tapyrus::ExtPubkey.parse_from_payload(buf.read(78))
32
32
  payload = buf.read
33
33
  name, payload = Tapyrus.unpack_var_string(payload)
34
- name = name.force_encoding('utf-8')
35
- purpose, index, receive_depth, change_depth, lookahead = payload.unpack('I*')
34
+ name = name.force_encoding("utf-8")
35
+ purpose, index, receive_depth, change_depth, lookahead = payload.unpack("I*")
36
36
  a = Account.new(account_key, purpose, index, name)
37
37
  a.receive_depth = receive_depth
38
38
  a.change_depth = change_depth
@@ -42,8 +42,8 @@ module Tapyrus
42
42
 
43
43
  def to_payload
44
44
  payload = account_key.to_payload
45
- payload << Tapyrus.pack_var_string(name.unpack('H*').first.htb)
46
- payload << [purpose, index, receive_depth, change_depth, lookahead].pack('I*')
45
+ payload << Tapyrus.pack_var_string(name.unpack("H*").first.htb)
46
+ payload << [purpose, index, receive_depth, change_depth, lookahead].pack("I*")
47
47
  payload
48
48
  end
49
49
 
@@ -98,13 +98,13 @@ module Tapyrus
98
98
  def type
99
99
  case purpose
100
100
  when PURPOSE_TYPE[:legacy]
101
- 'pubkeyhash'
101
+ "pubkeyhash"
102
102
  when PURPOSE_TYPE[:nested_witness]
103
- 'p2wpkh-p2sh'
103
+ "p2wpkh-p2sh"
104
104
  when PURPOSE_TYPE[:native_segwit]
105
- 'p2wpkh'
105
+ "p2wpkh"
106
106
  else
107
- 'unknown'
107
+ "unknown"
108
108
  end
109
109
  end
110
110
 
@@ -146,10 +146,10 @@ module Tapyrus
146
146
  end
147
147
 
148
148
  def validate_params!(account_key, purpose, index)
149
- raise 'account_key must be an instance of Tapyrus::ExtPubkey.' unless account_key.is_a?(Tapyrus::ExtPubkey)
150
- raise 'Account key and index does not match.' unless account_key.number == (index + Tapyrus::HARDENED_THRESHOLD)
149
+ raise "account_key must be an instance of Tapyrus::ExtPubkey." unless account_key.is_a?(Tapyrus::ExtPubkey)
150
+ raise "Account key and index does not match." unless account_key.number == (index + Tapyrus::HARDENED_THRESHOLD)
151
151
  version_bytes = Tapyrus::ExtPubkey.version_from_purpose(purpose + Tapyrus::HARDENED_THRESHOLD)
152
- raise 'The purpose and the account key do not match.' unless account_key.version == version_bytes
152
+ raise "The purpose and the account key do not match." unless account_key.version == version_bytes
153
153
  end
154
154
  end
155
155
  end