Module: Ciri::RLP::Serializable

Overview

Serializable module allow ruby objects serialize/deserialize to or from RLP encoding. See Ciri::RLP::Serializable::TYPES for supported type.

schema method define ordered data structure for class, and determine how to encoding objects.

schema follow `type` format, if attr is raw type(string or array of string), you can just use `:attr_name` to define it schema simple types include Integer, Bool, String, Array…

schema also support complex types: array and serializable.

array types represented as `[type]`, for example: `[Integer]` means value of bill attr is an array of integer serializable type represent value of attr is a RLP serializable object

Examples:

class AuthMsgV4
  include Ciri::RLP::Serializable

  # define schema
  schema [
           :signature, # raw type: string
           {initiator_pubkey: MySerializableKey}, # this attr is a RLP serializable object
           {nonce: [Integer]},
           {version: Integer}
         ]

  # default values
  default_data(got_plain: false)
end

msg = AuthMsgV4.new(signature: "\x00", initiator_pubkey: my_pubkey, nonce: [1, 2, 3], version: 4)
encoded = msg.rlp_encode!
msg2 = AuthMsgV4.rlp_decode!(encoded)
msg == msg2 # true

Defined Under Namespace

Modules: ClassMethods Classes: Schema

Constant Summary collapse

TYPES =

nil represent RLP raw value(string or array of string)

[nil, Integer, Bool].map {|key| [key, true]}.to_h.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data


250
251
252
# File 'lib/ciri/rlp/serializable.rb', line 250

def data
  @data
end

Class Method Details

.decode_with_type(item, type) ⇒ Object

Use this method after RLP.decode, decode values from string or array to specific types see Ciri::RLP::Serializable::TYPES for supported types

Examples:

item = Ciri::RLP.decode(encoded_text)
decode_with_type(item, Integer)

220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/ciri/rlp/serializable.rb', line 220

def decode_with_type(item, type)
  if type == Integer
    if item == "\x80".b || item.empty?
      0
    elsif item[0].ord < 0x80
      Ciri::Utils.big_endian_decode(item)
    else
      size = item[0].ord - 0x80
      Ciri::Utils.big_endian_decode(item[1..size])
    end
  elsif type == Bool
    if item == Bool::ENCODED_TRUE
      true
    elsif item == Bool::ENCODED_FALSE
      false
    else
      raise InvalidValueError.new "invalid bool value #{item}"
    end
  elsif type.is_a?(Class) && type < Serializable    # already decoded from RLP encoding

    type.rlp_decode!(item, raw: false)
  elsif type.is_a?(Array)
    item.map {|i| decode_with_type(i, type[0])}
  else
    raise InvalidValueError.new "unknown type #{type}" unless TYPES.key?(type)
    item
  end
end

.encode_with_type(item, type, zero: '') ⇒ Object

use this method before RLP.encode encode item to string or array


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/ciri/rlp/serializable.rb', line 186

def encode_with_type(item, type, zero: '')
  if type == Integer
    if item == 0
      "\x80".b
    elsif item < 128
      Ciri::Utils.big_endian_encode(item, zero)
    else
      buf = Ciri::Utils.big_endian_encode(item, zero)
      [0x80 + buf.size].pack("c*") + buf
    end
  elsif type == Bool
    item ? Bool::ENCODED_TRUE : Bool::ENCODED_FALSE
  elsif type.is_a?(Class) && type < Serializable
    item.rlp_encode!(raw: false)
  elsif type.is_a?(Array)
    if type.size == 1 # array type
      item.map {|i| encode_with_type(i, type[0])}
    else # unknown
      raise InvalidValueError.new "type size should be 1, got #{type}"
    end
  else
    raise InvalidValueError.new "unknown type #{type}" unless TYPES.key?(type)
    item
  end
end

.included(base) ⇒ Object


180
181
182
# File 'lib/ciri/rlp/serializable.rb', line 180

def included(base)
  base.send :extend, ClassMethods
end

Instance Method Details

#==(other) ⇒ Object


262
263
264
# File 'lib/ciri/rlp/serializable.rb', line 262

def ==(other)
  self.class == other.class && data == other.data
end

#initialize(**data) ⇒ Object


252
253
254
255
# File 'lib/ciri/rlp/serializable.rb', line 252

def initialize(**data)
  @data = (self.class.default_data || {}).merge(data)
  self.class.schema.validate!(@data)
end

#rlp_encode!(raw: true) ⇒ Object

Encode object to rlp encoding string


258
259
260
# File 'lib/ciri/rlp/serializable.rb', line 258

def rlp_encode!(raw: true)
  self.class.schema.rlp_encode!(data, raw: raw)
end