ciri 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +8 -3
  3. data/README.md +5 -0
  4. data/Rakefile +24 -2
  5. data/ciri-rlp/.gitignore +11 -0
  6. data/ciri-rlp/.rspec +3 -0
  7. data/ciri-rlp/.travis.yml +5 -0
  8. data/ciri-rlp/CODE_OF_CONDUCT.md +74 -0
  9. data/ciri-rlp/Gemfile +6 -0
  10. data/ciri-rlp/Gemfile.lock +39 -0
  11. data/ciri-rlp/LICENSE.txt +21 -0
  12. data/ciri-rlp/README.md +98 -0
  13. data/ciri-rlp/Rakefile +6 -0
  14. data/ciri-rlp/bin/console +14 -0
  15. data/ciri-rlp/bin/setup +8 -0
  16. data/ciri-rlp/ciri-rlp.gemspec +28 -0
  17. data/{lib → ciri-rlp/lib}/ciri/rlp.rb +2 -2
  18. data/ciri-rlp/lib/ciri/rlp/decode.rb +144 -0
  19. data/ciri-rlp/lib/ciri/rlp/encode.rb +140 -0
  20. data/ciri-rlp/lib/ciri/rlp/serializable.rb +241 -0
  21. data/ciri-rlp/lib/ciri/rlp/version.rb +5 -0
  22. data/ciri-rlp/spec/ciri/fixture_spec.rb +95 -0
  23. data/ciri-rlp/spec/ciri/rlp/decode_spec.rb +68 -0
  24. data/ciri-rlp/spec/ciri/rlp/encode_spec.rb +72 -0
  25. data/ciri-rlp/spec/ciri/rlp/serializable_spec.rb +147 -0
  26. data/ciri-rlp/spec/ciri/rlp_spec.rb +5 -0
  27. data/ciri-rlp/spec/spec_helper.rb +14 -0
  28. data/ciri-utils/.gitignore +11 -0
  29. data/ciri-utils/.rspec +3 -0
  30. data/ciri-utils/.travis.yml +5 -0
  31. data/ciri-utils/CODE_OF_CONDUCT.md +74 -0
  32. data/ciri-utils/Gemfile +6 -0
  33. data/ciri-utils/Gemfile.lock +37 -0
  34. data/ciri-utils/LICENSE.txt +21 -0
  35. data/ciri-utils/README.md +61 -0
  36. data/ciri-utils/Rakefile +6 -0
  37. data/ciri-utils/bin/console +14 -0
  38. data/ciri-utils/bin/setup +8 -0
  39. data/ciri-utils/ciri-utils.gemspec +28 -0
  40. data/{lib → ciri-utils/lib}/ciri/utils.rb +9 -33
  41. data/{lib → ciri-utils/lib}/ciri/utils/logger.rb +0 -0
  42. data/{lib → ciri-utils/lib}/ciri/utils/number.rb +15 -12
  43. data/ciri-utils/lib/ciri/utils/version.rb +5 -0
  44. data/ciri-utils/spec/ciri/utils_spec.rb +5 -0
  45. data/ciri-utils/spec/spec_helper.rb +14 -0
  46. data/ciri.gemspec +6 -3
  47. data/lib/ciri/bloom_filter.rb +83 -0
  48. data/lib/ciri/chain.rb +67 -130
  49. data/lib/ciri/chain/header.rb +2 -2
  50. data/lib/ciri/chain/header_chain.rb +136 -0
  51. data/lib/ciri/chain/transaction.rb +1 -1
  52. data/lib/ciri/crypto.rb +2 -2
  53. data/lib/ciri/db/account_db.rb +145 -0
  54. data/lib/ciri/db/backend/memory.rb +24 -1
  55. data/lib/ciri/devp2p/peer.rb +1 -1
  56. data/lib/ciri/devp2p/rlpx/connection.rb +5 -5
  57. data/lib/ciri/eth/peer.rb +3 -3
  58. data/lib/ciri/eth/protocol_manage.rb +3 -3
  59. data/lib/ciri/eth/protocol_messages.rb +27 -26
  60. data/lib/ciri/ethash.rb +18 -3
  61. data/lib/ciri/evm.rb +101 -56
  62. data/lib/ciri/{utils/lib_c.rb → evm/errors.rb} +28 -18
  63. data/lib/ciri/evm/instruction.rb +3 -1
  64. data/lib/ciri/evm/{serialize.rb → log_entry.rb} +9 -27
  65. data/lib/ciri/evm/machine_state.rb +21 -2
  66. data/lib/ciri/evm/op.rb +19 -16
  67. data/lib/ciri/evm/state.rb +61 -0
  68. data/lib/ciri/evm/vm.rb +78 -102
  69. data/lib/ciri/forks.rb +1 -4
  70. data/lib/ciri/forks/base.rb +55 -0
  71. data/lib/ciri/forks/frontier.rb +37 -10
  72. data/lib/ciri/forks/frontier/cost.rb +186 -0
  73. data/lib/ciri/key.rb +1 -1
  74. data/lib/ciri/pow.rb +1 -1
  75. data/lib/ciri/rlp/decode.rb +6 -3
  76. data/lib/ciri/rlp/encode.rb +10 -10
  77. data/lib/ciri/rlp/serializable.rb +12 -9
  78. data/lib/ciri/serialize.rb +58 -0
  79. data/lib/ciri/trie.rb +362 -0
  80. data/lib/ciri/trie/nibbles.rb +97 -0
  81. data/lib/ciri/trie/nodes.rb +187 -0
  82. data/lib/ciri/{evm → types}/account.rb +20 -13
  83. data/lib/ciri/types/address.rb +16 -11
  84. data/lib/ciri/types/number.rb +73 -0
  85. data/lib/ciri/types/receipt.rb +57 -0
  86. data/lib/ciri/version.rb +1 -1
  87. metadata +82 -19
  88. data/lib/ciri/evm/forks/frontier.rb +0 -183
