ciri 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +14 -0
  3. data/.rspec +2 -1
  4. data/.travis.yml +11 -4
  5. data/Gemfile.lock +3 -0
  6. data/README.md +44 -34
  7. data/Rakefile +47 -4
  8. data/ciri.gemspec +13 -12
  9. data/docker/Base +34 -0
  10. data/lib/ciri/actor.rb +223 -0
  11. data/lib/ciri/chain.rb +293 -0
  12. data/lib/ciri/chain/block.rb +47 -0
  13. data/lib/ciri/chain/header.rb +62 -0
  14. data/lib/ciri/chain/transaction.rb +145 -0
  15. data/lib/ciri/crypto.rb +58 -5
  16. data/lib/ciri/db/backend/memory.rb +68 -0
  17. data/lib/ciri/db/backend/rocks.rb +104 -0
  18. data/lib/ciri/db/backend/rocks_db.rb +278 -0
  19. data/lib/ciri/devp2p/peer.rb +10 -2
  20. data/lib/ciri/devp2p/protocol.rb +11 -3
  21. data/lib/ciri/devp2p/protocol_io.rb +6 -3
  22. data/lib/ciri/devp2p/rlpx.rb +1 -0
  23. data/lib/ciri/devp2p/rlpx/encryption_handshake.rb +1 -1
  24. data/lib/ciri/devp2p/rlpx/frame_io.rb +1 -1
  25. data/lib/ciri/devp2p/rlpx/message.rb +4 -4
  26. data/lib/ciri/devp2p/server.rb +14 -13
  27. data/lib/ciri/eth.rb +33 -0
  28. data/lib/ciri/eth/peer.rb +64 -0
  29. data/lib/ciri/eth/protocol_manage.rb +122 -0
  30. data/lib/ciri/eth/protocol_messages.rb +158 -0
  31. data/lib/ciri/eth/synchronizer.rb +188 -0
  32. data/lib/ciri/ethash.rb +123 -0
  33. data/lib/ciri/evm.rb +140 -0
  34. data/lib/ciri/evm/account.rb +50 -0
  35. data/lib/ciri/evm/block_info.rb +31 -0
  36. data/lib/ciri/evm/forks/frontier.rb +183 -0
  37. data/lib/ciri/evm/instruction.rb +92 -0
  38. data/lib/ciri/evm/machine_state.rb +81 -0
  39. data/lib/ciri/evm/op.rb +536 -0
  40. data/lib/ciri/evm/serialize.rb +60 -0
  41. data/lib/ciri/evm/sub_state.rb +64 -0
  42. data/lib/ciri/evm/vm.rb +379 -0
  43. data/lib/ciri/forks.rb +38 -0
  44. data/lib/ciri/forks/frontier.rb +43 -0
  45. data/lib/ciri/key.rb +7 -1
  46. data/lib/ciri/pow.rb +95 -0
  47. data/lib/ciri/rlp.rb +3 -53
  48. data/lib/ciri/rlp/decode.rb +100 -40
  49. data/lib/ciri/rlp/encode.rb +95 -34
  50. data/lib/ciri/rlp/serializable.rb +61 -91
  51. data/lib/ciri/types/address.rb +70 -0
  52. data/lib/ciri/types/errors.rb +36 -0
  53. data/lib/ciri/utils.rb +45 -13
  54. data/lib/ciri/utils/lib_c.rb +46 -0
  55. data/lib/ciri/utils/logger.rb +99 -0
  56. data/lib/ciri/utils/number.rb +67 -0
  57. data/lib/ciri/version.rb +1 -1
  58. metadata +67 -7
  59. data/lib/ciri/devp2p/actor.rb +0 -224
@@ -29,7 +29,11 @@ module Ciri
29
29
  # represent bool types: true | false
30
30
  class Bool
31
31
  ENCODED_TRUE = Ciri::Utils.big_endian_encode(0x01)
32
- ENCODED_FALSE = Ciri::Utils.big_endian_encode(0x00)
32
+ ENCODED_FALSE = Ciri::Utils.big_endian_encode(0x80)
33
+ end
34
+
35
+ # represent RLP raw types, binary or array
36
+ class Raw
33
37
  end
34
38
 
35
39
  # Serializable module allow ruby objects serialize/deserialize to or from RLP encoding.
@@ -71,26 +75,38 @@ module Ciri
71
75
  #
72
76
  module Serializable
