eth 0.5.14 → 0.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/codeql.yml +4 -4
 - data/.github/workflows/docs.yml +1 -1
 - data/.github/workflows/spec.yml +31 -13
 - data/CHANGELOG.md +53 -0
 - data/CODE_OF_CONDUCT.md +3 -5
 - data/Gemfile +3 -3
 - data/README.md +8 -6
 - data/SECURITY.md +2 -2
 - data/eth.gemspec +10 -1
 - data/lib/eth/abi/decoder.rb +94 -39
 - data/lib/eth/abi/encoder.rb +85 -58
 - data/lib/eth/abi/function.rb +124 -0
 - data/lib/eth/abi/type.rb +69 -5
 - data/lib/eth/abi.rb +2 -0
 - data/lib/eth/bls.rb +68 -0
 - data/lib/eth/chain.rb +3 -0
 - data/lib/eth/client/http.rb +5 -8
 - data/lib/eth/client/ws.rb +323 -0
 - data/lib/eth/client.rb +44 -40
 - data/lib/eth/contract/error.rb +62 -0
 - data/lib/eth/contract/function.rb +21 -0
 - data/lib/eth/contract/function_output.rb +11 -3
 - data/lib/eth/contract.rb +55 -4
 - data/lib/eth/eip712.rb +48 -13
 - data/lib/eth/key.rb +1 -1
 - data/lib/eth/tx/eip1559.rb +32 -7
 - data/lib/eth/tx/eip2930.rb +31 -6
 - data/lib/eth/tx/eip4844.rb +401 -0
 - data/lib/eth/tx/eip7702.rb +34 -9
 - data/lib/eth/tx/legacy.rb +30 -6
 - data/lib/eth/tx.rb +45 -4
 - data/lib/eth/util.rb +19 -7
 - data/lib/eth/version.rb +1 -1
 - data/lib/eth.rb +1 -0
 - metadata +53 -15
 
    
        data/lib/eth/abi/encoder.rb
    CHANGED
    
    | 
         @@ -13,6 +13,7 @@ 
     | 
|
| 
       13 
13 
     | 
    
         
             
            # limitations under the License.
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
            # -*- encoding : ascii-8bit -*-
         
     | 
| 
      
 16 
     | 
    
         
            +
            require "bigdecimal"
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
            # Provides the {Eth} module.
         
     | 
| 
       18 
19 
     | 
    
         
             
            module Eth
         
     | 
| 
         @@ -33,55 +34,18 @@ module Eth 
     | 
|
| 
       33 
34 
     | 
    
         
             
                  def type(type, arg)
         
     | 
| 
       34 
35 
     | 
    
         
             
                    if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty?
         
     | 
| 
       35 
36 
     | 
    
         
             
                      raise EncodingError, "Argument must be a String" unless arg.instance_of? String
         
     | 
| 
      
 37 
     | 
    
         
            +
                      arg = handle_hex_string arg, type
         
     | 
| 
       36 
38 
     | 
    
         | 
| 
       37 
39 
     | 
    
         
             
                      # encodes strings and bytes
         
     | 
| 
       38 
40 
     | 
    
         
             
                      size = type Type.size_type, arg.size
         
     | 
| 
       39 
41 
     | 
    
         
             
                      padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
         
     | 
| 
       40 
42 
     | 
    
         
             
                      "#{size}#{arg}#{padding}"
         
     | 
| 
       41 
     | 
    
         
            -
                    elsif type.base_type == "tuple" && type.dimensions. 
     | 
| 
       42 
     | 
    
         
            -
                       
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                       
     | 
| 
       45 
     | 
    
         
            -
                      result
         
     | 
| 
       46 
     | 
    
         
            -
                    elsif type.dynamic? && arg.is_a?(Array)
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                      # encodes dynamic-sized arrays
         
     | 
| 
       49 
     | 
    
         
            -
                      head, tail = "", ""
         
     | 
| 
       50 
     | 
    
         
            -
                      head += type(Type.size_type, arg.size)
         
     | 
| 
       51 
     | 
    
         
            -
                      nested_sub = type.nested_sub
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                      # calculate offsets
         
     | 
| 
       54 
     | 
    
         
            -
                      if %w(string bytes).include?(type.base_type) && type.sub_type.empty?
         
     | 
| 
       55 
     | 
    
         
            -
                        offset = 0
         
     | 
| 
       56 
     | 
    
         
            -
                        arg.size.times do |i|
         
     | 
| 
       57 
     | 
    
         
            -
                          if i == 0
         
     | 
| 
       58 
     | 
    
         
            -
                            offset = arg.size * 32
         
     | 
| 
       59 
     | 
    
         
            -
                          else
         
     | 
| 
       60 
     | 
    
         
            -
                            number_of_words = ((arg[i - 1].size + 32 - 1) / 32).floor
         
     | 
| 
       61 
     | 
    
         
            -
                            total_bytes_length = number_of_words * 32
         
     | 
| 
       62 
     | 
    
         
            -
                            offset += total_bytes_length + 32
         
     | 
| 
       63 
     | 
    
         
            -
                          end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                          head += type(Type.size_type, offset)
         
     | 
| 
       66 
     | 
    
         
            -
                        end
         
     | 
| 
       67 
     | 
    
         
            -
                      elsif nested_sub.base_type == "tuple" && nested_sub.dynamic?
         
     | 