data/lib/ciri/forks.rb CHANGED
@@ -26,12 +26,9 @@ require_relative 'forks/frontier'
26
26
  module Ciri
27
27
  module Forks
28
28
 
29
- # Fork configure
30
- ForkConfig = Struct.new(:cost_of_operation, :cost_of_memory, :intrinsic_gas_of_transaction, keyword_init: true)
31
-
32
29
  def self.detect_fork(header: nil, number: nil)
33
30
  number ||= header.number
34
- Frontier.fork_config
31
+ Frontier::Config.new
35
32
  end
36
33
 
37
34
  end
@@ -0,0 +1,55 @@
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 Forks
26
+
27
+ class Base
28
+ # gas methods
29
+ def gas_of_operation(vm)
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def gas_of_memory(word_count)
34
+ raise NotImplementedError
35
+ end
36
+
37
+ def intrinsic_gas_of_transaction(transaction)
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def calculate_deposit_code_gas(code_bytes)
42
+ raise NotImplementedError
43
+ end
44
+
45
+ def mining_rewards_of_block(block)
46
+ raise NotImplementedError
47
+ end
48
+
49
+ def calculate_refund_gas(vm)
50
+ raise NotImplementedError
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -21,23 +21,50 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
- require 'ciri/evm/forks/frontier'
24
+ require_relative 'base'
25
+ require_relative 'frontier/cost'
25
26
 
26
27
  module Ciri
27
28
  module Forks
28
29
  module Frontier
30
+ class Config < Base
29
31
 
30
- class << self
31
- def fork_config
32
- ForkConfig.new(
33
- cost_of_operation: proc {|vm| EVM::Forks::Frontier::Cost.cost_of_operation vm},
34
- cost_of_memory: proc {|i| EVM::Forks::Frontier::Cost.cost_of_memory i},
35
- intrinsic_gas_of_transaction: proc {|t| EVM::Forks::Frontier::Cost.intrinsic_gas_of_transaction t}
36
- # transaction_klass: nil
37
- )
32
+ BLOCK_REWARD = 5 * 10.pow(18) # 5 ether
33
+
34
+ # gas methods
35
+ def gas_of_operation(vm)
36
+ Cost.cost_of_operation vm
38
37
  end