73
77
  # nil represent RLP raw value(string or array of string)
74
- TYPES = [nil, Integer, Bool].map {|key| [key, true]}.to_h.freeze
78
+ TYPES = [Raw, Integer, Bool].map {|key| [key, true]}.to_h.freeze
75
79
 
76
80
  # Schema specific columns types of classes, normally you should not use Serializable::Schema directly
77
81
  #
78
82
  class Schema
83
+ include Encode
84
+ include Decode
85
+
79
86
  class InvalidSchemaError < StandardError
80
87
  end
81
88
 
82
89
  # keys return data columns array
83
90
  attr_reader :keys
84
91
 
92
+ KeySchema = Struct.new(:type, :options, keyword_init: true)
93
+
85
94
  def initialize(schema)
86
95
  keys = []
87
96
  @_schema = {}
88
97
 
89
98
  schema.each do |key|
90
- key, type = key.is_a?(Hash) ? key.to_a[0] : [key, nil]
99
+ if key.is_a?(Hash)
100
+ options = [:optional].map {|o| [o, key.delete(o)]}.to_h
101
+ raise InvalidSchemaError.new("include unknown options #{key}") unless key.size == 1
102
+ key, type = key.to_a[0]
103
+ else
104
+ options = {}
105
+ type = Raw
106
+ end
91
107
  raise InvalidSchemaError.new("missing type #{type} for key #{key}") unless check_key_type(type)
92
108
  keys << key
93
- @_schema[key] = type
109
+ @_schema[key] = KeySchema.new(type: type, options: options)
94
110
  end
95
111
 
96
112
  @_schema.freeze
@@ -109,25 +125,38 @@ module Ciri
109
125
  end
110
126
  end
111
127
 
112
- def rlp_encode!(data, raw: true)
128
+ def rlp_encode!(data, skip_keys: nil, white_list_keys: nil)
113
129
  # pre-encode, encode data to rlp compatible format(only string or array)
114
- data_list = keys.map do |key|
115
- Serializable.encode_with_type(data[key], self[key])
130
+ used_keys = if white_list_keys
131
+ white_list_keys
132
+ elsif skip_keys
133
+ keys - skip_keys
134
+ else
135
+ keys
136
+ end
137
+ data_list = []
138
+ used_keys.each do |key|
139
+ value = data[key]
140
+ next if value.nil? && self[key].options[:optional]
141
+ data_list << encode_with_type(value, self[key].type)
116
142
  end
117
- raw ? RLP.encode(data_list) : data_list
143
+ encode_list(data_list)
118
144
  end
119
145
 
120
- def rlp_decode!(input, raw: true)
121
- data = raw ? RLP.decode(input) : input
122
- keys.each_with_index.map do |key, i|
123
- # decode data by type
124
- decoded_item = Serializable.decode_with_type(data[i], self[key])
125
- [key, decoded_item]
126
- end.to_h
146
+ def rlp_decode!(input)
147
+ values = decode_list(input) do |list, stream|
148
+ keys.each do |key|
149
+ # decode data by type
150
+ next if stream.eof? && self[key].options[:optional]
151
+ list << decode_with_type(stream, self[key].type)
152
+ end
153
+ end
154
+ # convert to key value hash
155
+ keys.zip(values).to_h
127
156
  end
128
157
 
129
-
130
158
  private
159
+
131
160
  def check_key_type(type)
132
161
  return true if TYPES.key?(type)
133
162
  return true if type.is_a?(Class) && type < Serializable
@@ -142,8 +171,8 @@ module Ciri
142
171
 
143
172
  module ClassMethods
144
173
  # Decode object from input
145
- def rlp_decode(input, raw: true)
146
- data = schema.rlp_decode!(input, raw: raw)
174
+ def rlp_decode(input)
175
+ data = schema.rlp_decode!(input)
147
176
  self.new(data)
148
177
  end
149
178
 
@@ -161,15 +190,16 @@ module Ciri
161
190
  end
162
191
 
163
192
  private
193
+
164
194
  def define_attributes(schema)
165
195
  schema.keys.each do |attribute|
166
196
  module_eval <<-ATTR_METHODS
167
197
  def #{attribute}
168
- data[:"#{attribute}"]
198
+ serializable_attributes[:"#{attribute}"]
169
199
  end
170
200
 
171
201
  def #{attribute}=(value)
