@@ -2,6 +2,202 @@ 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
+ module UIntN;
150
+ test
151
+ (A : Type)
152
+ {{Show A}}
153
+ {{Eq A}}
154
+ {{FixedSize A}}
155
+ {{ToBytes A}}
156
+ {{FromBytes A}}
157
+ {{FromNatural A}}
158
+ (name : String)
159
+ : TestSuite :=
160
+ let
161
+ size := byteSize {A};
162
+ numBits := 8 * size;
163
+ in TestSuite.group
164
+ name
165
+ [
166
+ testCase
167
+ "encode(0)"
168
+ (assertEqual
169
+ "not equal"
170
+ (toByteArray (fromNat {A} 0))
171
+ (ByteArray.fromList (replicate evmAlign 0)));
172
+ testCase
173
+ ("encode(2 ^ " ++str show numBits ++str ")")
174
+ (assertEqual
175
+ "not equal"
176
+ (toByteArray (fromNat {A} (pow 256 size)))
177
+ (ByteArray.fromList (replicate evmAlign 0)));
178
+ testCase
179
+ ("encode(2 ^ " ++str show numBits ++str " - 1)")
180
+ (assertEqual
181
+ "not equal"
182
+ (toByteArray {A} (fromNat (sub (pow 256 size) 1)))
183
+ (ByteArray.fromList
184
+ (replicate (pad32 size) 0 ++ replicate size 255)));
185
+ testCase "decode(encode(0))" (assertRoundTrip {A} "0" 0);
186
+ testCase
187
+ ("decode(encode(2 ^ " ++str show numBits ++str " - 1)")
188
+ (assertRoundTrip {A} "0" (fromNat (sub (pow 256 size) 1)));
189
+ ];
190
+ end;
191
+
192
+ module UInt32;
193
+ test : TestSuite := UIntN.test UInt32 "UInt32";
194
+ end;
195
+
196
+ module UInt256;
197
+ test : TestSuite := UIntN.test UInt256 "UInt256";
198
+ end;
5
199
6
200
test : TestSuite :=
7
- testSuite "TestPass" [testCase "1 == 1" (assertEqual "1 /= 1" 1 1)];
201
+ TestSuite.group
202
+ "Abi Encoding"
203
+ [TupleUsageExample.test; UInt32.test; UInt256.test];
0 commit comments