abicoder 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
1
+ module ABI
2
+ class Type
3
+
4
+ class ParseError < StandardError; end
5
+
6
+
7
+ ## nested inside Type - why? why not?
8
+ class Parser
9
+
10
+ TUPLE_TYPE_RX = /^\((.*)\)
11
+ ((\[[0-9]*\])*)
12
+ /x
13
+
14
+ def self.parse( type )
15
+ if type =~ TUPLE_TYPE_RX
16
+ types = _parse_tuple_type( $1 )
17
+ dims = _parse_dims( $2 )
18
+
19
+ parsed_types = types.map{ |t| parse( t ) }
20
+
21
+ return _parse_array_type( Tuple.new( parsed_types ), dims )
22
+ end
23
+
24
+ base, sub, dims = _parse_base_type( type )
25
+
26
+ _validate_base_type( base, sub )
27
+
28
+ subtype = case base
29
+ when 'string' then String.new
30
+ when 'bytes' then sub ? FixedBytes.new( sub ) : Bytes.new
31
+ when 'uint' then Uint.new( sub )
32
+ when 'int' then Int.new( sub )
33
+ when 'address' then Address.new
34
+ when 'bool' then Bool.new
35
+ else
36
+ ## puts " type: >#{type}<"
37
+ raise ParseError, "Unrecognized type base: #{base}"
38
+ end
39
+
40
+ _parse_array_type( subtype, dims )
41
+ end
42
+
43
+ ##
44
+ # Crazy regexp to seperate out base type component (eg. uint), size (eg.
45
+ # 256, 128, nil), array component (eg. [], [45], nil)
46
+ #
47
+ BASE_TYPE_RX = /([a-z]*)
48
+ ([0-9]*)
49
+ ((\[[0-9]*\])*)
50
+ /x
51
+
52
+ def self._parse_base_type( str )
53
+ _, base, subscript, dimension = BASE_TYPE_RX.match( str ).to_a
54
+
55
+ ## note: use [Integer,Integer] array in the future for sub
56
+ ## for fixed (e.g. 128x128 => [128,128]) or such
57
+ ## for now always assume single integer (as string)
58
+ sub = subscript == '' ? nil : subscript.to_i
59
+
60
+ ## e.g. turn "[][1][2]" into [-1,1,2]
61
+ ## or "" into [] -- that is, empty array
62
+ dims = _parse_dims( dimension )
63
+
64
+ [base, sub, dims]
65
+ end
66
+
67
+ def self._parse_dims( str )
68
+ dims = str.scan( /\[[0-9]*\]/ )
69
+
70
+ ## note: return -1 for dynamic array size e.g. []
71
+ ## e.g. "[]"[1...-1] => ""
72
+ ## "[0]"[1...-1] => "0"
73
+ ## "[1]"[1...-1] => "1"
74
+ dims = dims.map do |dim|
75
+ size = dim[1...-1]
76
+ size == '' ? -1 : size.to_i
77
+ end
78
+ dims
79
+ end
80
+
81
+ def self._parse_array_type( subtype, dims )
82
+ ##
83
+ ## todo/check - double check if the order in reverse
84
+ ## in solidity / abi encoding / decoding?
85
+ ##
86
+ dims.each do |dim|
87
+ subtype = if dim == -1
88
+ Array.new( subtype )
89
+ else
90
+ FixedArray.new( subtype, dim )
91
+ end
92
+ end
93
+
94
+ subtype
95
+ end
96
+
97
+
98
+ def self._validate_base_type( base, sub )
99
+ case base
100
+ when 'string'
101
+ # note: string can not have any suffix
102
+ raise ParseError, "String cannot have suffix" if sub
103
+ when 'bytes'
104
+ raise ParseError, "Maximum 32 bytes for fixed-length bytes" if sub && sub > 32
105
+ when 'uint', 'int'
106
+ raise ParseError, "Integer type must have numerical suffix" unless sub
107
+ raise ParseError, "Integer size out of bounds" unless sub >= 8 && sub <= 256
108
+ raise ParseError, "Integer size must be multiple of 8" unless sub % 8 == 0
109
+ when 'address'
110
+ raise ParseError, "Address cannot have suffix" if sub
111
+ when 'bool'
112
+ raise ParseError, "Bool cannot have suffix" if sub
113
+ else
114
+ ## puts " type: >#{type}<"
115
+ raise ParseError, "Unrecognized type base: #{base}"
116
+ end
117
+ end
118
+
119
+
120
+
121
+ def self._parse_tuple_type( str )
122
+ ## note: types assumes string WITHOUT enclosing () e.g.
123
+ ## tuple(string,string,bool) => expected as "string,string,bool"
124
+
125
+ depth = 0
126
+ collected = []
127
+ current = ''
128
+
129
+ ### todo/fix: replace with a simple parser!!!
130
+ ## allow () and move verbose tuple() too!!!
131
+ str.each_char do |c|
132
+ case c
133
+ when ',' then
134
+ if depth == 0
135
+ collected << current
136
+ current = ''
137
+ else
138
+ current += c
139
+ end
140
+ when '(' then
141
+ depth += 1
142
+ current += c
143
+ when ')' then
144
+ depth -= 1
145
+ current += c
146
+ else
147
+ current += c
148
+ end
149
+ end
150
+ collected << current unless current.empty?
151
+
152
+ collected
153
+ end
154
+
155
+ end # class Parser
156
+ end # class Type
157
+ end # module ABI
158
+
@@ -0,0 +1,155 @@
1
+ module ABI
2
+
3
+
4
+ #######
5
+ ## for now use (get inspired)
6
+ ## by the type names used by abi coder in rust
7
+ ## see https://github.com/rust-ethereum/ethabi/blob/master/ethabi/src/param_type/param_type.rs
8
+
9
+
10
+ class Type
11
+
12
+ def self.parse( type ) ## convenience helper
13
+ Parser.parse( type )
14
+ end
15
+
16
+ ##
17
+ # Get the static size of a type, or nil if dynamic.
18
+ #
19
+ # @return [Integer, NilClass] size of static type, or nil for dynamic type
20
+ #
21
+ def size
22
+ ## check/todo: what error to raise for not implemented / method not defined???
23
+ raise ArgumentError, "no required size method defined for Type subclass #{self.class.name}; sorry"
24
+ end
25
+ def dynamic?() size.nil?; end
26
+
27
+ def format
28
+ ## check/todo: what error to raise for not implemented / method not defined???
29
+ raise ArgumentError, "no required format method defined for Type subclass #{self.class.name}; sorry"
30
+ end
31
+
32
+ ####
33
+ ## default implementation
34
+ ## assume equal if class match (e.g. Address == Address)
35
+ ## - use format string for generic compare - why? why not?
36
+ end
37
+
38
+
39
+
40
+ class Address < Type
41
+ def size() 32; end ## note: address is always 20 bytes; BUT uses 32 bytes (with padding)
42
+ def format() 'address'; end
43
+ def ==(another_type) another_type.kind_of?( Address ); end
44
+ end # class Address
45
+
46
+ class Bytes < Type
47
+ def size() nil; end ## note: dynamic (not known at compile-time)
48
+ def format() 'bytes'; end
49
+ def ==(another_type) another_type.kind_of?( Bytes ); end
50
+ end # class Bytes
51
+
52
+ class FixedBytes < Type
53
+ attr_reader :length
54
+ def initialize( length )
55
+ @length = length # in bytes (1,2,...32)
56
+ end
57
+ def size() 32; end ## note: always uses 32 bytes (with padding)
58
+ def format() "bytes#{@length}"; end
59
+ def ==(another_type)
60
+ another_type.kind_of?( FixedBytes ) && @length == another_type.length
61
+ end
62
+ end # class FixedBytes
63
+
64
+ class Int < Type
65
+ attr_reader :bits
66
+ def initialize( bits=256 )
67
+ @bits = bits # in bits (8,16,...256)
68
+ end
69
+ def size() 32; end ## note: always uses 32 bytes (with padding)
70
+ def format() "int#{@bits}"; end
71
+ def ==(another_type)
72
+ another_type.kind_of?( Int ) && @bits == another_type.bits
73
+ end
74
+ end # class Int
75
+
76
+ class Uint < Type
77
+ attr_reader :bits
78
+ def initialize( bits=256 )
79
+ @bits = bits # in bits (8,16,...256)
80
+ end
81
+ def size() 32; end ## note: always uses 32 bytes (with padding)
82
+ def format() "uint#{@bits}"; end
83
+ def ==(another_type)
84
+ another_type.kind_of?( Uint ) && @bits == another_type.bits
85
+ end
86
+ end # class Uint
87
+
88
+ class Bool < Type
89
+ def size() 32; end ## note: always uses 32 bytes (with padding)
90
+ def format() 'bool'; end
91
+ def ==(another_type) another_type.kind_of?( Bool ); end
92
+ end # class Bool
93
+
94
+ class String < Type
95
+ def size() nil; end ## note: dynamic (not known at compile-time)
96
+ def format() 'string'; end
97
+ def ==(another_type) another_type.kind_of?( String ); end
98
+ end # class String
99
+
100
+ class Array < Type
101
+ attr_reader :subtype
102
+ def initialize( subtype )
103
+ @subtype = subtype
104
+ end
105
+ def size() nil; end ## note: dynamic (not known at compile-time)
106
+ def format() "#{@subtype.format}[]"; end
107
+ def ==(another_type)
108
+ another_type.kind_of?( Array ) && @subtype == another_type.subtype
109
+ end
110
+ end # class Array
111
+
112
+ class FixedArray < Type
113
+ attr_reader :subtype
114
+ attr_reader :dim
115
+ def initialize( subtype, dim )
116
+ @subtype = subtype
117
+ @dim = dim
118
+ end
119
+
120
+ def size
121
+ @subtype.dynamic? ? nil : @dim * subtype.size
122
+ end
123
+ def format() "#{@subtype.format}[#{@dim}]"; end
124
+ def ==(another_type)
125
+ another_type.kind_of?( FixedArray ) &&
126
+ @dim == another_type.dim &&
127
+ @subtype == another_type.subtype
128
+ end
129
+ end # class FixedArray
130
+
131
+ class Tuple < Type
132
+ attr_reader :types
133
+
134
+ def initialize( types )
135
+ @types = types
136
+ end
137
+
138
+ def size
139
+ s = 0
140
+ @types.each do |type|
141
+ ts = type.size
142
+ return nil if ts.nil?
143
+ s += ts
144
+ end
145
+ s
146
+ end
147
+ def format
148
+ "(#{@types.map {|t| t.format }.join(',')})" ## rebuild minimal string
149
+ end
150
+ def ==(another_type)
151
+ another_type.kind_of?( Tuple ) && @types == another_type.types
152
+ end
153
+ end # class Tuple
154
+ end # module ABI
155
+
@@ -2,7 +2,7 @@
2
2
  module ABICoder
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- PATCH = 0
5
+ PATCH = 1
6
6
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
7
 