172
- data[:"#{attribute}"] = value
202
+ serializable_attributes[:"#{attribute}"] = value
173
203
  end
174
204
  ATTR_METHODS
175
205
  end
@@ -180,87 +210,27 @@ module Ciri
180
210
  def included(base)
181
211
  base.send :extend, ClassMethods
182
212
  end
183
-
184
- # use this method before RLP.encode
185
- # encode item to string or array
186
- def encode_with_type(item, type, zero: '')
187
- if type == Integer
188
- if item == 0
189
- "\x80".b
190
- elsif item < 128
191
- Ciri::Utils.big_endian_encode(item, zero)
192
- else
193
- buf = Ciri::Utils.big_endian_encode(item, zero)
194
- [0x80 + buf.size].pack("c*") + buf
195
- end
196
- elsif type == Bool
197
- item ? Bool::ENCODED_TRUE : Bool::ENCODED_FALSE
198
- elsif type.is_a?(Class) && type < Serializable
199
- item.rlp_encode!(raw: false)
200
- elsif type.is_a?(Array)
201
- if type.size == 1 # array type
202
- item.map {|i| encode_with_type(i, type[0])}
203
- else # unknown
204
- raise InvalidValueError.new "type size should be 1, got #{type}"
205
- end
206
- else
207
- raise InvalidValueError.new "unknown type #{type}" unless TYPES.key?(type)
208
- item
209
- end
210
- end
211
-
212
- # Use this method after RLP.decode, decode values from string or array to specific types
213
- # see Ciri::RLP::Serializable::TYPES for supported types
214
- #
215
- # Examples:
216
- #
217
- # item = Ciri::RLP.decode(encoded_text)
218
- # decode_with_type(item, Integer)
219
- #
220
- def decode_with_type(item, type)
221
- if type == Integer
222
- if item == "\x80".b || item.empty?
223
- 0
224
- elsif item[0].ord < 0x80
225
- Ciri::Utils.big_endian_decode(item)
226
- else
227
- size = item[0].ord - 0x80
228
- Ciri::Utils.big_endian_decode(item[1..size])
229
- end
230
- elsif type == Bool
231
- if item == Bool::ENCODED_TRUE
232
- true
233
- elsif item == Bool::ENCODED_FALSE
234
- false
235
- else
236
- raise InvalidValueError.new "invalid bool value #{item}"
237
- end
238
- elsif type.is_a?(Class) && type < Serializable
239
- # already decoded from RLP encoding
240
- type.rlp_decode!(item, raw: false)
241
- elsif type.is_a?(Array)
242
- item.map {|i| decode_with_type(i, type[0])}
243
- else
244
- raise InvalidValueError.new "unknown type #{type}" unless TYPES.key?(type)
245
- item
246
- end
247
- end
248
213
  end
249
214
 
250
- attr_reader :data
215
+ attr_reader :serializable_attributes
251
216
 
252
217
  def initialize(**data)
253
- @data = (self.class.default_data || {}).merge(data)
254
- self.class.schema.validate!(@data)
218
+ @serializable_attributes = (self.class.default_data || {}).merge(data)
219
+ self.class.schema.validate!(@serializable_attributes)
220
+ end
221
+
222
+ def initialize_copy(orig)
223
+ super
224
+ @serializable_attributes = orig.serializable_attributes.dup
255
225
  end
256
226
 
257
227
  # Encode object to rlp encoding string
258
- def rlp_encode!(raw: true)
259
- self.class.schema.rlp_encode!(data, raw: raw)
228
+ def rlp_encode!(skip_keys: nil, white_list_keys: nil)
229
+ self.class.schema.rlp_encode!(serializable_attributes, skip_keys: skip_keys, white_list_keys: white_list_keys)
260
230
  end
261
231
 
262
232
  def ==(other)
263
- self.class == other.class && data == other.data
233
+ self.class == other.class && serializable_attributes == other.serializable_attributes
264
234
  end
265
235
 