39
- end
40
38
 
39
+ def gas_of_memory(word_count)
40
+ Cost.cost_of_memory word_count
41
+ end
42
+
43
+ def intrinsic_gas_of_transaction(transaction)
44
+ Cost.intrinsic_gas_of_transaction transaction
45
+ end
46
+
47
+ def calculate_deposit_code_gas(code_bytes)
48
+ Cost::G_CODEDEPOSIT * (code_bytes || ''.b).size
49
+ end
50
+
51
+ def calculate_refund_gas(vm)
52
+ vm.sub_state.suicide_accounts.size * Cost::R_SELFDESTRUCT
53
+ end
54
+
55
+ def mining_rewards_of_block(block)
56
+ rewards = Hash.new(0)
57
+ # reward miner
58
+ rewards[block.header.beneficiary] += ((1 + block.ommers.count.to_f / 32) * BLOCK_REWARD).to_i
59
+
60
+ # reward ommer(uncle) block miners
61
+ block.ommers.each do |ommer|
62
+ rewards[ommer.beneficiary] += ((1 + (ommer.number - block.header.number).to_f / 8) * BLOCK_REWARD).to_i
63
+ end
64
+ rewards
65
+ end
66
+
67
+ end
41
68
  end
42
69
  end
43
70
  end
@@ -0,0 +1,186 @@
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 'ciri/evm/op'
25
+
26
+ module Ciri
27
+ module Forks
28
+ module Frontier
29
+
30
+ module Cost
31
+
32
+ include Ciri::EVM::OP
33
+
34
+ # fee schedule, start with G
35
+ G_ZERO = 0
36
+ G_BASE = 2
37
+ G_VERYLOW = 3
38
+ G_LOW = 5
39
+ G_MID = 8
40
+ G_HIGH = 10
41
+ G_EXTCODE = 700
42
+ G_BALANCE = 400
43
+ G_SLOAD = 50
44
+ G_JUMPDEST = 1
45
+ G_SSET = 20000
46
+ G_RESET = 5000
47
+ R_SCLEAR = 15000
48
+ R_SELFDESTRUCT = 24000
49
+ G_SELFDESTRUCT = 0
50
+ G_CREATE = 32000
51
+ G_CODEDEPOSIT = 200
52
+ G_CALL = 700
53
+ G_CALLVALUE = 9000
54
+ G_CALLSTIPEND = 2300
55
+ G_NEWACCOUNT = 25000
56
+ G_EXP = 10
57
+ G_EXPBYTE = 10
58
+ G_MEMORY = 3
59
+ G_TXCREATE = 32000
60
+ G_TXDATAZERO = 4
61
+ G_TXDATANONZERO = 68
62
+ G_TRANSACTION = 21000
63
+ G_LOG = 375
64
+ G_LOGDATA = 8
65
+ G_TOPIC = 375
66
+ G_SHA3 = 30
67
+ G_SHA3WORD = 6
68
+ G_COPY = 3
69
+ G_BLOCKHASH = 20
70
+ G_QUADDIVISOR = 100
71
+
72
+ # operation code by group, for later calculation
73
+ W_ZERO = [STOP, RETURN, REVERT]
74
+ W_BASE = [ADDRESS, ORIGIN, CALLER, CALLVALUE, CALLDATASIZE, CODESIZE, GASPRICE,
75
+ COINBASE, TIMESTAMP, NUMBER, DIFFICULTY, GASLIMIT, RETURNDATASIZE,
76
+ POP, PC, MSIZE, GAS]
77
+ W_VERYLOW = [ADD, SUB, NOT, LT, GT, SLT, SGT, EQ, ISZERO, AND, OR,
78
+ XOR, BYTE, CALLDATALOAD, MLOAD, MSTORE, MSTORE8,
79
+ *(1..32).map {|i| EVM::OP.get(PUSH1 + i - 1).code}, # push1 - push32
80
+ *(1..16).map {|i| EVM::OP.get(DUP1 + i - 1).code}, # dup1 - dup16
81
+ *(1..16).map {|i| EVM::OP.get(SWAP1 + i - 1).code}] # swap1 - swap16
82
+ W_LOW = [MUL, DIV, SDIV, MOD, SMOD, SIGNEXTEND]
83
+ W_MID = [ADDMOD, MULMOD, JUMP]
84
+ W_HIGH = [JUMPI]
85
+ W_EXTCODE = [EXTCODESIZE]
86
+
87
+
88
+ class << self
89
+ include Ciri::EVM::OP
90
+
91
+ # C(σ,μ,I)
92
+ # calculate cost of current operation
93
+ def cost_of_operation(vm)
94
+ ms = vm.machine_state
95
+ instruction = vm.instruction
96
+ w = instruction.get_op(ms.pc)
97
+ if w == SSTORE
98
+ cost_of_sstore(vm)
99
+ elsif w == EXP && ms.get_stack(1, Integer) == 0
100
+ G_EXP
101
+ elsif w == EXP && (x = ms.get_stack(1, Integer)) > 0
102
+ G_EXP + G_EXPBYTE * Utils.ceil_div(x.bit_length, 8)
103
+ elsif w == CALLDATACOPY || w == CODECOPY || w == RETURNDATACOPY
104
+ G_VERYLOW + G_COPY * Utils.ceil_div(ms.get_stack(2, Integer), 32)
105
+ elsif w == EXTCODECOPY
106
+ G_EXTCODE + G_COPY * Utils.ceil_div(ms.get_stack(3, Integer), 32)
107
+ elsif (LOG0..LOG4).include? w
108
+ G_LOG + G_LOGDATA * ms.get_stack(1, Integer) + (w - LOG0) * G_TOPIC
109
+ elsif w == CALL || w == CALLCODE || w == DELEGATECALL
110
+ 1 # cost_of_call(state, ms)
111
+ elsif w == SELFDESTRUCT
112
+ cost_of_self_destruct(vm)
113
+ elsif w == CREATE
114
+ G_CREATE
115
+ elsif w == SHA3
116
+ G_SHA3 + G_SHA3WORD * Utils.ceil_div(ms.get_stack(1, Integer), 32)
117
+ elsif w == JUMPDEST
118
+ G_JUMPDEST
119
+ elsif w == SLOAD
120
+ G_SLOAD
121
+ elsif W_ZERO.include? w
122
+ G_ZERO
123
+ elsif W_BASE.include? w
124
+ G_BASE
125
+ elsif W_VERYLOW.include? w
126
+ G_VERYLOW
127
+ elsif W_LOW.include? w
128
+ G_LOW
129
+ elsif W_MID.include? w
130
+ G_MID
131
+ elsif W_HIGH.include? w
132
+ G_HIGH
133
+ elsif W_EXTCODE.include? w
134
+ G_EXTCODE
135
+ elsif w == BALANCE
136
+ G_BALANCE
137
+ elsif w == BLOCKHASH
138
+ G_BLOCKHASH
139
+ else
140
+ raise "can't compute cost for unknown op #{w}"
141
+ end
142
+ end
143
+
144
+ def cost_of_memory(i)
145
+ G_MEMORY * i + (i ** 2) / 512
146
+ end
147
+
148
+ def intrinsic_gas_of_transaction(t)
149
+ gas = (t.data.each_byte || '').reduce(0) {|sum, i| sum + (i.zero? ? G_TXDATAZERO : G_TXDATANONZERO)}
150
+ # gas + (t.to.empty? ? G_TXCREATE : 0) + G_TRANSACTION
151
+ gas + G_TRANSACTION
152
+ end
153
+
154
+ private
155
+
156
+ def cost_of_self_destruct(vm)
157
+ G_SELFDESTRUCT
158
+ end
159
+
160
+ def cost_of_call
161
+
162
+ end
163
+
164
+ def cost_of_sstore(vm)
165
+ ms = vm.machine_state
166
+ instruction = vm.instruction
167
+
168
+ key = ms.get_stack(0, Integer)
169
+ value = ms.get_stack(1, Integer)
170
+
171
+ stored_is_empty = vm.fetch(instruction.address, key).zero?
172
+ value_is_non_zero = value && !value.zero?
173
+
174
+ if value_is_non_zero && stored_is_empty
175
+ G_SSET
176
+ else
177
+ G_RESET
178
+ end
179
+ end
180
+
181
+ end
182
+ end
183
+
184
+ end
185
+ end
186
+ end
data/lib/ciri/key.rb CHANGED
@@ -79,7 +79,7 @@ module Ciri
79
79
  privkey = ec_key.private_key.to_s(2)
