abicoder 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +4 -4
- data/README.md +87 -18
- data/lib/abicoder/decoder.rb +208 -0
- data/lib/abicoder/encoder.rb +279 -0
- data/lib/abicoder/parser.rb +158 -0
- data/lib/abicoder/types.rb +155 -0
- data/lib/abicoder/version.rb +1 -1
- data/lib/abicoder.rb +36 -14
- metadata +6 -6
- data/lib/abicoder/codec.rb +0 -413
- data/lib/abicoder/type.rb +0 -141
- data/lib/abicoder/type_tuple.rb +0 -106
- data/lib/abicoder/utils.rb +0 -81
@@ -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
|
+
|
data/lib/abicoder/version.rb
CHANGED
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
|
-
|
38
|
-
|
50
|
+
def self.decode( types, data, raise_errors = false )
|
51
|
+
decoder.decode( types, data, raise_errors )
|
52
|
+
end
|
39
53
|
|
40
|
-
|
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.
|
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:
|
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/
|
64
|
-
- lib/abicoder/
|
65
|
-
- lib/abicoder/
|
66
|
-
- lib/abicoder/
|
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:
|