266
236
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require_relative 'errors'
25
+ require 'ciri/rlp'
26
+
27
+ module Ciri
28
+ module Types
29
+ class Address
30
+
31
+ include RLP::Serializable
32
+ include Errors
33
+
34
+ def initialize(address)
35
+ @address = address.to_s
36
+ end
37
+
38
+ def rlp_encode!
39
+ RLP.encode(@address)
40
+ end
41
+
42
+ def self.rlp_decode!(data)
43
+ address = self.new(RLP.decode(data))
44
+ address.validate
45
+ address
46
+ end
47
+
48
+ def to_s
49
+ @address
50
+ end
51
+
52
+ alias to_str to_s
53
+
54
+ def to_hex
55
+ Utils.data_to_hex to_s
56
+ end
57
+
58
+ def empty?
59
+ @address.empty?
60
+ end
61
+
62
+ def validate
63
+ # empty address is valid
64
+ return if empty?
65
+ raise InvalidError.new("address must be 20 size, got #{@address.size}") unless @address.size == 20
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ module Ciri
25
+ module Types
26
+ module Errors
27
+
28
+ class Error < StandardError
29
+ end
30
+
31
+ class InvalidError < Error
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -22,11 +22,15 @@
22
22
 
23
23
 
24
24
  require 'digest/sha3'
25
+ require_relative 'utils/number'
25
26
 
26
27
  module Ciri
27
28
  module Utils
28
29
 
29
30
  class << self
31
+ include Utils::Number
32
+
33
+
30
34
  def sha3(*data)
31
35
  s = Digest::SHA3.new(256)
32
36
  data.each {|i| s.update(i)}
@@ -37,24 +41,24 @@ module Ciri
37
41
  s1.size == s2.size && s1.each_byte.each_with_index.map {|b, i| b ^ s2[i].ord}.reduce(0, :+) == 0
38
42
  end
39
43
 
40
- def big_endian_encode(n, zero = '')
41
- if n == 0
42
- zero
43
- else
44
- big_endian_encode(n / 256) + (n % 256).chr
45
- end
44
+ def hex_to_data(hex)
45
+ data = [hex].pack("H*")
46
+ data = data[1..-1] if data.size > 0 && data[0].ord == 1
47
+ data
46
48
  end
47
49
 
48
- def big_endian_decode(input)
49
- input.each_byte.reduce(0) {|s, i| s * 256 + i}
50
+ def hex_to_number(hex)
51
+ big_endian_decode hex_to_data(hex)
50
52
  end
51
53
 
52
- def hex_to_data(hex)
53
- [hex].pack("H*")
54
+ def data_to_hex(data)
55
+ hex = data.to_s.unpack("H*").first
56
+ hex[0..1] = '0x' if hex.start_with?('01')
57
+ hex
54
58
  end
55
59
 
56
- def data_to_hex(data)
57
- data.unpack("H*").first
60
+ def number_to_hex(number)
61
+ data_to_hex big_endian_encode(number)
58
62
  end
59
63
 
60
64
  def create_ec_pk(raw_pubkey: nil, raw_privkey: nil)
@@ -69,7 +73,35 @@ module Ciri
69
73
  key.private_key = OpenSSL::BN.new(raw_privkey, 2) if raw_privkey
70
74
  end
71
75
  end
76
+
77
+ def to_underscore(str)
78
+ str.gsub(/[A-Z]/) {|a| "_" + a.downcase}
79
+ end
80
+
81
+ def blank_binary?(item)
82
+ return true if item.is_a?(String) && item.each_byte.all?(&:zero?)
83
+ blank?(item)
84
+ end
85
+
86
+ def blank?(item)
87
+ if item.nil?
88
+ true
89
+ elsif item.is_a? Integer
90
+ item.zero?
91
+ elsif item.is_a? String
92
+ item.empty?
93
+ else
94
+ false
95
+ end
96
+ end
97
+
98
+ def present?(item)
99
+ !blank?(item)
100
+ end
101
+
72
102
  end
73
103
 
104
+ BLANK_SHA3 = Utils.sha3(''.b).freeze
105
+
74
106
  end
75
- end
107
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require 'ffi'
25
+
26
+ module Ciri
27
+ module Utils
28
+
29
+ module LibC
30
+ extend FFI::Library
31
+ ffi_lib FFI::Library::LIBC
32
+
33
+ # memory allocators
34
+ attach_function :malloc, [:size_t], :pointer
35
+ attach_function :calloc, [:size_t], :pointer
36
+ attach_function :valloc, [:size_t], :pointer
37
+ attach_function :realloc, [:pointer, :size_t], :pointer
38
+ attach_function :free, [:pointer], :void
39
+
40
+ # memory movers
41
+ attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
42
+ attach_function :bcopy, [:pointer, :pointer, :size_t], :void
43
+ end
44
+
45
+ end
46
+ end