80
80
  # some times below error will occurs, raise error with more detail
81
81
  unless privkey.instance_of?(String) && privkey.size == 32
82
- raise ArgumentError, "privkey must be composed of 32 bytes, #{bytes}: #{privkey.size} privkey: #{Utils.data_to_hex privkey}"
82
+ raise ArgumentError, "privkey must be composed of 32 bytes, #{bytes}: #{privkey.size} privkey: #{Utils.to_hex privkey}"
83
83
  end
84
84
  @secp256k1_key ||= Crypto.ensure_secp256k1_key(privkey: privkey)
85
85
  end
data/lib/ciri/pow.rb CHANGED
@@ -68,7 +68,7 @@ module Ciri
68
68
  out_mix_hash, out_result = Ethash.hashimoto_light(block_number, cache, mining_hash, Utils.big_endian_decode(nonce_bytes))
69
69
 
70
70
  if out_mix_hash != mix_hash
71
- raise InvalidError.new("mix hash mismatch; #{Utils.data_to_hex(out_mix_hash)} != #{Utils.data_to_hex(mix_hash)}")
71
+ raise InvalidError.new("mix hash mismatch; #{Utils.to_hex(out_mix_hash)} != #{Utils.to_hex(mix_hash)}")
72
72
  end
73
73
 
