Class: ABI::Decoder
- Inherits:
-
Object
- Object
- ABI::Decoder
- Defined in:
- lib/abicoder/decoder.rb
Instance Method Summary collapse
-
#decode(types, data, raise_errors = false) ⇒ Object
Decodes multiple arguments using the head/tail mechanism.
- #decode_primitive_type(type, data) ⇒ Object
- #decode_type(type, data) ⇒ Object
-
#decode_uint256(bin) ⇒ Object
decoding helpers / utils.
Instance Method Details
#decode(types, data, raise_errors = false) ⇒ Object
Decodes multiple arguments using the head/tail mechanism.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/abicoder/decoder.rb', line 9 def decode( types, data, raise_errors = false ) ## ## todo/check: always change data (string) to binary encoding (e.g. data = data.b ) ## or such - why? why not? ## for convenience check if types is a String ## otherwise assume ABI::Type already types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) } outputs = [nil] * types.size start_positions = [nil] * types.size + [data.size] # TODO: refactor, a reverse iteration will be better - why? why not? # try to simplify / clean-up code - possible? why? why not? pos = 0 types.each_with_index do |t, i| # If a type is static, grab the data directly, otherwise record its # start position if t.dynamic? if pos>data.size-1 if raise_errors raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}" else puts "!! WARN - DecodingError: Position out of bounds #{pos}>#{data.size-1}" end end start_positions[i] = decode_uint256(data[pos, 32]) if start_positions[i]>data.size-1 if raise_errors raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}" else puts "!! WARN - DecodingError: Start position out of bounds #{start_positions[i]}>#{data.size-1}" end end j = i - 1 while j >= 0 && start_positions[j].nil? start_positions[j] = start_positions[i] j -= 1 end pos += 32 else ## puts "step 1 - decode item [#{i}] - #{t.format} size: #{t.size} dynamic? #{t.dynamic?}" count = t.size ## was zero_padding( data, pos, t.size, start_positions ) ## inline for now and clean-up later - why? why not? outputs[i] = if pos >= data.size start_positions[start_positions.size-1] += count BYTE_ZERO*count elsif pos + count > data.size start_positions[start_positions.size-1] += ( count - (data.size-pos)) data[pos,data.size-pos] + BYTE_ZERO*( count - (data.size-pos)) else data[pos, count] end pos += t.size end end # We add a start position equal to the length of the entire data for # convenience. j = types.size - 1 while j >= 0 && start_positions[j].nil? start_positions[j] = start_positions[types.size] j -= 1 end if pos > data.size if raise_errors raise DecodingError, "Not enough data for head" else puts "!! WARN - DecodingError: Not enough data for head" end end types.each_with_index do |t, i| if t.dynamic? offset, next_offset = start_positions[i, 2] if offset<=data.size && next_offset<=data.size outputs[i] = data[offset...next_offset] end end end if outputs.include?( nil ) if raise_errors raise DecodingError, "Not all data can be parsed" else puts "!! WARN: DecodingError - Not all data can be parsed" end end types.zip(outputs).map do |(t, out)| ## puts "step 2 - decode item - #{t.format} got: #{out.size} byte(s) - size: #{t.size} dynamic? #{t.dynamic?}" decode_type(t, out) end end |
#decode_primitive_type(type, data) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/abicoder/decoder.rb', line 164 def decode_primitive_type(type, data) case type when Uint decode_uint256( data ) when Int u = decode_uint256( data ) u >= 2**(type.bits-1) ? (u - 2**type.bits) : u when Bool data[-1] == BYTE_ONE when String ## note: convert to a string (with UTF_8 encoding NOT BINARY!!!) size = decode_uint256( data[0,32] ) data[32..-1][0,size].force_encoding( Encoding::UTF_8 ) when Bytes size = decode_uint256( data[0,32] ) data[32..-1][0,size] when FixedBytes data[0, type.length] when Address ## note: convert to a hex string (with UTF_8 encoding NOT BINARY!!!) data[12..-1].unpack("H*").first.force_encoding( Encoding::UTF_8 ) else raise DecodingError, "Unknown primitive type: #{type.class.name} #{type.format}" end end |
#decode_type(type, data) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/abicoder/decoder.rb', line 123 def decode_type( type, data ) return nil if data.nil? || data.empty? if type.is_a?( Tuple ) ## todo: support empty (unit) tuple - why? why not? decode( type.types, data ) elsif type.is_a?( FixedArray ) # static-sized arrays l = type.dim subtype = type.subtype if subtype.dynamic? start_positions = (0...l).map {|i| decode_uint256(data[32*i, 32]) } start_positions.push( data.size ) outputs = (0...l).map {|i| data[start_positions[i]...start_positions[i+1]] } outputs.map {|out| decode_type(subtype, out) } else (0...l).map {|i| decode_type(subtype, data[subtype.size*i, subtype.size]) } end elsif type.is_a?( Array ) l = decode_uint256( data[0,32] ) raise DecodingError, "Too long length: #{l}" if l > 100000 subtype = type.subtype if subtype.dynamic? raise DecodingError, "Not enough data for head" unless data.size >= 32 + 32*l start_positions = (1..l).map {|i| 32+decode_uint256(data[32*i, 32]) } start_positions.push( data.size ) outputs = (0...l).map {|i| data[start_positions[i]...start_positions[i+1]] } outputs.map {|out| decode_type(subtype, out) } else (0...l).map {|i| decode_type(subtype, data[32 + subtype.size*i, subtype.size]) } end else decode_primitive_type( type, data ) end end |
#decode_uint256(bin) ⇒ Object
decoding helpers / utils
195 196 197 198 199 200 201 |
# File 'lib/abicoder/decoder.rb', line 195 def decode_uint256( bin ) # bin = bin.sub( /\A(\x00)+/, '' ) ## keep "performance" shortcut - why? why not? ### todo/check - allow nil - why? why not? ## raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == BYTE_ZERO # bin = bin || BYTE_ZERO bin.unpack("H*").first.to_i(16) end |