| 
       68 
     | 
    
         
            -
                        head += struct_offsets(nested_sub, arg)
         
     | 
| 
       69 
     | 
    
         
            -
                      end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                      arg.size.times do |i|
         
     | 
| 
       72 
     | 
    
         
            -
                        head += type nested_sub, arg[i]
         
     | 
| 
       73 
     | 
    
         
            -
                      end
         
     | 
| 
       74 
     | 
    
         
            -
                      "#{head}#{tail}"
         
     | 
| 
      
 43 
     | 
    
         
            +
                    elsif type.base_type == "tuple" && type.dimensions.empty?
         
     | 
| 
      
 44 
     | 
    
         
            +
                      tuple arg, type
         
     | 
| 
      
 45 
     | 
    
         
            +
                    elsif !type.dimensions.empty?
         
     | 
| 
      
 46 
     | 
    
         
            +
                      encode_array type, arg
         
     | 
| 
       75 
47 
     | 
    
         
             
                    else
         
     | 
| 
       76 
     | 
    
         
            -
                       
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                        # encode a primitive type
         
     | 
| 
       79 
     | 
    
         
            -
                        primitive_type type, arg
         
     | 
| 
       80 
     | 
    
         
            -
                      else
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                        # encode static-size arrays
         
     | 
| 
       83 
     | 
    
         
            -
                        arg.map { |x| type(type.nested_sub, x) }.join
         
     | 
| 
       84 
     | 
    
         
            -
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
                      primitive_type type, arg
         
     | 
| 
       85 
49 
     | 
    
         
             
                    end
         
     | 
| 
       86 
50 
     | 
    
         
             
                  end
         
     | 
| 
       87 
51 
     | 
    
         | 
| 
         @@ -122,6 +86,7 @@ module Eth 
     | 
|
| 
       122 
86 
     | 
    
         | 
| 
       123 
87 
     | 
    
         
             
                  # Properly encodes unsigned integers.
         
     | 
| 
       124 
88 
     | 
    
         
             
                  def uint(arg, type)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    arg = coerce_number arg
         
     | 
| 
       125 
90 
     | 
    
         
             
                    raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
         
     | 
| 
       126 
91 
     | 
    
         
             
                    raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
         
     | 
| 
       127 
92 
     | 
    
         
             
                    real_size = type.sub_type.to_i
         
     | 
| 
         @@ -132,6 +97,7 @@ module Eth 
     | 
|
| 
       132 
97 
     | 
    
         | 
| 
       133 
98 
     | 
    
         
             
                  # Properly encodes signed integers.
         
     | 
| 
       134 
99 
     | 
    
         
             
                  def int(arg, type)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    arg = coerce_number arg
         
     | 
| 
       135 
101 
     | 
    
         
             
                    raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
         
     | 
| 
       136 
102 
     | 
    
         
             
                    raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
         
     | 
| 
       137 
103 
     | 
    
         
             
                    real_size = type.sub_type.to_i
         
     | 
| 
         @@ -148,6 +114,7 @@ module Eth 
     | 
|
| 
       148 
114 
     | 
    
         | 
| 
       149 
115 
     | 
    
         
             
                  # Properly encodes unsigned fixed-point numbers.
         
     | 
| 
       150 
116 
     | 
    
         
             
                  def ufixed(arg, type)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    arg = coerce_number arg
         
     | 
| 
       151 
118 
     | 
    
         
             
                    raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
         
     | 
| 
       152 
119 
     | 
    
         
             
                    high, low = type.sub_type.split("x").map(&:to_i)
         
     | 
| 
       153 
120 
     | 
    
         
             
                    raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
         
     | 
| 
         @@ -156,6 +123,7 @@ module Eth 
     | 
|
| 
       156 
123 
     | 
    
         | 
| 
       157 
124 
     | 
    
         
             
                  # Properly encodes signed fixed-point numbers.
         
     | 
| 
       158 
125 
     | 
    
         
             
                  def fixed(arg, type)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    arg = coerce_number arg
         
     | 
| 
       159 
127 
     | 
    
         
             
                    raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
         
     | 
| 
       160 
128 
     | 
    
         
             
                    high, low = type.sub_type.split("x").map(&:to_i)
         
     | 
| 
       161 
129 
     | 
    
         
             
                    raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
         
     | 
| 
         @@ -185,8 +153,11 @@ module Eth 
     | 
|
| 
       185 
153 
     | 
    
         | 
| 
       186 
154 
     | 
    
         
             
                  # Properly encodes tuples.
         
     | 
| 
       187 
155 
     | 
    
         
             
                  def tuple(arg, type)
         
     | 
| 
       188 
     | 
    
         
            -
                     
     | 
| 
      
 156 
     | 
    
         
            +
                    unless arg.is_a?(Hash) || arg.is_a?(Array)
         
     | 
| 
      
 157 
     | 
    
         
            +
                      raise EncodingError, "Expecting Hash or Array: #{arg}"
         
     | 
| 
      
 158 
     | 
    
         
            +
                    end
         
     | 
| 
       189 
159 
     | 
    
         
             
                    raise EncodingError, "Expecting #{type.components.size} elements: #{arg}" unless arg.size == type.components.size
         
     | 
| 
      
 160 
     | 
    
         
            +
                    arg = arg.transform_keys(&:to_s) if arg.is_a?(Hash) # because component_type.name is String
         
     | 