74
74
  result = Utils.big_endian_decode(out_result)
@@ -68,12 +68,15 @@ module Ciri
68
68
  else
69
69
  raise InvalidValueError.new "invalid bool value #{item}"
70
70
  end
71
- elsif type.is_a?(Class) && type < Serializable
72
- type.rlp_decode!(s)
71
+ elsif type.is_a?(Class) && type.respond_to?(:rlp_decode)
72
+ type.rlp_decode(s)
73
73
  elsif type.is_a?(Array)
74
74
  decode_list(s) do |list, s2|
75
+ i = 0
75
76
  until s2.eof?
76
- list << decode_with_type(s2, type[0])
77
+ t = type.size > i ? type[i] : type[-1]
78
+ list << decode_with_type(s2, t)
79
+ i += 1
77
80
  end
78
81
  end
79
82
  elsif type == Raw
@@ -28,25 +28,25 @@ module Ciri
28
28
  class InputOverflow < StandardError
29
29
  end
30
30
 
31
- # Encode input to rlp encoding, only allow string or array
31
+ # Encode input to rlp encoding
32
32
  #
33
33
  # Examples:
34
34
  #
35
35
  # Ciri::RLP.encode("hello world")
36
36
  #
37
- def encode(input, type = Raw)
38
- encode_with_type(input, type)
37
+ def encode(input, type = nil)
38
+ type ? encode_with_type(input, type) : encode_simple(input)
39
39
  end
40
40
 
41
41
  def encode_simple(input)
42
42
  if input.is_a?(Array)
43
43
  encode_list(input) {|i| encode_simple(i)}
44
44
  elsif input.is_a?(Integer)
45
- encode(input, Integer)
46
- elsif input.is_a?(Serializable)
47
- input.rlp_encode!
45
+ encode_with_type(input, Integer)
46
+ elsif input.class.respond_to?(:rlp_encode)
47
+ input.class.rlp_encode(input)
48
48
  else
49
- encode(input)
49
+ encode_with_type(input, Raw)
50
50
  end
