Class: Steem::Marshal

Inherits:
Object
  • Object
show all
Includes:
ChainConfig, Utils
Defined in:
lib/steem/marshal.rb

Constant Summary collapse

PUBLIC_KEY_DISABLED =
'1111111111111111111111111111111114T1Anm'

Constants included from ChainConfig

ChainConfig::EXPIRE_IN_SECS, ChainConfig::EXPIRE_IN_SECS_PROPOSAL, ChainConfig::NETWORKS_STEEM_ADDRESS_PREFIX, ChainConfig::NETWORKS_STEEM_CHAIN_ID, ChainConfig::NETWORKS_STEEM_CORE_ASSET, ChainConfig::NETWORKS_STEEM_DEBT_ASSET, ChainConfig::NETWORKS_STEEM_DEFAULT_NODE, ChainConfig::NETWORKS_STEEM_VEST_ASSET, ChainConfig::NETWORKS_TEST_ADDRESS_PREFIX, ChainConfig::NETWORKS_TEST_CHAIN_ID, ChainConfig::NETWORKS_TEST_CORE_ASSET, ChainConfig::NETWORKS_TEST_DEBT_ASSET, ChainConfig::NETWORKS_TEST_DEFAULT_NODE, ChainConfig::NETWORKS_TEST_VEST_ASSET, ChainConfig::NETWORK_CHAIN_IDS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#hexlify, #unhexlify

Constructor Details

#initialize(options = {}) ⇒ Marshal

Returns a new instance of Marshal.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/steem/marshal.rb', line 13

def initialize(options = {})
  @bytes = if !!(hex = options[:hex])
    unhexlify hex
  else
    options[:bytes]
  end
  
  @chain = options[:chain] || :steem
  @prefix ||= case @chain
  when :steem then NETWORKS_STEEM_ADDRESS_PREFIX
  when :test then NETWORKS_TEST_ADDRESS_PREFIX
  else; raise UnsupportedChainError, "Unsupported chain: #{@chain}"
  end
  @cursor = 0
end

Instance Attribute Details

#bytesObject (readonly)

Returns the value of attribute bytes.



11
12
13
# File 'lib/steem/marshal.rb', line 11

def bytes
  @bytes
end

#cursorObject (readonly)

Returns the value of attribute cursor.



11
12
13
# File 'lib/steem/marshal.rb', line 11

def cursor
  @cursor
end

Instance Method Details

#amountObject



101
102
103
104
105
106
107
108
109
# File 'lib/steem/marshal.rb', line 101

def amount
  amount = uint64.to_f
  precision = signed_char
  asset = scan(7).strip
  
  amount = "%.#{precision}f #{asset}" % (amount / 10 ** precision)

  Steem::Type::Amount.new(amount)
end

#authority(options = {optional: false}) ⇒ Object



115
116
117
118
119
120
121
122
123
# File 'lib/steem/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

#beneficiariesObject



137
138
139
140
141
# File 'lib/steem/marshal.rb', line 137

def beneficiaries
  if scan(1) == "\x00"
    varint.times.map {{account: string, weight: uint16}}
  end
end

#booleanObject



59
# File 'lib/steem/marshal.rb', line 59

def boolean; scan(1) == "\x01"; end

#chain_propertiesObject



143
144
145
146
147
148
149
# File 'lib/steem/marshal.rb', line 143

def chain_properties
  {
    account_creation_fee: amount,
    maximum_block_size: uint32,
    sbd_interest_rate: uint16
  }
end

#comment_options_extensionsObject



129
130
131
132
133
134
135
# File 'lib/steem/marshal.rb', line 129

def comment_options_extensions
  if scan(1) == "\x01"
    beneficiaries
  else
    []
  end
end

#empty_arrayObject



179
180
181
# File 'lib/steem/marshal.rb', line 179

def empty_array
  unsigned_char == 0 and [] or raise "Found non-empty array."
end

#hexObject