| 
       190 
161 
     | 
    
         | 
| 
       191 
162 
     | 
    
         
             
                    static_size = 0
         
     | 
| 
       192 
163 
     | 
    
         
             
                    type.components.each_with_index do |component, i|
         
     | 
| 
         @@ -209,28 +180,84 @@ module Eth 
     | 
|
| 
       209 
180 
     | 
    
         
             
                        dynamic_values << dynamic_value
         
     | 
| 
       210 
181 
     | 
    
         
             
                        dynamic_offset += dynamic_value.size
         
     | 
| 
       211 
182 
     | 
    
         
             
                      else
         
     | 
| 
       212 
     | 
    
         
            -
                        offsets_and_static_values << type(component_type, arg.is_a?(Array) ? arg[i] : arg 
     | 
| 
      
 183 
     | 
    
         
            +
                        offsets_and_static_values << type(component_type, arg.is_a?(Array) ? arg[i] : arg.fetch(component_type.name))
         
     | 
| 
       213 
184 
     | 
    
         
             
                      end
         
     | 
| 
       214 
185 
     | 
    
         
             
                    end
         
     | 
| 
       215 
186 
     | 
    
         | 
| 
       216 
187 
     | 
    
         
             
                    offsets_and_static_values.join + dynamic_values.join
         
     | 
| 
       217 
188 
     | 
    
         
             
                  end
         
     | 
| 
       218 
189 
     | 
    
         | 
| 
       219 
     | 
    
         
            -
                   
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
                     
     | 
| 
       222 
     | 
    
         
            -
                     
     | 
| 
       223 
     | 
    
         
            -
                     
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
             
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
             
     | 
| 
      
 190 
     | 
    
         
            +
                  def coerce_number(arg)
         
     | 
| 
      
 191 
     | 
    
         
            +
                    return arg if arg.is_a? Numeric
         
     | 
| 
      
 192 
     | 
    
         
            +
                    return arg.to_i(0) if arg.is_a?(String) && arg.match?(/^-?(0x)?[0-9a-fA-F]+$/)
         
     | 
| 
      
 193 
     | 
    
         
            +
                    return BigDecimal(arg) if arg.is_a?(String) && arg.match?(/^-?\d+(\.\d+)?$/)
         
     | 
| 
      
 194 
     | 
    
         
            +
                    arg
         
     | 
| 
      
 195 
     | 
    
         
            +
                  end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  # Encodes array values of any dimensionality.
         
     | 
| 
      
 198 
     | 
    
         
            +
                  #
         
     | 
| 
      
 199 
     | 
    
         
            +
                  # @param type [Eth::Abi::Type] the type describing the array.
         
     | 
| 
      
 200 
     | 
    
         
            +
                  # @param values [Array] the Ruby values to encode.
         
     | 
| 
      
 201 
     | 
    
         
            +
                  # @return [String] ABI encoded array payload.
         
     | 
| 
      
 202 
     | 
    
         
            +
                  # @raise [EncodingError] if the value cardinality does not match static dimensions.
         
     | 
| 
      
 203 
     | 
    
         
            +
                  def encode_array(type, values)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    raise EncodingError, "Expecting Array value" unless values.is_a?(Array)
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                    required_length = type.dimensions.last
         
     | 
| 
      
 207 
     | 
    
         
            +
                    if required_length != 0 && values.size != required_length
         
     | 
| 
      
 208 
     | 
    
         
            +
                      raise EncodingError, "Expecting #{required_length} elements: #{values.size} provided"
         
     | 
| 
      
 209 
     | 
    
         
            +
                    end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                    nested_sub = type.nested_sub
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                    if required_length.zero?
         
     | 
| 
      
 214 
     | 
    
         
            +
                      encode_dynamic_array(nested_sub, values)
         
     | 
| 
      
 215 
     | 
    
         
            +
                    else
         
     | 
| 
      
 216 
     | 
    
         
            +
                      encode_static_array(nested_sub, values)
         
     | 
| 
      
 217 
     | 
    
         
            +
                    end
         
     | 
| 
      
 218 
     | 
    
         
            +
                  end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                  # Encodes dynamic-sized arrays, including nested tuples.
         
     | 
| 
      
 221 
     | 
    
         
            +
                  #
         
     | 
| 
      
 222 
     | 
    
         
            +
                  # @param nested_sub [Eth::Abi::Type] the element type.
         
     | 
| 
      
 223 
     | 
    
         
            +
                  # @param values [Array] elements to encode.
         
     | 
| 
      
 224 
     | 
    
         
            +
                  # @return [String] ABI encoded dynamic array payload.
         
     | 
| 
      
 225 
     | 
    
         
            +
                  def encode_dynamic_array(nested_sub, values)
         
     | 
| 
      
 226 
     | 
    
         
            +
                    head = type(Type.size_type, values.size)
         
     | 
| 
      
 227 
     | 
    
         
            +
                    element_heads, element_tails = encode_array_elements(nested_sub, values)
         
     | 
| 
      
 228 
     | 
    
         
            +
                    head + element_heads + element_tails
         
     | 
| 
      
 229 
     | 
    
         
            +
                  end
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
                  # Encodes static-sized arrays, including nested tuples.
         
     | 
| 
      
 232 
     | 
    
         
            +
                  #
         
     | 
| 
      
 233 
     | 
    
         
            +
                  # @param nested_sub [Eth::Abi::Type] the element type.
         
     | 
| 
      
 234 
     | 
    
         
            +
                  # @param values [Array] elements to encode.
         
     | 
| 
      
 235 
     | 
    
         
            +
                  # @return [String] ABI encoded static array payload.
         
     | 
| 
      
 236 
     | 
    
         
            +
                  def encode_static_array(nested_sub, values)
         
     | 
| 
      
 237 
     | 
    
         
            +
                    element_heads, element_tails = encode_array_elements(nested_sub, values)
         
     | 
| 
      
 238 
     | 
    
         
            +
                    element_heads + element_tails
         
     | 
| 
      
 239 
     | 
    
         
            +
                  end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                  # Encodes the head/tail portions for array elements.
         
     | 
| 
      
 242 
     | 
    
         
            +
                  #
         
     | 
| 
      
 243 
     | 
    
         
            +
                  # @param nested_sub [Eth::Abi::Type] the element type.
         
     | 
| 
      
 244 
     | 
    
         
            +
                  # @param values [Array] elements to encode.
         
     | 
| 
      
 245 
     | 
    
         
            +
                  # @return [Array<String, String>] head/tail encoded segments.
         
     | 
| 
      
 246 
     | 
    
         
            +
                  def encode_array_elements(nested_sub, values)
         
     | 
| 
      
 247 
     | 
    
         
            +
                    if nested_sub.dynamic?
         
     | 
| 
      
 248 
     | 
    
         
            +
                      head = ""
         
     | 
| 
      
 249 
     | 
    
         
            +
                      tail = ""
         
     | 
| 
      
 250 
     | 
    
         
            +
                      offset = values.size * 32
         
     | 
| 
      
 251 
     | 
    
         
            +
                      values.each do |value|
         
     | 
| 
      
 252 
     | 
    
         
            +
                        encoded = type(nested_sub, value)
         
     | 
| 
      
 253 
     | 
    
         
            +
                        head += type(Type.size_type, offset)
         
     | 
| 
      
 254 
     | 
    
         
            +
                        tail += encoded
         
     | 
| 
      
 255 
     | 
    
         
            +
                        offset += encoded.size
         
     | 
| 
       229 
256 
     | 
    
         
             
                      end
         
     | 
| 
       230 
     | 
    
         
            -
                       
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
      
 257 
     | 
    
         
            +
                      [head, tail]
         
     | 
| 
      
 258 
     | 
    
         
            +
                    else
         
     | 
| 
      
 259 
     | 
    
         
            +
                      [values.map { |value| type(nested_sub, value) }.join, ""]
         
     | 
| 
       232 
260 
     | 
    
         
             
                    end
         
     | 
| 
       233 
     | 
    
         
            -
                    result
         
     | 
| 
       234 
261 
     | 
    
         
             
                  end
         
     | 
| 
       235 
262 
     | 
    
         | 
| 
       236 
263 
     | 
    
         
             
                  # Properly encodes hash-strings.
         
     | 
| 
         @@ -0,0 +1,124 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright (c) 2016-2025 The Ruby-Eth Contributors
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Licensed under the Apache License, Version 2.0 (the "License");
         
     | 
| 
      
 4 
     | 
    
         
            +
            # you may not use this file except in compliance with the License.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # You may obtain a copy of the License at
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            #     http://www.apache.org/licenses/LICENSE-2.0
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            # Unless required by applicable law or agreed to in writing, software
         
     | 
| 
      
 10 
     | 
    
         
            +
            # distributed under the License is distributed on an "AS IS" BASIS,
         
     | 
| 
      
 11 
     | 
    
         
            +
            # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         
     | 
| 
      
 12 
     | 
    
         
            +
            # See the License for the specific language governing permissions and
         
     | 
| 
      
 13 
     | 
    
         
            +
            # limitations under the License.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            # -*- encoding : ascii-8bit -*-
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            # Provides the {Eth} module.
         
     | 
| 
      
 18 
     | 
    
         
            +
            module Eth
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
         
     | 
| 
      
 21 
     | 
    
         
            +
              module Abi
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                # Provides a module to decode transaction input data.
         
     | 
| 
      
 24 
     | 
    
         
            +
                module Function
         
     | 
| 
      
 25 
     | 
    
         
            +
                  extend self
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  # Build function signature string from ABI interface.
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # @param interface [Hash] ABI function interface.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @return [String] interface signature string.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def signature(interface)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    name = interface.fetch("name")
         
     | 
| 
      
 33 
     | 
    
         
            +
                    inputs = interface.fetch("inputs", [])
         
     | 
| 
      
 34 
     | 
    
         
            +
                    types = inputs.map { |i| type(i) }
         
     | 
| 
      
 35 
     | 
    
         
            +
                    "#{name}(#{types.join(",")})"
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  # Compute selector for ABI function interface.
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @param interface [Hash] ABI function interface.
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # @return [String] a hex-string selector.
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def selector(interface)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    sig = signature(interface)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig))[0, 8])
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  # Gets the input type for functions.
         
     | 
