class Crea::Marshal

Constants

PUBLIC_KEY_DISABLED

Attributes

bytes[R]
cursor[R]

Public Class Methods

new(options = {}) click to toggle source
# File lib/crea/marshal.rb, line 13
def initialize(options = {})
  @bytes = if !!(hex = options[:hex])
    unhexlify hex
  else
    options[:bytes]
  end
  
  @chain = options[:chain] || :crea
  @prefix ||= case @chain
  when :crea then NETWORKS_CREA_ADDRESS_PREFIX
  when :test then NETWORKS_TEST_ADDRESS_PREFIX
  else; raise UnsupportedChainError, "Unsupported chain: #{@chain}"
  end
  @cursor = 0
end

Public Instance Methods

amount() click to toggle source
# File lib/crea/marshal.rb, line 101
def amount
  amount = uint64.to_f
  precision = signed_char
  asset = scan(7).strip
  
  amount = "%.#{precision}f #{asset}" % (amount / 10 ** precision)

  Crea::Type::Amount.new(amount)
end
authority(options = {optional: false}) click to toggle source
# File lib/crea/marshal.rb, line 115
def authority(options = {optional: false})
  return if !!options[:optional] && unsigned_char == 0
  
  {
    weight_threshold: uint32,
    account_auths: varint.times.map { [string, uint16] },
    key_auths: varint.times.map { [public_key, uint16] }
  }
end
beneficiaries() click to toggle source
# File lib/crea/marshal.rb, line 137
def beneficiaries
  if scan(1) == "\x00"
    varint.times.map {{account: string, weight: uint16}}
  end
end
boolean() click to toggle source
# File lib/crea/marshal.rb, line 59
def boolean; scan(1) == "\x01"; end
chain_properties() click to toggle source
# File lib/crea/marshal.rb, line 143
def chain_properties
  {
    account_creation_fee: amount,
    maximum_block_size: uint32,
    cbd_interest_rate: uint16
  }
end
comment_options_extensions() click to toggle source
# File lib/crea/marshal.rb, line 129
def comment_options_extensions
  if scan(1) == "\x01"
    beneficiaries
  else
    []
  end
end
empty_array() click to toggle source
# File lib/crea/marshal.rb, line 179
def empty_array
  unsigned_char == 0 and [] or raise "Found non-empty array."
end
hex() click to toggle source
# File lib/crea/marshal.rb, line 29
def hex
  hexlify bytes
end
int16() click to toggle source
# File lib/crea/marshal.rb, line 55
def int16; BinData::Int16le.read(scan(2)); end
int32() click to toggle source
# File lib/crea/marshal.rb, line 56
def int32; BinData::Int32le.read(scan(4)); end
int64() click to toggle source
# File lib/crea/marshal.rb, line 57
def int64; BinData::Int64le.read(scan(8)); end
operation_type() click to toggle source
# File lib/crea/marshal.rb, line 45
def operation_type
  Operation::IDS[unsigned_char]
end
operations() click to toggle source
# File lib/crea/marshal.rb, line 197
def operations
  operations_len = signed_char
  operations = []
  
  while operations.size < operations_len do
    begin
      type = operation_type
      break if type.nil?
      
      op_class_name = type.to_s.sub!(/_operation$/, '')
      op_class_name = "Crea::Operation::" + op_class_name.split('_').map(&:capitalize).join
      op_class = Object::const_get(op_class_name)
      op = op_class.new
      
      op_class::serializable_types.each do |k, v|
        begin
          # binding.pry if v == :comment_options_extensions
          op.send("#{k}=", send(v))
        rescue => e
          raise DeserializationError.new("#{type}.#{k} (#{v}) failed", e)
        end
      end
      
      operations << {type: type, value: op}
    rescue => e
      raise DeserializationError.new("#{type} failed", e)
    end
  end
  
  operations
rescue => e
  raise DeserializationError.new("Operations failed", e)
end
optional_authority() click to toggle source
# File lib/crea/marshal.rb, line 125
def optional_authority
  authority(optional: true)