29
30
31
# File 'lib/steem/marshal.rb', line 29

def hex
  hexlify bytes
end

#int16Object

16-bit signed, little-endian



55
# File 'lib/steem/marshal.rb', line 55

def int16; BinData::Int16le.read(scan(2)); end

#int32Object

32-bit signed, little-endian



56
# File 'lib/steem/marshal.rb', line 56

def int32; BinData::Int32le.read(scan(4)); end

#int64Object

64-bit signed, little-endian



57
# File 'lib/steem/marshal.rb', line 57

def int64; BinData::Int64le.read(scan(8)); end

#operation_typeObject



45
46
47
# File 'lib/steem/marshal.rb', line 45

def operation_type
  Operation::IDS[unsigned_char]
end

#operationsObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/steem/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 = "Steem::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_authorityObject



125
126
127
# File 'lib/steem/marshal.rb', line 125

def optional_authority
  authority(optional: true)
end

#point_in_timeObject



85
86
87
88
89
90
91
# File 'lib/steem/marshal.rb', line 85

def point_in_time
  if (time = uint32) == 2**32-1
    Time.at -1
  else
    Time.at time
  end.utc
end

#priceObject



111
112
113
# File 'lib/steem/marshal.rb', line 111

def price
  {base: amount, quote: amount}
end

#public_key(prefix = @prefix) ⇒ Object



93
94
95
96
97
98
99
# File 'lib/steem/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) ⇒ Object



83
# File 'lib/steem/marshal.rb', line 83

def raw_bytes(len = nil); scan(len || varint).force_encoding('BINARY'); end

#required_authsObject



151
152
153
# File 'lib/steem/marshal.rb', line 151

def required_auths
  varint.times.map { string }
end

#rewind!Object



33
34
35
# File 'lib/steem/marshal.rb', line 33

def rewind!
  @cursor = 0
end

#scan(len) ⇒ Object



41
42
43
# File 'lib/steem/marshal.rb', line 41

def scan(len)
  bytes.slice(@cursor..(@cursor - 1) + len).tap { |_| @cursor += len }
end

#signed_charObject

8-bit signed



54
# File 'lib/steem/marshal.rb', line 54

def signed_char; BinData::Int8le.read(scan(1)); end

#step(n = 0) ⇒ Object



37
38
39
# File 'lib/steem/marshal.rb', line 37

def step(n = 0)
  @cursor += n
end

#string(len = nil) ⇒ Object



81
# File 'lib/steem/marshal.rb', line 81

def string(len = nil); scan(len || varint); end

#transaction(options = {}) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/steem/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

#uint16Object

16-bit unsigned, VAX (little-endian) byte order



50
# File 'lib/steem/marshal.rb', line 50

def uint16; BinData::Uint16le.read(scan(2)); end

#uint32Object

32-bit unsigned, VAX (little-endian) byte order



51
# File 'lib/steem/marshal.rb', line 51

def uint32; BinData::Uint32le.read(scan(4)); end

#uint64Object

64-bit unsigned, little-endian



52
# File 'lib/steem/marshal.rb', line 52

def uint64; BinData::Uint64le.read(scan(8)); end

#unsigned_charObject

8-bit unsigned



49
# File 'lib/steem/marshal.rb', line 49

def unsigned_char; BinData::Uint8le.read(scan(1)); end

#varintObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/steem/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_propertiesObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/steem/marshal.rb', line 155

def witness_properties
  properties = {}
  
  varint.times do
    key = string.to_sym
    properties[key] = case key
    when :account_creation_fee then Steem::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 :sbd_exchange_rate
      JSON[string].tap do |rate|
        rate["base"] = Steem::Type::Amount.new(rate["base"])
        rate["quote"] = Steem::Type::Amount.new(rate["quote"])
      end
    when :sbd_interest_rate then uint16
    when :key, :new_signing_key then @prefix + scan(50)
    else; raise "Unknown witness property: #{key}"
    end
  end
  
  properties
end