51
51
  end
52
52
 
@@ -70,8 +70,8 @@ module Ciri
70
70
  end
71
71
  elsif type == Bool
72
72
  item ? Bool::ENCODED_TRUE : Bool::ENCODED_FALSE
73
- elsif type.is_a?(Class) && type < Serializable
74
- item.rlp_encode!
73
+ elsif type.is_a?(Class) && type.respond_to?(:rlp_encode)
74
+ type.rlp_encode(item)
75
75
  elsif type.is_a?(Array)
76
76
  if type.size == 1 # array type
77
77
  encode_list(item) {|i| encode_with_type(i, type[0])}
@@ -84,7 +84,7 @@ module Ciri
84
84
  raise RLP::InvalidValueError.new "unknown type #{type}"
85
85
  end
86
86
  rescue
87
- STDERR.puts "when encoding #{Utils.data_to_hex item.to_s} into #{type}"
87
+ STDERR.puts "when encoding #{Utils.to_hex item.to_s} into #{type}"
88
88
  raise
89
89
  end
90
90
 
@@ -69,8 +69,8 @@ module Ciri
69
69
  # end
70
70
  #
71
71
  # msg = AuthMsgV4.new(signature: "\x00", initiator_pubkey: my_pubkey, nonce: [1, 2, 3], version: 4)
72
- # encoded = msg.rlp_encode!
73
- # msg2 = AuthMsgV4.rlp_decode!(encoded)
72
+ # encoded = msg.rlp_encode
73
+ # msg2 = AuthMsgV4.rlp_decode(encoded)
74
74
  # msg == msg2 # true
75
75
  #
76
76
  module Serializable
@@ -125,7 +125,7 @@ module Ciri
125
125
  end
126
126
  end
127
127
 
128
- def rlp_encode!(data, skip_keys: nil, white_list_keys: nil)
128
+ def rlp_encode(data, skip_keys: nil, white_list_keys: nil)
129
129
  # pre-encode, encode data to rlp compatible format(only string or array)
130
130
  used_keys = if white_list_keys
131
131
  white_list_keys
@@ -143,7 +143,7 @@ module Ciri
143
143
  encode_list(data_list)
144
144
  end
145
145
 
146
- def rlp_decode!(input)
146
+ def rlp_decode(input)
147
147
  values = decode_list(input) do |list, stream|
148
148
  keys.each do |key|
149
149
  # decode data by type
@@ -159,7 +159,7 @@ module Ciri
159
159
 
160
160
  def check_key_type(type)
161
161
  return true if TYPES.key?(type)
162
- return true if type.is_a?(Class) && type < Serializable
162
+ return true if type.is_a?(Class) && type.respond_to?(:rlp_decode) && type.respond_to?(:rlp_encode)
163
163
 
164
164
  if type.is_a?(Array) && type.size == 1
165
165
  check_key_type(type[0])
@@ -172,11 +172,14 @@ module Ciri
172
172
  module ClassMethods
173
173
  # Decode object from input
174
174
  def rlp_decode(input)
175
- data = schema.rlp_decode!(input)
175
+ data = schema.rlp_decode(input)
176
176
  self.new(data)
177
177
  end
178
178
 
179
- alias rlp_decode! rlp_decode
179
+ # Encode object to rlp encoding string
180
+ def rlp_encode(item, skip_keys: nil, white_list_keys: nil)
181
+ schema.rlp_encode(item.serializable_attributes, skip_keys: skip_keys, white_list_keys: white_list_keys)
182
+ end
180
183
 
181
184
  def schema(data_schema = nil)
182
185
  @data_schema ||= Schema.new(data_schema).tap do |schema|
@@ -225,8 +228,8 @@ module Ciri
225
228
  end
226
229
 
227
230
  # Encode object to rlp encoding string
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)
231
+ def rlp_encode(skip_keys: nil, white_list_keys: nil)
232
+ self.class.rlp_encode(self, skip_keys: skip_keys, white_list_keys: white_list_keys)
230
233
  end
231
234
 
232
235
  def ==(other)