ethlite 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +3 -5
- data/Rakefile +1 -1
- data/lib/digest/{keccak256.rb → keccak.rb} +32 -18
- data/lib/digest/sha3.rb +31 -21
- data/lib/ethlite/abi/abi_coder.rb +3 -0
- data/lib/ethlite/abi/type.rb +10 -13
- data/lib/ethlite/abi/utils.rb +51 -49
- data/lib/ethlite/constant.rb +38 -0
- data/lib/ethlite/contract.rb +39 -23
- data/lib/ethlite/version.rb +1 -1
- data/lib/ethlite.rb +14 -9
- data/lib/jsonrpc/jsonrpc.rb +48 -0
- metadata +6 -8
- data/lib/ethlite/abi/constant.rb +0 -32
- data/lib/ethlite/abi/exceptions.rb +0 -29
- data/lib/ethlite/rpc.rb +0 -48
- data/lib/ethlite/utility.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6a9601ad8c91c592541ffc721dbe9bbb8572683ef88c995a1bb810f7da79e33
|
4
|
+
data.tar.gz: 4ddf8c5a91c9a370e580bc9519f4d7c9832f863f31e4dfb8fa2b0505c2c6d6e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12d4cd45f40f8ecf3835f058e2ac5d4e9a9b361e3cd91fd5bca193e982c7fd726ff3ffab08ed7bedb543c3800e76808364c51f2992d4971816629ac170f700f6
|
7
|
+
data.tar.gz: 236164ce176d5b50187e7402729a895130354f929df269e3b9d7b04472300b1661b9992a5579ba53064cbe0b7aee95e21aeeaae65225519ca55707caad771533
|
data/Manifest.txt
CHANGED
@@ -2,15 +2,13 @@ CHANGELOG.md
|
|
2
2
|
Manifest.txt
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
|
-
lib/digest/
|
5
|
+
lib/digest/keccak.rb
|
6
6
|
lib/digest/sha3.rb
|
7
7
|
lib/ethlite.rb
|
8
8
|
lib/ethlite/abi/abi_coder.rb
|
9
|
-
lib/ethlite/abi/constant.rb
|
10
|
-
lib/ethlite/abi/exceptions.rb
|
11
9
|
lib/ethlite/abi/type.rb
|
12
10
|
lib/ethlite/abi/utils.rb
|
11
|
+
lib/ethlite/constant.rb
|
13
12
|
lib/ethlite/contract.rb
|
14
|
-
lib/ethlite/rpc.rb
|
15
|
-
lib/ethlite/utility.rb
|
16
13
|
lib/ethlite/version.rb
|
14
|
+
lib/jsonrpc/jsonrpc.rb
|
data/Rakefile
CHANGED
@@ -8,8 +8,15 @@
|
|
8
8
|
## require 'digest'
|
9
9
|
|
10
10
|
|
11
|
+
|
12
|
+
##
|
13
|
+
# note: use KeccakLite (or KeccakZero? KeccakSHA3?) instead of Keccak
|
14
|
+
# to allow usage of native w/ c-extension Keccak and "lite" version
|
15
|
+
|
16
|
+
|
17
|
+
|
11
18
|
module Digest
|
12
|
-
class
|
19
|
+
class KeccakLite < Digest::Class
|
13
20
|
PILN = [10, 7, 11, 17, 18, 3, 5, 16,
|
14
21
|
8, 21, 24, 4, 15, 23, 19, 13,
|
15
22
|
12, 2, 20, 14, 22, 9, 6, 1]
|
@@ -27,16 +34,18 @@ module Digest
|
|
27
34
|
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
28
35
|
0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
|
29
36
|
|
30
|
-
|
31
|
-
|
37
|
+
## note: changed initialize to use param hash_size = 512
|
38
|
+
## do NOT hard-code only 256 bit
|
39
|
+
def initialize( hash_size = 512 )
|
40
|
+
@size = hash_size / 8
|
32
41
|
@buffer = ''
|
33
42
|
end
|
34
43
|
|
35
|
-
def << s
|
44
|
+
def <<( s )
|
36
45
|
@buffer << s
|
37
46
|
self
|
38
47
|
end
|
39
|
-
|
48
|
+
alias_method :update, :<<
|
40
49
|
|
41
50
|
def reset
|
42
51
|
@buffer.clear
|
@@ -44,37 +53,40 @@ module Digest
|
|
44
53
|
end
|
45
54
|
|
46
55
|
def finish
|
47
|
-
s = Array.new 25, 0
|
56
|
+
s = Array.new( 25, 0 )
|
48
57
|
width = 200 - @size * 2
|
58
|
+
|
59
|
+
### note: padding changed in final FIPS PUB 202 standard
|
60
|
+
## from "\x01" to "\x06"
|
49
61
|
padding = "\x01"
|
50
62
|
|
51
63
|
buffer = @buffer
|
52
64
|
buffer << padding << "\0" * (width - buffer.size % width)
|
53
65
|
buffer[-1] = (buffer[-1].ord | 0x80).chr
|
54
66
|
|
55
|
-
0.step buffer.size - 1, width do |j|
|
56
|
-
quads = buffer[j, width].unpack 'Q*'
|
67
|
+
0.step( buffer.size - 1, width ) do |j|
|
68
|
+
quads = buffer[j, width].unpack( 'Q*' )
|
57
69
|
(width / 8).times do |i|
|
58
70
|
s[i] ^= quads[i]
|
59
71
|
end
|
60
72
|
|
61
|
-
|
73
|
+
_keccak( s )
|
62
74
|
end
|
63
75
|
|
64
76
|
s.pack('Q*')[0, @size]
|
65
77
|
end
|
66
78
|
|
67
79
|
private
|
68
|
-
def
|
69
|
-
24.times.each_with_object [] do |round, a|
|
80
|
+
def _keccak( s )
|
81
|
+
24.times.each_with_object( [] ) do |round, a|
|
70
82
|
# Theta
|
71
83
|
5.times do |i|
|
72
84
|
a[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]
|
73
85
|
end
|
74
86
|
|
75
87
|
5.times do |i|
|
76
|
-
t = a[(i + 4) % 5] ^
|
77
|
-
0.step 24, 5 do |j|
|
88
|
+
t = a[(i + 4) % 5] ^ _rotate(a[(i + 1) % 5], 1)
|
89
|
+
0.step( 24, 5 ) do |j|
|
78
90
|
s[j + i] ^= t
|
79
91
|
end
|
80
92
|
end
|
@@ -84,12 +96,12 @@ module Digest
|
|
84
96
|
24.times do |i|
|
85
97
|
j = PILN[i]
|
86
98
|
a[0] = s[j]
|
87
|
-
s[j] =
|
99
|
+
s[j] = _rotate( t, ROTC[i] )
|
88
100
|
t = a[0]
|
89
101
|
end
|
90
102
|
|
91
103
|
# Chi
|
92
|
-
0.step 24, 5 do |j|
|
104
|
+
0.step( 24, 5 ) do |j|
|
93
105
|
5.times do |i|
|
94
106
|
a[i] = s[j + i]
|
95
107
|
end
|
@@ -104,8 +116,10 @@ module Digest
|
|
104
116
|
end
|
105
117
|
end
|
106
118
|
|
107
|
-
def
|
119
|
+
def _rotate( x, y )
|
108
120
|
(x << y | x >> 64 - y) & (1 << 64) - 1
|
109
121
|
end
|
110
|
-
|
111
|
-
|
122
|
+
|
123
|
+
|
124
|
+
end # class KeccakLite
|
125
|
+
end # module Digest
|
data/lib/digest/sha3.rb
CHANGED
@@ -6,8 +6,13 @@
|
|
6
6
|
|
7
7
|
## require 'digest'
|
8
8
|
|
9
|
+
##
|
10
|
+
# note: use SHA3Lite (or SHA3Zero? ZeroSHA3?) instead of SHA3
|
11
|
+
# to allow usage of native w/ c-extension SHA3 and "lite" version
|
12
|
+
|
13
|
+
|
9
14
|
module Digest
|
10
|
-
class
|
15
|
+
class SHA3Lite < Digest::Class
|
11
16
|
PILN = [10, 7, 11, 17, 18, 3, 5, 16,
|
12
17
|
8, 21, 24, 4, 15, 23, 19, 13,
|
13
18
|
12, 2, 20, 14, 22, 9, 6, 1]
|
@@ -25,16 +30,16 @@ module Digest
|
|
25
30
|
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
26
31
|
0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
|
27
32
|
|
28
|
-
def initialize hash_size = 512
|
29
|
-
@size
|
30
|
-
@buffer = ''
|
33
|
+
def initialize( hash_size = 512 )
|
34
|
+
@size = hash_size / 8
|
35
|
+
@buffer = '' ## todo/check: make sure buffer is with encoding BINARY - why? why not?
|
31
36
|
end
|
32
37
|
|
33
|
-
def << s
|
38
|
+
def <<( s )
|
34
39
|
@buffer << s
|
35
40
|
self
|
36
41
|
end
|
37
|
-
|
42
|
+
alias_method :update, :<<
|
38
43
|
|
39
44
|
def reset
|
40
45
|
@buffer.clear
|
@@ -42,36 +47,40 @@ module Digest
|
|
42
47
|
end
|
43
48
|
|
44
49
|
def finish
|
45
|
-
s = Array.new 25, 0
|
50
|
+
s = Array.new( 25, 0 )
|
46
51
|
width = 200 - @size * 2
|
47
52
|
|
53
|
+
### note: padding changed in final FIPS PUB 202 standard
|
54
|
+
## from "\x01" to "\x06"
|
55
|
+
padding = "\x06"
|
56
|
+
|
48
57
|
buffer = @buffer
|
49
|
-
buffer <<
|
58
|
+
buffer << padding << "\0" * (width - buffer.size % width)
|
50
59
|
buffer[-1] = (buffer[-1].ord | 0x80).chr
|
51
60
|
|
52
|
-
0.step buffer.size - 1, width do |j|
|
53
|
-
quads = buffer[j, width].unpack 'Q*'
|
61
|
+
0.step( buffer.size - 1, width ) do |j|
|
62
|
+
quads = buffer[j, width].unpack( 'Q*' )
|
54
63
|
(width / 8).times do |i|
|
55
64
|
s[i] ^= quads[i]
|
56
65
|
end
|
57
66
|
|
58
|
-
|
67
|
+
_keccak( s )
|
59
68
|
end
|
60
69
|
|
61
70
|
s.pack('Q*')[0, @size]
|
62
71
|
end
|
63
72
|
|
64
|
-
|
65
|
-
def
|
66
|
-
24.times.each_with_object [] do |round, a|
|
73
|
+
private
|
74
|
+
def _keccak( s )
|
75
|
+
24.times.each_with_object( [] ) do |round, a|
|
67
76
|
# Theta
|
68
77
|
5.times do |i|
|
69
78
|
a[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]
|
70
79
|
end
|
71
80
|
|
72
81
|
5.times do |i|
|
73
|
-
t = a[(i + 4) % 5] ^
|
74
|
-
0.step 24, 5 do |j|
|
82
|
+
t = a[(i + 4) % 5] ^ _rotate(a[(i + 1) % 5], 1)
|
83
|
+
0.step( 24, 5 ) do |j|
|
75
84
|
s[j + i] ^= t
|
76
85
|
end
|
77
86
|
end
|
@@ -81,12 +90,12 @@ module Digest
|
|
81
90
|
24.times do |i|
|
82
91
|
j = PILN[i]
|
83
92
|
a[0] = s[j]
|
84
|
-
s[j] =
|
93
|
+
s[j] = _rotate( t, ROTC[i] )
|
85
94
|
t = a[0]
|
86
95
|
end
|
87
96
|
|
88
97
|
# Chi
|
89
|
-
0.step 24, 5 do |j|
|
98
|
+
0.step( 24, 5 ) do |j|
|
90
99
|
5.times do |i|
|
91
100
|
a[i] = s[j + i]
|
92
101
|
end
|
@@ -101,8 +110,9 @@ module Digest
|
|
101
110
|
end
|
102
111
|
end
|
103
112
|
|
104
|
-
def
|
113
|
+
def _rotate( x, y )
|
105
114
|
(x << y | x >> 64 - y) & (1 << 64) - 1
|
106
115
|
end
|
107
|
-
|
108
|
-
end
|
116
|
+
|
117
|
+
end # class SHA3Lite
|
118
|
+
end # module Digest
|
data/lib/ethlite/abi/type.rb
CHANGED
@@ -2,17 +2,16 @@ module Ethlite
|
|
2
2
|
module Abi
|
3
3
|
class Type
|
4
4
|
|
5
|
-
class ParseError < StandardError;
|
6
|
-
|
5
|
+
class ParseError < StandardError; end
|
6
|
+
|
7
7
|
|
8
|
-
class <<self
|
9
8
|
##
|
10
9
|
# Crazy regexp to seperate out base type component (eg. uint), size (eg.
|
11
10
|
# 256, 128x128, nil), array component (eg. [], [45], nil)
|
12
11
|
#
|
13
|
-
def parse(type)
|
12
|
+
def self.parse( type )
|
14
13
|
|
15
|
-
return parse('uint256')
|
14
|
+
return parse('uint256') if type=='trcToken'
|
16
15
|
|
17
16
|
if type =~ /^\((.*)\)((\[[0-9]*\])*)/
|
18
17
|
return Tuple.parse $1, $2.scan(/\[[0-9]*\]/)
|
@@ -56,10 +55,10 @@ module Ethlite
|
|
56
55
|
new(base, sub, dims.map {|x| x[1...-1].to_i})
|
57
56
|
end
|
58
57
|
|
59
|
-
def size_type
|
58
|
+
def self.size_type
|
60
59
|
@size_type ||= new('uint', 256, [])
|
61
60
|
end
|
62
|
-
|
61
|
+
|
63
62
|
|
64
63
|
attr :base, :sub, :dims
|
65
64
|
|
@@ -69,7 +68,7 @@ module Ethlite
|
|
69
68
|
# @param dims [Array[Integer]] dimensions of array type, e.g. [1,2,0]
|
70
69
|
# for uint256[1][2][], [] for non-array type
|
71
70
|
#
|
72
|
-
def initialize(base, sub, dims)
|
71
|
+
def initialize( base, sub, dims )
|
73
72
|
@base = base
|
74
73
|
@sub = sub
|
75
74
|
@dims = dims
|
@@ -123,7 +122,7 @@ module Ethlite
|
|
123
122
|
|
124
123
|
class Tuple < Type
|
125
124
|
|
126
|
-
def self.parse types, dims
|
125
|
+
def self.parse( types, dims )
|
127
126
|
|
128
127
|
depth = 0
|
129
128
|
collected = []
|
@@ -156,7 +155,8 @@ module Ethlite
|
|
156
155
|
end
|
157
156
|
|
158
157
|
attr_reader :types, :parsed_types
|
159
|
-
|
158
|
+
|
159
|
+
def initialize( types, dims )
|
160
160
|
super('tuple', '', dims)
|
161
161
|
@types = types
|
162
162
|
@parsed_types = types.map{|t| Type.parse t}
|
@@ -188,14 +188,11 @@ module Ethlite
|
|
188
188
|
subtype.dynamic? ? nil : dims.last * subtype.size
|
189
189
|
end
|
190
190
|
end
|
191
|
-
|
192
|
-
|
193
191
|
end
|
194
192
|
|
195
193
|
def subtype
|
196
194
|
@subtype ||= Tuple.new(types, dims[0...-1])
|
197
195
|
end
|
198
|
-
|
199
196
|
end
|
200
197
|
|
201
198
|
|
data/lib/ethlite/abi/utils.rb
CHANGED
@@ -12,16 +12,16 @@ module Abi
|
|
12
12
|
def keccak256(x)
|
13
13
|
# Digest::SHA3.new(256).digest(x)
|
14
14
|
## Digest::Keccak.digest(x, 256)
|
15
|
-
Digest::
|
15
|
+
Digest::KeccakLite.new(256).digest( x )
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
## check where required / in use - what for?
|
18
|
+
## todo/check where required / in use - what for?
|
20
19
|
def keccak512(x)
|
21
20
|
# Digest::SHA3.new(512).digest(x)
|
22
|
-
Digest::Keccak.digest(x, 512)
|
21
|
+
# Digest::Keccak.digest(x, 512)
|
22
|
+
Digest::KeccakLite.new(512).digest( x )
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
|
26
26
|
def keccak256_rlp(x)
|
27
27
|
keccak256 RLP.encode(x)
|
@@ -56,44 +56,50 @@ module Abi
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def to_signed(i)
|
59
|
-
i >
|
59
|
+
i > INT_MAX ? (i-TT256) : i
|
60
60
|
end
|
61
61
|
|
62
|
-
def base58_check_to_bytes(s)
|
63
|
-
leadingzbytes = s.match(/\A1*/)[0]
|
64
|
-
data = Constant::BYTE_ZERO * leadingzbytes.size + BaseConvert.convert(s, 58, 256)
|
65
|
-
|
66
|
-
raise ChecksumError, "double sha256 checksum doesn't match" unless double_sha256(data[0...-4])[0,4] == data[-4..-1]
|
67
|
-
data[1...-4]
|
68
|
-
end
|
69
|
-
|
70
|
-
def bytes_to_base58_check(bytes, magicbyte=0)
|
71
|
-
bs = "#{magicbyte.chr}#{bytes}"
|
72
|
-
leadingzbytes = bs.match(/\A#{Constant::BYTE_ZERO}*/)[0]
|
73
|
-
checksum = double_sha256(bs)[0,4]
|
74
|
-
'1'*leadingzbytes.size + BaseConvert.convert("#{bs}#{checksum}", 256, 58)
|
75
|
-
end
|
76
62
|
|
77
63
|
def ceil32(x)
|
78
64
|
x % 32 == 0 ? x : (x + 32 - x%32)
|
79
65
|
end
|
80
66
|
|
81
67
|
def encode_hex(b)
|
82
|
-
|
68
|
+
raise TypeError, "Value must be an instance of String" unless b.instance_of?(String)
|
69
|
+
b.unpack("H*").first
|
83
70
|
end
|
84
71
|
|
85
|
-
def decode_hex(
|
86
|
-
|
72
|
+
def decode_hex(str)
|
73
|
+
raise TypeError, "Value must be an instance of string" unless sstr.instance_of?(String)
|
74
|
+
raise TypeError, 'Non-hexadecimal digit found' unless str =~ /\A[0-9a-fA-F]*\z/
|
75
|
+
[str].pack("H*")
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
def int_to_big_endian(n)
|
82
|
+
RLP::Sedes.big_endian_int.serialize n
|
87
83
|
end
|
88
84
|
|
89
85
|
def big_endian_to_int(s)
|
90
86
|
RLP::Sedes.big_endian_int.deserialize s.sub(/\A(\x00)+/, '')
|
91
87
|
end
|
92
88
|
|
93
|
-
|
94
|
-
|
89
|
+
|
90
|
+
def encode_int(n)
|
91
|
+
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
92
|
+
int_to_big_endian n
|
95
93
|
end
|
96
94
|
|
95
|
+
def decode_int(v)
|
96
|
+
raise ArgumentError, "No leading zero bytes allowed for integers" if v.size > 0 && (v[0] == BYTE_ZERO || v[0] == 0)
|
97
|
+
big_endian_to_int v
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
97
103
|
def lpad(x, symbol, l)
|
98
104
|
return x if x.size >= l
|
99
105
|
symbol * (l - x.size) + x
|
@@ -124,15 +130,6 @@ module Abi
|
|
124
130
|
zpad_int x, 20
|
125
131
|
end
|
126
132
|
|
127
|
-
def encode_int(n)
|
128
|
-
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
129
|
-
int_to_big_endian n
|
130
|
-
end
|
131
|
-
|
132
|
-
def decode_int(v)
|
133
|
-
raise ArgumentError, "No leading zero bytes allowed for integers" if v.size > 0 && (v[0] == Constant::BYTE_ZERO || v[0] == 0)
|
134
|
-
big_endian_to_int v
|
135
|
-
end
|
136
133
|
|
137
134
|
def bytearray_to_int(arr)
|
138
135
|
o = 0
|
@@ -148,6 +145,7 @@ module Abi
|
|
148
145
|
bytes.unpack('C*')
|
149
146
|
end
|
150
147
|
|
148
|
+
|
151
149
|
def coerce_to_int(x)
|
152
150
|
if x.is_a?(Numeric)
|
153
151
|
x
|
@@ -178,19 +176,6 @@ module Abi
|
|
178
176
|
end
|
179
177
|
end
|
180
178
|
|
181
|
-
def normalize_address(x, allow_blank: false)
|
182
|
-
address = Address.new(x)
|
183
|
-
raise ValueError, "address is blank" if !allow_blank && address.blank?
|
184
|
-
address.to_bytes
|
185
|
-
end
|
186
|
-
|
187
|
-
def mk_contract_address(sender, nonce)
|
188
|
-
keccak256_rlp([normalize_address(sender), nonce])[12..-1]
|
189
|
-
end
|
190
|
-
|
191
|
-
def mk_metropolis_contract_address(sender, initcode)
|
192
|
-
keccak256(normalize_address(sender) + initcode)[12..-1]
|
193
|
-
end
|
194
179
|
|
195
180
|
|
196
181
|
def parse_int_or_hex(s)
|
@@ -203,6 +188,24 @@ module Abi
|
|
203
188
|
end
|
204
189
|
end
|
205
190
|
|
191
|
+
|
192
|
+
=begin
|
193
|
+
## add? moved over from the old module Utility
|
194
|
+
def hex( num )
|
195
|
+
'0x' + num.to_s(16)
|
196
|
+
end
|
197
|
+
|
198
|
+
def from_hex( h )
|
199
|
+
h.nil? ? 0 : (h.kind_of?(String) ? h.to_i(16) : h)
|
200
|
+
end
|
201
|
+
=end
|
202
|
+
|
203
|
+
|
204
|
+
def remove_0x_head( s )
|
205
|
+
return s if !s || s.length<2
|
206
|
+
s[0,2] == '0x' ? s[2..-1] : s
|
207
|
+
end
|
208
|
+
|
206
209
|
def normalize_hex_without_prefix(s)
|
207
210
|
if s[0,2] == '0x'
|
208
211
|
(s.size % 2 == 1 ? '0' : '') + s[2..-1]
|
@@ -211,6 +214,7 @@ module Abi
|
|
211
214
|
end
|
212
215
|
end
|
213
216
|
|
217
|
+
|
214
218
|
def function_signature method_name, arg_types
|
215
219
|
"#{method_name}(#{arg_types.join(',')})"
|
216
220
|
end
|
@@ -218,8 +222,6 @@ module Abi
|
|
218
222
|
def signature_hash signature, length=64
|
219
223
|
encode_hex(keccak256(signature))[0...length]
|
220
224
|
end
|
221
|
-
|
222
|
-
|
223
225
|
end
|
224
226
|
|
225
227
|
end # module Abi
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module Ethlite
|
3
|
+
module Constant
|
4
|
+
|
5
|
+
|
6
|
+
## todo/check - use encoding -ascii-8bit for source file or ? - why? why not?
|
7
|
+
## use #b/.b to ensure binary encoding? - why? why not?
|
8
|
+
|
9
|
+
## todo/check: use auto-freeze string literals magic comment - why? why not?
|
10
|
+
BYTE_EMPTY = "".b.freeze
|
11
|
+
BYTE_ZERO = "\x00".b.freeze
|
12
|
+
BYTE_ONE = "\x01".b.freeze
|
13
|
+
|
14
|
+
|
15
|
+
TT32 = 2**32
|
16
|
+
TT40 = 2**40
|
17
|
+
TT160 = 2**160
|
18
|
+
TT256 = 2**256
|
19
|
+
TT64M1 = 2**64 - 1
|
20
|
+
|
21
|
+
UINT_MAX = 2**256 - 1
|
22
|
+
UINT_MIN = 0
|
23
|
+
INT_MAX = 2**255 - 1
|
24
|
+
INT_MIN = -2**255
|
25
|
+
|
26
|
+
HASH_ZERO = ("\x00"*32).b.freeze
|
27
|
+
|
28
|
+
|
29
|
+
PUBKEY_ZERO = ("\x00"*32).b.freeze
|
30
|
+
PRIVKEY_ZERO = ("\x00"*32).b.freeze
|
31
|
+
|
32
|
+
PRIVKEY_ZERO_HEX = ('0'*64).freeze
|
33
|
+
|
34
|
+
CONTRACT_CODE_SIZE_LIMIT = 0x6000
|
35
|
+
|
36
|
+
|
37
|
+
end # module Constant
|
38
|
+
end # module Ethlite
|
data/lib/ethlite/contract.rb
CHANGED
@@ -1,38 +1,54 @@
|
|
1
1
|
module Ethlite
|
2
2
|
class ContractMethod
|
3
|
-
include Utility
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
def self.parse_abi( abi )
|
5
|
+
## convenience helper - auto-convert to json if string passed in
|
6
|
+
abi = JSON.parse( abi ) if abi.is_a?( String )
|
7
|
+
|
8
|
+
name = abi['name']
|
9
|
+
constant = !!abi['constant'] || abi['stateMutability']=='view'
|
10
|
+
input_types = abi['inputs'] ? abi['inputs'].map{|a| _parse_component_type( a ) } : []
|
11
|
+
output_types = abi['outputs'] ? abi['outputs'].map{|a| _parse_component_type( a ) } : []
|
12
|
+
type = abi['type'] || 'function'
|
13
|
+
|
14
|
+
new( name, inputs: input_types,
|
15
|
+
outputs: output_types,
|
16
|
+
constant: constant,
|
17
|
+
type: type )
|
18
|
+
end
|
19
|
+
|
20
|
+
def self._parse_component_type( argument )
|
21
|
+
if argument['type'] =~ /^tuple((\[[0-9]*\])*)/
|
22
|
+
argument['components'] ? "(#{argument['components'].collect{ |c| _parse_component_type( c ) }.join(',')})#{$1}"
|
23
|
+
: "()#{$1}"
|
24
|
+
else
|
25
|
+
argument['type']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
attr_reader :signature,
|
7
33
|
:name,
|
8
34
|
:signature_hash,
|
9
35
|
:input_types,
|
10
36
|
:output_types,
|
11
37
|
:constant
|
12
38
|
|
13
|
-
def initialize(
|
14
|
-
|
15
|
-
|
39
|
+
def initialize( name, inputs:,
|
40
|
+
outputs: [],
|
41
|
+
constant: true,
|
42
|
+
type: 'function' )
|
43
|
+
@name = name
|
44
|
+
@constant = constant
|
45
|
+
@input_types = inputs
|
46
|
+
@output_types = outputs
|
16
47
|
|
17
|
-
@abi = abi
|
18
|
-
@name = abi['name']
|
19
|
-
@constant = !!abi['constant'] || abi['stateMutability']=='view'
|
20
|
-
@input_types = abi['inputs'] ? abi['inputs'].map{|a| parse_component_type a } : []
|
21
|
-
@output_types = abi['outputs'] ? abi['outputs'].map{|a| parse_component_type a } : nil
|
22
48
|
@signature = Abi::Utils.function_signature( @name, @input_types )
|
23
|
-
@signature_hash = Abi::Utils.signature_hash( @signature,
|
49
|
+
@signature_hash = Abi::Utils.signature_hash( @signature, type=='event' ? 64 : 8)
|
24
50
|
end
|
25
51
|
|
26
|
-
def parse_component_type( argument )
|
27
|
-
if argument['type'] =~ /^tuple((\[[0-9]*\])*)/
|
28
|
-
argument['components'] ? "(#{argument['components'].collect{|c| parse_component_type( c ) }.join(',')})#{$1}"
|
29
|
-
: "()#{$1}"
|
30
|
-
else
|
31
|
-
argument['type']
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
52
|
def do_call( rpc, contract_address, args )
|
37
53
|
data = '0x' + @signature_hash + Abi::Utils.encode_hex(
|
38
54
|
Abi::AbiCoder.encode_abi(@input_types, args) )
|
@@ -47,7 +63,7 @@
|
|
47
63
|
puts "response:"
|
48
64
|
pp response
|
49
65
|
|
50
|
-
string_data = [remove_0x_head(response)].pack('H*')
|
66
|
+
string_data = [Abi::Utils.remove_0x_head(response)].pack('H*')
|
51
67
|
return nil if string_data.empty?
|
52
68
|
|
53
69
|
result = Abi::AbiCoder.decode_abi( @output_types, string_data )
|
data/lib/ethlite/version.rb
CHANGED
data/lib/ethlite.rb
CHANGED
@@ -10,28 +10,27 @@ require 'openssl'
|
|
10
10
|
require 'digest'
|
11
11
|
|
12
12
|
## 3rd party gems
|
13
|
-
require 'rlp'
|
13
|
+
require 'rlp-lite'
|
14
14
|
|
15
15
|
## bundled require 'digest/keccak' ## gem keccak - see https://rubygems.org/gems/keccak
|
16
|
-
|
16
|
+
require_relative 'digest/keccak'
|
17
|
+
require_relative 'digest/sha3'
|
17
18
|
|
19
|
+
require_relative 'jsonrpc/jsonrpc'
|
18
20
|
|
19
21
|
|
20
22
|
|
21
23
|
## our own code
|
22
24
|
require_relative 'ethlite/version' # note: let version always go first
|
23
25
|
|
26
|
+
require_relative 'ethlite/constant'
|
27
|
+
|
24
28
|
|
25
29
|
require_relative 'ethlite/abi/type'
|
26
|
-
require_relative 'ethlite/abi/constant'
|
27
|
-
require_relative 'ethlite/abi/exceptions'
|
28
30
|
require_relative 'ethlite/abi/utils'
|
29
31
|
require_relative 'ethlite/abi/abi_coder'
|
30
32
|
|
31
33
|
|
32
|
-
require_relative 'ethlite/rpc'
|
33
|
-
|
34
|
-
require_relative 'ethlite/utility'
|
35
34
|
require_relative 'ethlite/contract'
|
36
35
|
|
37
36
|
|
@@ -39,8 +38,14 @@ require_relative 'ethlite/contract'
|
|
39
38
|
|
40
39
|
module Ethlite
|
41
40
|
class Configuration
|
42
|
-
def rpc() @rpc ||
|
43
|
-
def rpc=(value)
|
41
|
+
def rpc() @rpc || JsonRpc.new( ENV['INFURA_URI'] ); end
|
42
|
+
def rpc=(value)
|
43
|
+
@rpc = if value.is_a?( String )
|
44
|
+
JsonRpc.new( value ) ## auto-wrap in (built-in/simple) jsonrpc client/serverproxy
|
45
|
+
else
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
44
49
|
end # class Configuration
|
45
50
|
|
46
51
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
###
|
3
|
+
# simple JsonRpc client
|
4
|
+
#
|
5
|
+
# see https://www.jsonrpc.org/specification
|
6
|
+
# https://en.wikipedia.org/wiki/JSON-RPC
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
class JsonRpc
|
11
|
+
def initialize( uri )
|
12
|
+
@uri = uri ## assume uri always as string for now
|
13
|
+
@request_id = 1
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def request( method, params=[] )
|
18
|
+
|
19
|
+
data = { jsonrpc: '2.0',
|
20
|
+
method: method,
|
21
|
+
params: params,
|
22
|
+
id: @request_id }
|
23
|
+
|
24
|
+
@request_id += 1
|
25
|
+
|
26
|
+
puts "json POST payload:"
|
27
|
+
puts data.to_json
|
28
|
+
|
29
|
+
response = Webclient.post( @uri, json: data )
|
30
|
+
|
31
|
+
|
32
|
+
if response.status.nok?
|
33
|
+
raise "Error code #{response.status.code} on request #{@uri} #{data}"
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
body = JSON.parse( response.body, max_nesting: 1500 )
|
38
|
+
|
39
|
+
if body['result']
|
40
|
+
body['result']
|
41
|
+
elsif body['error']
|
42
|
+
raise "Error #{@uri} #{body['error']} on request #{@uri} #{data}"
|
43
|
+
else
|
44
|
+
raise "No response on request #{@uri} #{data}"
|
45
|
+
end
|
46
|
+
end # method request
|
47
|
+
end # class JsonRpc
|
48
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ethlite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
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-11-
|
11
|
+
date: 2022-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cocos
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: rlp
|
28
|
+
name: rlp-lite
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -86,18 +86,16 @@ files:
|
|
86
86
|
- Manifest.txt
|
87
87
|
- README.md
|
88
88
|
- Rakefile
|
89
|
-
- lib/digest/
|
89
|
+
- lib/digest/keccak.rb
|
90
90
|
- lib/digest/sha3.rb
|
91
91
|
- lib/ethlite.rb
|
92
92
|
- lib/ethlite/abi/abi_coder.rb
|
93
|
-
- lib/ethlite/abi/constant.rb
|
94
|
-
- lib/ethlite/abi/exceptions.rb
|
95
93
|
- lib/ethlite/abi/type.rb
|
96
94
|
- lib/ethlite/abi/utils.rb
|
95
|
+
- lib/ethlite/constant.rb
|
97
96
|
- lib/ethlite/contract.rb
|
98
|
-
- lib/ethlite/rpc.rb
|
99
|
-
- lib/ethlite/utility.rb
|
100
97
|
- lib/ethlite/version.rb
|
98
|
+
- lib/jsonrpc/jsonrpc.rb
|
101
99
|
homepage: https://github.com/pixelartexchange/artbase
|
102
100
|
licenses:
|
103
101
|
- Public Domain
|
data/lib/ethlite/abi/constant.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
|
2
|
-
module Ethlite
|
3
|
-
module Abi
|
4
|
-
module Constant
|
5
|
-
|
6
|
-
BYTE_EMPTY = "".freeze
|
7
|
-
BYTE_ZERO = "\x00".freeze
|
8
|
-
BYTE_ONE = "\x01".freeze
|
9
|
-
|
10
|
-
TT32 = 2**32
|
11
|
-
TT40 = 2**40
|
12
|
-
TT160 = 2**160
|
13
|
-
TT256 = 2**256
|
14
|
-
TT64M1 = 2**64 - 1
|
15
|
-
|
16
|
-
UINT_MAX = 2**256 - 1
|
17
|
-
UINT_MIN = 0
|
18
|
-
INT_MAX = 2**255 - 1
|
19
|
-
INT_MIN = -2**255
|
20
|
-
|
21
|
-
HASH_ZERO = ("\x00"*32).freeze
|
22
|
-
|
23
|
-
PUBKEY_ZERO = ("\x00"*32).freeze
|
24
|
-
PRIVKEY_ZERO = ("\x00"*32).freeze
|
25
|
-
PRIVKEY_ZERO_HEX = ('0'*64).freeze
|
26
|
-
|
27
|
-
CONTRACT_CODE_SIZE_LIMIT = 0x6000
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
end # module Abi
|
32
|
-
end # module Ethlite
|
@@ -1,29 +0,0 @@
|
|
1
|
-
|
2
|
-
module Ethlite
|
3
|
-
module Abi
|
4
|
-
|
5
|
-
class DeprecatedError < StandardError; end
|
6
|
-
class ChecksumError < StandardError; end
|
7
|
-
class FormatError < StandardError; end
|
8
|
-
class ValidationError < StandardError; end
|
9
|
-
class ValueError < StandardError; end
|
10
|
-
class AssertError < StandardError; end
|
11
|
-
|
12
|
-
class UnknownParentError < StandardError; end
|
13
|
-
class InvalidBlock < ValidationError; end
|
14
|
-
class InvalidUncles < ValidationError; end
|
15
|
-
|
16
|
-
class InvalidTransaction < ValidationError; end
|
17
|
-
class UnsignedTransactionError < InvalidTransaction; end
|
18
|
-
class InvalidNonce < InvalidTransaction; end
|
19
|
-
class InsufficientStartGas < InvalidTransaction; end
|
20
|
-
class InsufficientBalance < InvalidTransaction; end
|
21
|
-
class BlockGasLimitReached < InvalidTransaction; end
|
22
|
-
|
23
|
-
class InvalidSPVProof < ValidationError; end
|
24
|
-
|
25
|
-
class ContractCreationFailed < StandardError; end
|
26
|
-
class TransactionFailed < StandardError; end
|
27
|
-
|
28
|
-
end # module Abi
|
29
|
-
end # module Ethlite
|
data/lib/ethlite/rpc.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
module Ethlite
|
2
|
-
|
3
|
-
class Rpc
|
4
|
-
def initialize( uri )
|
5
|
-
@client_id = Random.rand( 10000000 )
|
6
|
-
|
7
|
-
@uri = URI.parse( uri )
|
8
|
-
end
|
9
|
-
|
10
|
-
def request( method, params=[] )
|
11
|
-
opts = {}
|
12
|
-
if @uri.instance_of?( URI::HTTPS )
|
13
|
-
opts[:use_ssl] = true
|
14
|
-
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
|
15
|
-
end
|
16
|
-
|
17
|
-
Net::HTTP.start( @uri.host, @uri.port, **opts ) do |http|
|
18
|
-
headers = {"Content-Type" => "application/json"}
|
19
|
-
request = Net::HTTP::Post.new( @uri.request_uri, headers )
|
20
|
-
|
21
|
-
json = { jsonrpc: '2.0',
|
22
|
-
method: method,
|
23
|
-
params: params,
|
24
|
-
id: @client_id }.to_json
|
25
|
-
|
26
|
-
puts "json POST payload:"
|
27
|
-
puts json
|
28
|
-
|
29
|
-
request.body = json
|
30
|
-
response = http.request( request )
|
31
|
-
|
32
|
-
raise "Error code #{response.code} on request #{@uri.to_s} #{request.body}" unless response.kind_of?( Net::HTTPOK )
|
33
|
-
|
34
|
-
body = JSON.parse(response.body, max_nesting: 1500)
|
35
|
-
|
36
|
-
if body['result']
|
37
|
-
body['result']
|
38
|
-
elsif body['error']
|
39
|
-
raise "Error #{@uri.to_s} #{body['error']} on request #{@uri.to_s} #{request.body}"
|
40
|
-
else
|
41
|
-
raise "No response on request #{@uri.to_s} #{request.body}"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end # class Rpc
|
46
|
-
|
47
|
-
end # module Ethlite
|
48
|
-
|
data/lib/ethlite/utility.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Ethlite
|
2
|
-
|
3
|
-
module Utility
|
4
|
-
|
5
|
-
def hex( num )
|
6
|
-
'0x' + num.to_s(16)
|
7
|
-
end
|
8
|
-
|
9
|
-
def from_hex( h )
|
10
|
-
h.nil? ? 0 : (h.kind_of?(String) ? h.to_i(16) : h)
|
11
|
-
end
|
12
|
-
|
13
|
-
def remove_0x_head( s )
|
14
|
-
return s if !s || s.length<2
|
15
|
-
s[0,2] == '0x' ? s[2..-1] : s
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
def wei_to_ether( wei )
|
20
|
-
1.0 * wei / 10**18
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end # module Ethlite
|