| 
      
 48 
     | 
    
         
            +
                  #
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # @param input [Hash] function input.
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # @return [String] input type.
         
     | 
| 
      
 51 
     | 
    
         
            +
                  def type(input)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    if input["type"] == "tuple"
         
     | 
| 
      
 53 
     | 
    
         
            +
                      "(#{input["components"].map { |c| type(c) }.join(",")})"
         
     | 
| 
      
 54 
     | 
    
         
            +
                    elsif input["type"] == "enum"
         
     | 
| 
      
 55 
     | 
    
         
            +
                      "uint8"
         
     | 
| 
      
 56 
     | 
    
         
            +
                    else
         
     | 
| 
      
 57 
     | 
    
         
            +
                      input["type"]
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  # A decoded function call.
         
     | 
| 
      
 62 
     | 
    
         
            +
                  class CallDescription
         
     | 
| 
      
 63 
     | 
    
         
            +
                    # The function ABI interface used to decode the call.
         
     | 
| 
      
 64 
     | 
    
         
            +
                    attr_accessor :function_interface
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    # The positional arguments of the call.
         
     | 
| 
      
 67 
     | 
    
         
            +
                    attr_accessor :args
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    # The named arguments of the call.
         
     | 
| 
      
 70 
     | 
    
         
            +
                    attr_accessor :kwargs
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    # The function selector.
         
     | 
| 
      
 73 
     | 
    
         
            +
                    attr_accessor :selector
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    # Creates a description object for a decoded function call.
         
     | 
| 
      
 76 
     | 
    
         
            +
                    #
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # @param function_interface [Hash] function ABI type.
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # @param selector [String] function selector hex-string.
         
     | 
| 
      
 79 
     | 
    
         
            +
                    # @param args [Array] decoded positional arguments.
         
     | 
| 
      
 80 
     | 
    
         
            +
                    # @param kwargs [Hash] decoded keyword arguments.
         
     | 
| 
      
 81 
     | 
    
         
            +
                    def initialize(function_interface, selector, args, kwargs)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      @function_interface = function_interface
         
     | 
| 
      
 83 
     | 
    
         
            +
                      @selector = selector
         
     | 
| 
      
 84 
     | 
    
         
            +
                      @args = args
         
     | 
| 
      
 85 
     | 
    
         
            +
                      @kwargs = kwargs
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    # The function name. (e.g. transfer)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    def name
         
     | 
| 
      
 90 
     | 
    
         
            +
                      @name ||= function_interface.fetch("name")
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    # The function signature. (e.g. transfer(address,uint256))
         
     | 
| 
      
 94 
     | 
    
         
            +
                    def signature
         
     | 
| 
      
 95 
     | 
    
         
            +
                      @signature ||= Function.signature(function_interface)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  # Decodes a transaction input with a set of ABI interfaces.
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # @param interfaces [Array] function ABI types.
         
     | 
| 
      
 102 
     | 
    
         
            +
                  # @param data [String] transaction input data.
         
     | 
| 
      
 103 
     | 
    
         
            +
                  # @return [CallDescription, nil] a CallDescription object or nil if selector unknown.
         
     | 
| 
      
 104 
     | 
    
         
            +
                  def decode(interfaces, data)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    data = Util.remove_hex_prefix(data)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    selector = Util.prefix_hex(data[0, 8])
         
     | 
| 
      
 107 
     | 
    
         
            +
                    payload = Util.prefix_hex(data[8..] || "")
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                    selector_to_interfaces = Hash[interfaces.map { |i| [selector(i), i] }]
         
     | 
| 
      
 110 
     | 
    
         
            +
                    if (interface = selector_to_interfaces[selector])
         
     | 
| 
      
 111 
     | 
    
         
            +
                      inputs = interface.fetch("inputs", [])
         
     | 
| 
      
 112 
     | 
    
         
            +
                      types = inputs.map { |i| type(i) }
         
     | 
| 
      
 113 
     | 
    
         
            +
                      args = Abi.decode(types, payload)
         
     | 
| 
      
 114 
     | 
    
         
            +
                      kwargs = {}
         
     | 
| 
      
 115 
     | 
    
         
            +
                      inputs.each_with_index do |input, i|
         
     | 
| 
      
 116 
     | 
    
         
            +
                        name = input.fetch("name", "")
         
     | 
| 
      
 117 
     | 
    
         
            +
                        kwargs[name.to_sym] = args[i] unless name.empty?
         
     | 
| 
      
 118 
     | 
    
         
            +
                      end
         
     | 
| 
      
 119 
     | 
    
         
            +
                      CallDescription.new(interface, selector, args, kwargs)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    end
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/eth/abi/type.rb
    CHANGED
    
    | 
         @@ -80,10 +80,27 @@ module Eth 
     | 
|
| 
       80 
80 
     | 
    
         
             
                      return
         
     | 
| 
       81 
81 
     | 
    
         
             
                    end
         
     | 
| 
       82 
82 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
                     
     | 
| 
      
 83 
     | 
    
         
            +
                    # ensure the type string is reasonable before attempting to parse
         
     | 
| 
      
 84 
     | 
    
         
            +
                    raise ParseError, "Invalid type format" unless type.is_a? String
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    if type.start_with?("tuple(") || type.start_with?("(")
         
     | 
| 
      
 87 
     | 
    
         
            +
                      tuple_str = type.start_with?("tuple(") ? type : "tuple#{type}"
         
     | 
| 
      
 88 
     | 
    
         
            +
                      inner, rest = extract_tuple(tuple_str)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      inner_types = split_tuple_types(inner)
         
     | 
| 
      
 90 
     | 
    
         
            +
                      inner_types.each { |t| Type.parse(t) }
         
     | 
| 
      
 91 
     | 
    
         
            +
                      base_type = "tuple"
         
     | 
| 
      
 92 
     | 
    
         
            +
                      sub_type = ""
         
     | 
| 
      
 93 
     | 
    
         
            +
                      dimension = rest
         
     | 
| 
      
 94 
     | 
    
         
            +
                      components ||= inner_types.map { |t| { "type" => t } }
         
     | 
| 
      
 95 
     | 
    
         
            +
                    else
         
     | 
| 
      
 96 
     | 
    
         
            +
                      match = /\A([a-z]+)([0-9]*x?[0-9]*)((?:\[\d+\]|\[\])*)\z/.match(type)
         
     | 
| 
      
 97 
     | 
    
         
            +
                      raise ParseError, "Invalid type format" unless match
         
     | 
| 
      
 98 
     | 
    
         
            +
                      _, base_type, sub_type, dimension = match.to_a
         
     | 
| 
      
 99 
     | 
    
         
            +
                      sub_type = "256" if %w[uint int].include?(base_type) && sub_type.empty?
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
       84 
101 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                    # type dimension can only be numeric
         
     | 
| 
       86 
     | 
    
         
            -
                    dims = dimension.scan(/\[[ 
     | 
| 
      
 102 
     | 
    
         
            +
                    # type dimension can only be numeric or empty for dynamic arrays
         
     | 
| 
      
 103 
     | 
    
         
            +
                    dims = dimension.scan(/\[\d+\]|\[\]/)
         
     | 
| 
       87 
104 
     | 
    
         
             
                    raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
         
     | 
| 
       88 
105 
     | 
    
         | 
| 
       89 
106 
     | 
    
         
             
                    # enforce base types
         
     | 
| 
         @@ -93,7 +110,7 @@ module Eth 
     | 
|
| 
       93 
110 
     | 
    
         
             
                    sub_type = sub_type.to_s
         
     | 
| 
       94 
111 
     | 
    
         
             
                    @base_type = base_type
         
     | 
| 
       95 
112 
     | 
    
         
             
                    @sub_type = sub_type
         
     | 
| 
       96 
     | 
    
         
            -
                    @dimensions = dims.map { |x| x[1...-1].to_i }
         
     | 
| 
      
 113 
     | 
    
         
            +
                    @dimensions = dims.map { |x| x == "[]" ? 0 : x[1...-1].to_i }
         
     | 
| 
       97 
114 
     | 
    
         
             
                    @components = components.map { |component| Abi::Type.parse(component["type"], component.dig("components"), component.dig("name")) } if components&.any?
         
     | 
| 
       98 
115 
     | 
    
         
             
                    @name = component_name
         
     | 
| 
       99 
116 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -123,7 +140,7 @@ module Eth 
     | 
|
| 
       123 
140 
     | 
    
         
             
                    if dimensions.empty?
         
     | 
| 
       124 
141 
     | 
    
         
             
                      if !(["string", "bytes", "tuple"].include?(base_type) and sub_type.empty?)
         
     | 
| 
       125 
142 
     | 
    
         
             
                        s = 32
         
     | 
| 
       126 
     | 
    
         
            -
                      elsif base_type == "tuple" and components 
     | 
| 
      
 143 
     | 
    
         
            +
                      elsif base_type == "tuple" and components&.none?(&:dynamic?)
         
     | 
| 
       127 
144 
     | 
    
         
             
                        s = components.sum(&:size)
         
     | 
| 
       128 
145 
     | 
    
         
             
                      end
         
     | 
| 
       129 
146 
     | 
    
         
             
                    elsif dimensions.last != 0 and !nested_sub.dynamic?
         
     | 
| 
         @@ -215,6 +232,53 @@ module Eth 
     | 
|
| 
       215 
232 
     | 
    
         
             
                      raise ParseError, "Unknown base type"
         
     | 
| 
       216 
233 
     | 
    
         
             
                    end
         
     | 
| 
       217 
234 
     | 
    
         
             
                  end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                  # Extracts the inner type list and trailing dimensions from an inline tuple definition.
         
     | 
| 
      
 237 
     | 
    
         
            +
                  def extract_tuple(type)
         
     | 
| 
      
 238 
     | 
    
         
            +
                    idx = 6 # skip "tuple("
         
     | 
| 
      
 239 
     | 
    
         
            +
                    depth = 1
         
     | 
| 
      
 240 
     | 
    
         
            +
                    while idx < type.length && depth > 0
         
     | 
| 
      
 241 
     | 
    
         
            +
                      case type[idx]
         
     | 
| 
      
 242 
     | 
    
         
            +
                      when "("
         
     | 
| 
      
 243 
     | 
    
         
            +
                        depth += 1
         
     | 
| 
      
 244 
     | 
    
         
            +
                      when ")"
         
     | 
| 
      
 245 
     | 
    
         
            +
                        depth -= 1
         
     | 
| 
      
 246 
     | 
    
         
            +
                      end
         
     | 
| 
      
 247 
     | 
    
         
            +
                      idx += 1
         
     | 
| 
      
 248 
     | 
    
         
            +
                    end
         
     | 
| 
      
 249 
     | 
    
         
            +
                    raise ParseError, "Invalid tuple format" unless depth.zero?
         
     | 
| 
      
 250 
     | 
    
         
            +
                    inner = type[6...(idx - 1)]
         
     | 
| 
      
 251 
     | 
    
         
            +
                    rest = type[idx..] || ""
         
     | 
| 
      
 252 
     | 
    
         
            +
                    [inner, rest]
         
     | 
| 
      
 253 
     | 
    
         
            +
                  end
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                  # Splits a tuple component list into individual type strings, handling nested tuples.
         
     | 
| 
      
 256 
     | 
    
         
            +
                  def split_tuple_types(str)
         
     | 
| 
      
 257 
     | 
    
         
            +
                    types = []
         
     | 
| 
      
 258 
     | 
    
         
            +
                    depth = 0
         
     | 
| 
      
 259 
     | 
    
         
            +
                    current = ""
         
     | 
| 
      
 260 
     | 
    
         
            +
                    str.each_char do |ch|
         
     | 
| 
      
 261 
     | 
    
         
            +
                      case ch
         
     | 
| 
      
 262 
     | 
    
         
            +
                      when "("
         
     | 
| 
      
 263 
     | 
    
         
            +
                        depth += 1
         
     | 
| 
      
 264 
     | 
    
         
            +
                        current << ch
         
     | 
| 
      
 265 
     | 
    
         
            +
                      when ")"
         
     | 
| 
      
 266 
     | 
    
         
            +
                        depth -= 1
         
     | 
| 
      
 267 
     | 
    
         
            +
                        current << ch
         
     | 
| 
      
 268 
     | 
    
         
            +
                      when ","
         
     | 
| 
      
 269 
     | 
    
         
            +
                        if depth.zero?
         
     | 
| 
      
 270 
     | 
    
         
            +
                          types << current
         
     | 
| 
      
 271 
     | 
    
         
            +
                          current = ""
         
     | 
| 
      
 272 
     | 
    
         
            +
                        else
         
     | 
| 
      
 273 
     | 
    
         
            +
                          current << ch
         
     | 
| 
      
 274 
     | 
    
         
            +
                        end
         
     | 
| 
      
 275 
     | 
    
         
            +
                      else
         
     | 
| 
      
 276 
     | 
    
         
            +
                        current << ch
         
     | 
| 
      
 277 
     | 
    
         
            +
                      end
         
     | 
| 
      
 278 
     | 
    
         
            +
                    end
         
     | 
| 
      
 279 
     | 
    
         
            +
                    types << current unless current.empty?
         
     | 
| 
      
 280 
     | 
    
         
            +
                    types
         
     | 
| 
      
 281 
     | 
    
         
            +
                  end
         
     | 
| 
       218 
282 
     | 
    
         
             
                end
         
     | 
| 
       219 
283 
     | 
    
         
             
              end
         
     | 
| 
       220 
284 
     | 
    
         
             
            end
         
     | 
    
        data/lib/eth/abi.rb
    CHANGED
    
    | 
         @@ -44,6 +44,7 @@ module Eth 
     | 
|
| 
       44 
44 
     | 
    
         
             
                  return solidity_packed(types, args) if packed
         
     | 
| 
       45 
45 
     | 
    
         
             
                  types = [types] unless types.instance_of? Array
         
     | 
| 
       46 
46 
     | 
    
         
             
                  args = [args] unless args.instance_of? Array
         
     | 
| 
      
 47 
     | 
    
         
            +
                  raise ArgumentError, "Types and values must be the same length" if types.length != args.length
         
     | 
| 
       47 
48 
     | 
    
         | 
| 
       48 
49 
     | 
    
         
             
                  # parse all types
         
     | 
| 
       49 
50 
     | 
    
         
             
                  parsed_types = types.map { |t| Type === t ? t : Type.parse(t) }
         
     | 
| 
         @@ -151,4 +152,5 @@ require "eth/abi/packed/encoder" 
     | 
|
| 
       151 
152 
     | 
    
         
             
            require "eth/abi/decoder"
         
     | 
| 
       152 
153 
     | 
    
         
             
            require "eth/abi/encoder"
         
     | 
| 
       153 
154 
     | 
    
         
             
            require "eth/abi/event"
         
     | 
| 
      
 155 
     | 
    
         
            +
            require "eth/abi/function"
         
     | 
| 
       154 
156 
     | 
    
         
             
            require "eth/abi/type"
         
     | 
    
        data/lib/eth/bls.rb
    ADDED
    
    | 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "bls"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Eth
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Helper methods for interacting with BLS12-381 points and signatures
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Bls
         
     | 
| 
      
 8 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # Decode a compressed G1 public key from hex.
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @param [String] hex a compressed G1 point
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @return [BLS::PointG1]
         
     | 
| 
      
 13 
     | 
    
         
            +
                def decode_public_key(hex)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  BLS::PointG1.from_hex Util.remove_hex_prefix(hex)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # Encode a G1 public key to compressed hex.
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @param [BLS::PointG1] point
         
     | 
| 
      
 19 
     | 
    
         
            +
                # @return [String] hex string prefixed with 0x
         
     | 
| 
      
 20 
     | 
    
         
            +
                def encode_public_key(point)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Util.prefix_hex point.to_hex(compressed: true)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Decode a compressed G2 signature from hex.
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @param [String] hex a compressed G2 point
         
     | 
| 
      
 26 
     | 
    
         
            +
                # @return [BLS::PointG2]
         
     | 
| 
      
 27 
     | 
    
         
            +
                def decode_signature(hex)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  BLS::PointG2.from_hex Util.remove_hex_prefix(hex)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                # Encode a G2 signature to compressed hex.
         
     | 
| 
      
 32 
     | 
    
         
            +
                # @param [BLS::PointG2] point
         
     | 
| 
      
 33 
     | 
    
         
            +
                # @return [String] hex string prefixed with 0x
         
     | 
| 
      
 34 
     | 
    
         
            +
                def encode_signature(point)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Util.prefix_hex point.to_hex(compressed: true)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                # Derive a compressed public key from a private key.
         
     | 
| 
      
 39 
     | 
    
         
            +
                # @param [String] priv_hex private key as hex
         
     | 
| 
      
 40 
     | 
    
         
            +
                # @return [String] compressed G1 public key (hex)
         
     | 
| 
      
 41 
     | 
    
         
            +
                def get_public_key(priv_hex)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  key = BLS.get_public_key Util.remove_hex_prefix(priv_hex)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  encode_public_key key
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                # Sign a message digest with the given private key.
         
     | 
| 
      
 47 
     | 
    
         
            +
                # @param [String] message message digest (hex)
         
     | 
| 
      
 48 
     | 
    
         
            +
                # @param [String] priv_hex private key as hex
         
     | 
| 
      
 49 
     | 
    
         
            +
                # @return [String] compressed G2 signature (hex)
         
     | 
| 
      
 50 
     | 
    
         
            +
                def sign(message, priv_hex)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  sig = BLS.sign Util.remove_hex_prefix(message),
         
     | 
| 
      
 52 
     | 
    
         
            +
                                 Util.remove_hex_prefix(priv_hex)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  encode_signature sig
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # Verify a BLS signature using pairings. This mirrors the behaviour of
         
     | 
| 
      
 57 
     | 
    
         
            +
                # the BLS12-381 pairing precompile.
         
     | 
| 
      
 58 
     | 
    
         
            +
                # @param [String] message message digest (hex)
         
     | 
| 
      
 59 
     | 
    
         
            +
                # @param [String] signature_hex compressed G2 signature (hex)
         
     | 
| 
      
 60 
     | 
    
         
            +
                # @param [String] pubkey_hex compressed G1 public key (hex)
         
     | 
| 
      
 61 
     | 
    
         
            +
                # @return [Boolean] verification result
         
     | 
| 
      
 62 
     | 
    
         
            +
                def verify(message, signature_hex, pubkey_hex)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  signature = decode_signature(signature_hex)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  pubkey = decode_public_key(pubkey_hex)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  BLS.verify(signature, Util.remove_hex_prefix(message), pubkey)
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/eth/chain.rb
    CHANGED
    
    
    
        data/lib/eth/client/http.rb
    CHANGED
    
    | 
         @@ -12,7 +12,8 @@ 
     | 
|
| 
       12 
12 
     | 
    
         
             
            # See the License for the specific language governing permissions and
         
     | 
| 
       13 
13 
     | 
    
         
             
            # limitations under the License.
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
            require " 
     | 
| 
      
 15 
     | 
    
         
            +
            require "uri"
         
     | 
| 
      
 16 
     | 
    
         
            +
            require "httpx"
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
            # Provides the {Eth} module.
         
     | 
| 
       18 
19 
     | 
    
         
             
            module Eth
         
     | 
| 
         @@ -57,6 +58,7 @@ module Eth 
     | 
|
| 
       57 
58 
     | 
    
         
             
                  else
         
     | 
| 
       58 
59 
     | 
    
         
             
                    @uri = uri
         
     | 
| 
       59 
60 
     | 
    
         
             
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @client = HTTPX.plugin(:persistent).with(headers: { "Content-Type" => "application/json" })
         
     | 
| 
       60 
62 
     | 
    
         
             
                end
         
     | 
| 
       61 
63 
     | 
    
         | 
| 
       62 
64 
     | 
    
         
             
                # Sends an RPC request to the connected HTTP client.
         
     | 
| 
         @@ -64,13 +66,8 @@ module Eth 
     | 
|
| 
       64 
66 
     | 
    
         
             
                # @param payload [Hash] the RPC request parameters.
         
     | 
| 
       65 
67 
     | 
    
         
             
                # @return [String] a JSON-encoded response.
         
     | 
| 
       66 
68 
     | 
    
         
             
                def send_request(payload)
         
     | 
| 
       67 
     | 
    
         
            -
                   
     | 
| 
       68 
     | 
    
         
            -
                   
     | 
| 
       69 
     | 
    
         
            -
                  header = { "Content-Type" => "application/json" }
         
     | 
| 
       70 
     | 
    
         
            -
                  request = Net::HTTP::Post.new(@uri, header)
         
     | 
| 
       71 
     | 
    
         
            -
                  request.body = payload
         
     | 
| 
       72 
     | 
    
         
            -
                  response = http.request(request)
         
     | 
| 
       73 
     | 
    
         
            -
                  response.body
         
     | 
| 
      
 69 
     | 
    
         
            +
                  response = @client.post(@uri, body: payload)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  response.body.to_s
         
     | 
| 
       74 
71 
     | 
    
         
             
                end
         
     | 
| 
       75 
72 
     | 
    
         
             
              end
         
     | 
| 
       76 
73 
     | 
    
         |