@@ -2,6 +2,205 @@ module Test.Ethereum.Abi;
2
2
3
3
import Stdlib.Prelude open;
4
4
import Test.JuvixUnit open;
5
+ import Anoma.Primitives.FixedSize open;
6
+ import Ethereum.Abi open;
7
+ import Applib open;
8
+ import Stdlib.Debug.Fail using {failwith} open;
9
+
10
+ {-- In order to define ;ToBytes; and ;FromBytes; instances for a
11
+ tuple, we need to follow some mechanical steps.
12
+ This module provides a concrete example. See the comments in the instances
13
+ declarations below.
14
+ --}
15
+ module TupleUsageExample;
16
+ type Example : Type :=
17
+ mk@{
18
+ word : UInt32;
19
+ bytearray : ByteArray;
20
+ int : Int256;
21
+ int32 : Int32;
22
+ };
23
+
24
+ deriving instance
25
+ Eq-Example : Eq Example;
26
+
27
+ {-- To define the ;ToBytes; instance we must create an ;EvmTuple; with the fields
28
+ of the record given in order. Then we use the ;ToBytes; instance of the ;EvmTuple;
29
+ as shown below.
30
+ Note that each of the fields must be an instance of ;ToBytes; and ;HasEvmSize;.
31
+ --}
32
+ instance
33
+ ToBytes-Example : ToBytes Example :=
34
+ ToBytes.mk@{
35
+ toBytes (e : Example) : BytesBuilder Unit :=
36
+ do {
37
+ let
38
+ tup : EvmTuple :=
39
+ Example.word e
40
+ ::: Example.bytearray e
41
+ ::: Example.int e
42
+ ::: Example.int32 e
43
+ ::: EvmTuple.nil;
44
+ in
45
+ ToBytes.toBytes tup;
46
+ };
47
+ };
48
+
49
+ {-- To define the ;FromBytes; instance we must follow these steps:
50
+ 1. call `l <- tupleSplit sizes`, where `sizes` is a list of the ;EvmSize; of
51
+ the type of each field.
52
+ 2. case on `l`. `l` will exactly `n` elements, where `n` is the number
53
+ of fields with a type `T` such that `evmSize T` is ;EvmSize.static;
54
+ 3. For each element `s` of `l`, we call:
55
+ `fieldName <- BytesConsumer.local FromBytes.fromBytes s`
56
+ 4. Then, for each field `f` with a dynamic ;EvmSize;, we call:
57
+ `f <- FromBytes.fromBytes`
58
+
59
+ For a concrete example, see the code below:
60
+ --}
61
+ instance
62
+ FromBytes-Example : FromBytes Example :=
63
+ FromBytes.mk@{
64
+ fromBytes : BytesConsumer Example :=
65
+ do {
66
+ l <- tupleSplit
67
+ [evmSize UInt32; evmSize ByteArray; evmSize Int256; evmSize Int32];
68
+ case l of {
69
+ | [sword; sint; sint32] :=
70
+ do {
71
+ -- static
72
+ word <- BytesConsumer.local FromBytes.fromBytes sword;
73
+ int <- BytesConsumer.local FromBytes.fromBytes sint;
74
+ int32 <- BytesConsumer.local FromBytes.fromBytes sint32;
75
+ -- dynamic
76
+ bytearray <- FromBytes.fromBytes;
77
+ pure
78
+ Example.mk@{
79
+ word;
80
+ bytearray;
81
+ int;
82
+ int32;
83
+ };
84
+ }
85
+ | _ := failwith "FromBytes-Example wrong number of static arguments"
86
+ };
87
+ };
88
+ };
89
+
90
+ instance
91
+ Show-Example : Show Example :=
92
+ Show.mk@{
93
+ show (e : Example) : String :=
94
+ Show.show (Example.word e)
95
+ ++str ", "
96
+ ++str Show.show (Example.bytearray e)
97
+ ++str ", "
98
+ ++str Show.show (Example.int e)
99
+ ++str ", "
100
+ ++str Show.show (Example.int32 e);
101
+ };
102
+
103
+ test : TestSuite :=
104
+ let
105
+ n : List Byte := [3; 4; 5];
106
+ b : ByteArray := ByteArray.mk n;
107
+ i : Int256 := Int256.fromInt -88888888888888;
108
+ i' : Int32 := Int32.fromInt -2147483647;
109
+ example : Example :=
110
+ Example.mk@{
111
+ word := UInt32.fromNat (sub (pow 256 4) 1);
112
+ bytearray := b;
113
+ int := i;
114
+ int32 := i';
115
+ };
116
+ encodedExample : ByteArray := toByteArray example;
117
+ resDecodedExample : Result BytesConsumer.ConsumerError Example :=
118
+ fromByteArray {Example} encodedExample;
119
+ in testCase
120
+ "decode (encode Tuple) == Tuple"
121
+ case resDecodedExample of {
122
+ | error err := Assertion.fail "decoding failed"
123
+ | ok decodedExample := assertEqual "not equal" decodedExample example
124
+ };
125
+ end;
126
+
127
+ assertRoundTrip
128
+ {A}
129
+ {{Show A}}
130
+ {{Eq A}}
131
+ {{FromBytes A}}
132
+ {{ToBytes A}}
133
+ (name : String)
134
+ (a : A)
135
+ : Assertion :=
136
+ let
137
+ encoded : ByteArray := toByteArray a;
138
+ resDecoded : Result BytesConsumer.ConsumerError A := fromByteArray encoded;
139
+ in case resDecoded of
140
+ | error err :=
141
+ Assertion.fail
142
+ ("decoding " ++str name ++str " failed:\n" ++str show err)
143
+ | ok decoded :=
144
+ assertEqual
145
+ ("\n" ++str show a ++str "\nnot equal to:\n" ++str show decoded)
146
+ decoded
147
+ a;
148
+
149
+ -- testCase
150
+ -- ("decode (encode " ++str name ++str ") == " ++str name)
151
+
152
+ module UIntN;
153
+ test
154
+ (A : Type)
155
+ {{Show A}}
156
+ {{Eq A}}
157
+ {{FixedSize A}}
158
+ {{ToBytes A}}
159
+ {{FromBytes A}}
160
+ {{FromNatural A}}
161
+ (name : String)
162
+ : TestSuite :=
163
+ let
164
+ size := byteSize {A};
165
+ numBits := 8 * size;
166
+ in TestSuite.group
167
+ name
168
+ [
169
+ testCase
170
+ "encode(0)"
171
+ (assertEqual
172
+ "not equal"
173
+ (toByteArray (fromNat {A} 0))
174
+ (ByteArray.fromList (replicate evmAlign 0)));
175
+ testCase
176
+ ("encode(2 ^ " ++str show numBits ++str ")")
177
+ (assertEqual
178
+ "not equal"
179
+ (toByteArray (fromNat {A} (pow 256 size)))
180
+ (ByteArray.fromList (replicate evmAlign 0)));
181
+ testCase
182
+ ("encode(2 ^ " ++str show numBits ++str " - 1)")
183
+ (assertEqual
184
+ "not equal"
185
+ (toByteArray {A} (fromNat (sub (pow 256 size) 1)))
186
+ (ByteArray.fromList
187
+ (replicate (pad32 size) 0 ++ replicate size 255)));
188
+ testCase "decode(encode(0))" (assertRoundTrip {A} "0" 0);
189
+ testCase
190
+ ("decode(encode(2 ^ " ++str show numBits ++str " - 1)")
191
+ (assertRoundTrip {A} "0" (fromNat (sub (pow 256 size) 1)));
192
+ ];
193
+ end;
194
+
195
+ module UInt32;
196
+ test : TestSuite := UIntN.test UInt32 "UInt32";
197
+ end;
198
+
199
+ module UInt256;
200
+ test : TestSuite := UIntN.test UInt256 "UInt256";
201
+ end;
5
202
6
203
test : TestSuite :=
7
- testSuite "TestPass" [testCase "1 == 1" (assertEqual "1 /= 1" 1 1)];
204
+ TestSuite.group
205
+ "Abi Encoding"
206
+ [TupleUsageExample.test; UInt32.test; UInt256.test];
0 commit comments