end
point_in_time() click to toggle source
# File lib/crea/marshal.rb, line 85
def point_in_time
  if (time = uint32) == 2**32-1
    Time.at -1
  else
    Time.at time
  end.utc
end
price() click to toggle source
# File lib/crea/marshal.rb, line 111
def price
  {base: amount, quote: amount}
end
public_key(prefix = @prefix) click to toggle source
# File lib/crea/marshal.rb, line 93
def public_key(prefix = @prefix)
  raw_public_key = raw_bytes(33)
  checksum = OpenSSL::Digest::RIPEMD160.digest(raw_public_key)
  key = Base58.binary_to_base58(raw_public_key + checksum.slice(0, 4), :bitcoin)
  
  prefix + key unless key == PUBLIC_KEY_DISABLED
end
raw_bytes(len = nil) click to toggle source
# File lib/crea/marshal.rb, line 83
def raw_bytes(len = nil); scan(len || varint).force_encoding('BINARY'); end
required_auths() click to toggle source
# File lib/crea/marshal.rb, line 151
def required_auths
  varint.times.map { string }
end
rewind!() click to toggle source
# File lib/crea/marshal.rb, line 33
def rewind!
  @cursor = 0
end
scan(len) click to toggle source
# File lib/crea/marshal.rb, line 41
def scan(len)
  bytes.slice(@cursor..(@cursor - 1) + len).tap { |_| @cursor += len }
end
signed_char() click to toggle source
# File lib/crea/marshal.rb, line 54
def signed_char; BinData::Int8le.read(scan(1)); end
step(n = 0) click to toggle source
# File lib/crea/marshal.rb, line 37
def step(n = 0)
  @cursor += n
end
string(len = nil) click to toggle source
# File lib/crea/marshal.rb, line 81
def string(len = nil); scan(len || varint); end
transaction(options = {}) click to toggle source
# File lib/crea/marshal.rb, line 183
def transaction(options = {})
  trx = options[:trx] || Transaction.new
  
  trx.ref_block_num = uint16
  trx.ref_block_prefix = uint32
  trx.expiration = point_in_time
  
  trx.operations = operations
  
  trx
rescue => e
  raise DeserializationError.new("Transaction failed\nOriginal serialized bytes:\n[#{hex[0..(@cursor * 2) - 1]}]#{hex[((@cursor) * 2)..-1]}", e)
end
uint16() click to toggle source
# File lib/crea/marshal.rb, line 50
def uint16; BinData::Uint16le.read(scan(2)); end
uint32() click to toggle source
# File lib/crea/marshal.rb, line 51
def uint32; BinData::Uint32le.read(scan(4)); end
uint64() click to toggle source
# File lib/crea/marshal.rb, line 52
def uint64; BinData::Uint64le.read(scan(8)); end
unsigned_char() click to toggle source
# File lib/crea/marshal.rb, line 49
def unsigned_char; BinData::Uint8le.read(scan(1)); end
varint() click to toggle source
# File lib/crea/marshal.rb, line 61
def varint
  shift = 0
  result = 0
  bytes = []
  
  while (n = unsigned_char) >> 7 == 1
    bytes << n
  end
  
  bytes << n
  
  bytes.each do |b|
    result += ((b & 0x7f) << shift)
    break unless (b & 0x80)
    shift += 7
  end
  
  result
end
witness_properties() click to toggle source
# File lib/crea/marshal.rb, line 155
def witness_properties
  properties = {}
  
  varint.times do
    key = string.to_sym
    properties[key] = case key
    when :account_creation_fee then Crea::Type::Amount.new(string)
    when :account_subsidy_budget then scan(3)
    when :account_subsidy_decay, :maximum_block_size then uint32
    when :url then string
    when :cbd_exchange_rate
      JSON[string].tap do |rate|
        rate["base"] = Crea::Type::Amount.new(rate["base"])
        rate["quote"] = Crea::Type::Amount.new(rate["quote"])
      end
    when :cbd_interest_rate then uint16
    when :key, :new_signing_key then @prefix + scan(50)
    else; raise "Unknown witness property: #{key}"
    end
  end
  
  properties
end