8
8
  def self.version
data/lib/abicoder.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  ## our own code
4
4
  require_relative 'abicoder/version' # note: let version always go first
5
5
 
6
+
6
7
  module ABI
7
8
  ###################
8
9
  ### some (shared) constants (move to constants.rb or such - why? why not?)
@@ -17,29 +18,50 @@ module ABI
17
18
  BYTE_ONE = "\x01".b.freeze ## note: used for encoding bool for now
18
19
 
19
20
 
20
- UINT_MAX = 2**256 - 1
21
+ UINT_MAX = 2**256 - 1 ## same as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
21
22
  UINT_MIN = 0
22
- INT_MAX = 2**255 - 1
23
- INT_MIN = -2**255
24
-
23
+ INT_MAX = 2**255 - 1 ## same as 57896044618658097711785492504343953926634992332820282019728792003956564819967
24
+ INT_MIN = -2**255 ## same as -57896044618658097711785492504343953926634992332820282019728792003956564819968
25
25
 
26
- ### todo/check - what is TT the abbrevation for ???
27
- TT32 = 2**32
28
- TT40 = 2**40 ## todo/check - used where and why???
29
- TT160 = 2**160 ## todo/check - used where and why???
30
- TT256 = 2**256 ## todo/check - used where and why???
31
- TT64M1 = 2**64 - 1 ## todo/check - used where and why???
32
26
  end # module ABI
33
27
 
34
- require_relative 'abicoder/utils'
35
28
 
29
+ require_relative 'abicoder/types'
30
+ require_relative 'abicoder/parser'
31
+
32
+
33
+ require_relative 'abicoder/encoder'
34
+ require_relative 'abicoder/decoder'
35
+
36
+
37
+ module ABI
38
+ def self.encoder
39
+ @encoder ||= Encoder.new
40
+ end
41
+ def self.decoder
42
+ @decoder ||= Decoder.new
43
+ end
44
+
45
+
46
+ def self.encode( types, args )
47
+ encoder.encode( types, args )
48
+ end
36
49
 
37
- require_relative 'abicoder/type'
38
- require_relative 'abicoder/type_tuple'
50
+ def self.decode( types, data, raise_errors = false )
51
+ decoder.decode( types, data, raise_errors )
52
+ end
39
53
 
40
- require_relative 'abicoder/codec'
54
+ ## add alternate _abi names - why? why not?
55
+ class << self
56
+ alias_method :encode_abi, :encode
57
+ alias_method :decode_abi, :decode
58
+ end
59
+ end ## module ABI
41
60
 
42
61
 
62
+ ################
63
+ ## add convenience alternate spellings - why? why not?
64
+ Abi = ABI
43
65
 
44
66
 
45
67
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abicoder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-30 00:00:00.000000000 Z
11
+ date: 2023-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -60,10 +60,10 @@ files:
60
60
  - README.md
61
61
  - Rakefile
62
62
  - lib/abicoder.rb
63
- - lib/abicoder/codec.rb
64
- - lib/abicoder/type.rb
65
- - lib/abicoder/type_tuple.rb
66
- - lib/abicoder/utils.rb
63
+ - lib/abicoder/decoder.rb
64
+ - lib/abicoder/encoder.rb
65
+ - lib/abicoder/parser.rb
66
+ - lib/abicoder/types.rb
67
67
  - lib/abicoder/version.rb
68
68
  homepage: https://github.com/rubycocos/blockchain
69
69
  licenses: