17
17
package com .hedera .services .bdd .suites .contract .precompile ;
18
18
19
19
import static com .hedera .services .bdd .junit .TestTags .SMART_CONTRACT ;
20
+ import static com .hedera .services .bdd .spec .HapiPropertySource .idAsHeadlongAddress ;
21
+ import static com .hedera .services .bdd .spec .HapiSpec .defaultHapiSpec ;
22
+ import static com .hedera .services .bdd .spec .assertions .TransactionRecordAsserts .recordWith ;
23
+ import static com .hedera .services .bdd .spec .keys .KeyShape .CONTRACT ;
24
+ import static com .hedera .services .bdd .spec .keys .KeyShape .ED25519 ;
25
+ import static com .hedera .services .bdd .spec .keys .KeyShape .sigs ;
26
+ import static com .hedera .services .bdd .spec .keys .SigControl .ON ;
27
+ import static com .hedera .services .bdd .spec .queries .QueryVerbs .getAccountInfo ;
28
+ import static com .hedera .services .bdd .spec .queries .crypto .ExpectedTokenRel .relationshipWith ;
29
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .contractCall ;
30
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .contractCreate ;
31
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .cryptoCreate ;
32
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .cryptoUpdate ;
33
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .tokenAssociate ;
34
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .tokenCreate ;
35
+ import static com .hedera .services .bdd .spec .transactions .TxnVerbs .uploadInitCode ;
36
+ import static com .hedera .services .bdd .spec .utilops .CustomSpecAssert .allRunFor ;
37
+ import static com .hedera .services .bdd .spec .utilops .UtilVerbs .childRecordsCheck ;
38
+ import static com .hedera .services .bdd .spec .utilops .UtilVerbs .newKeyNamed ;
39
+ import static com .hedera .services .bdd .spec .utilops .UtilVerbs .withOpContext ;
40
+ import static com .hederahashgraph .api .proto .java .ResponseCodeEnum .CONTRACT_REVERT_EXECUTED ;
41
+ import static com .hederahashgraph .api .proto .java .ResponseCodeEnum .INVALID_ACCOUNT_ID ;
42
+ import static com .hederahashgraph .api .proto .java .ResponseCodeEnum .SUCCESS ;
43
+ import static com .hederahashgraph .api .proto .java .ResponseCodeEnum .TOKEN_NOT_ASSOCIATED_TO_ACCOUNT ;
20
44
45
+ import com .esaulpaugh .headlong .abi .Address ;
46
+ import com .hedera .services .bdd .junit .HapiTest ;
21
47
import com .hedera .services .bdd .junit .HapiTestSuite ;
22
48
import com .hedera .services .bdd .spec .HapiSpec ;
49
+ import com .hedera .services .bdd .spec .keys .KeyShape ;
23
50
import com .hedera .services .bdd .suites .HapiSuite ;
51
+ import com .hederahashgraph .api .proto .java .TokenType ;
24
52
import java .util .List ;
53
+ import java .util .concurrent .atomic .AtomicReference ;
25
54
import org .apache .logging .log4j .LogManager ;
26
55
import org .apache .logging .log4j .Logger ;
27
56
import org .junit .jupiter .api .Tag ;
31
60
public class DissociatePrecompileSuite extends HapiSuite {
32
61
33
62
private static final Logger log = LogManager .getLogger (DissociatePrecompileSuite .class );
63
+ private static final String NEGATIVE_DISSOCIATIONS_CONTRACT = "NegativeDissociationsContract" ;
64
+ private static final long GAS_TO_OFFER = 4_000_000L ;
65
+ private static final String ACCOUNT = "anybody" ;
66
+ private static final String TOKEN = "Token" ;
67
+ private static final String TOKEN1 = "Token1" ;
68
+ private static final String CONTRACT_KEY = "ContractKey" ;
69
+ private static final KeyShape KEY_SHAPE = KeyShape .threshOf (1 , ED25519 , CONTRACT );
34
70
35
71
public static void main (String ... args ) {
36
72
new DissociatePrecompileSuite ().runSuiteAsync ();
@@ -43,7 +79,211 @@ public boolean canRunConcurrent() {
43
79
44
80
@ Override
45
81
public List <HapiSpec > getSpecsInSuite () {
46
- return List .of ();
82
+ return allOf (negativeSpecs ());
83
+ }
84
+
85
+ List <HapiSpec > negativeSpecs () {
86
+ return List .of (dissociateTokensNegativeScenarios (), dissociateTokenNegativeScenarios ());
87
+ }
88
+
89
+ @ HapiTest
90
+ final HapiSpec dissociateTokensNegativeScenarios () {
91
+ final AtomicReference <Address > tokenAddress1 = new AtomicReference <>();
92
+ final AtomicReference <Address > tokenAddress2 = new AtomicReference <>();
93
+ final AtomicReference <Address > accountAddress = new AtomicReference <>();
94
+ final var nonExistingAccount = "nonExistingAccount" ;
95
+ final var nonExistingTokenArray = "nonExistingTokenArray" ;
96
+ final var someNonExistingTokenArray = "someNonExistingTokenArray" ;
97
+ final var zeroAccountAddress = "zeroAccountAddress" ;
98
+ final var nullTokenArray = "nullTokens" ;
99
+ final var nonExistingTokensInArray = "nonExistingTokensInArray" ;
100
+ return defaultHapiSpec ("dissociateTokensNegativeScenarios" )
101
+ .given (
102
+ uploadInitCode (NEGATIVE_DISSOCIATIONS_CONTRACT ),
103
+ contractCreate (NEGATIVE_DISSOCIATIONS_CONTRACT ),
104
+ cryptoCreate (TOKEN_TREASURY ),
105
+ tokenCreate (TOKEN )
106
+ .tokenType (TokenType .FUNGIBLE_COMMON )
107
+ .initialSupply (50L )
108
+ .supplyKey (TOKEN_TREASURY )
109
+ .adminKey (TOKEN_TREASURY )
110
+ .treasury (TOKEN_TREASURY )
111
+ .exposingAddressTo (tokenAddress1 ::set ),
112
+ tokenCreate (TOKEN1 )
113
+ .tokenType (TokenType .FUNGIBLE_COMMON )
114
+ .initialSupply (50L )
115
+ .supplyKey (TOKEN_TREASURY )
116
+ .adminKey (TOKEN_TREASURY )
117
+ .treasury (TOKEN_TREASURY )
118
+ .exposingAddressTo (tokenAddress2 ::set ),
119
+ cryptoCreate (ACCOUNT ).exposingCreatedIdTo (id -> accountAddress .set (idAsHeadlongAddress (id ))),
120
+ tokenAssociate (ACCOUNT , List .of (TOKEN , TOKEN1 )))
121
+ .when (withOpContext ((spec , custom ) -> allRunFor (
122
+ spec ,
123
+ contractCall (
124
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
125
+ "dissociateTokensWithNonExistingAccountAddress" ,
126
+ (Object ) new Address [] {tokenAddress1 .get (), tokenAddress2 .get ()})
127
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
128
+ .gas (GAS_TO_OFFER )
129
+ .via (nonExistingAccount )
130
+ .logged (),
131
+ getAccountInfo (ACCOUNT ).hasToken (relationshipWith (TOKEN )),
132
+ getAccountInfo (ACCOUNT ).hasToken (relationshipWith (TOKEN1 )),
133
+ newKeyNamed (CONTRACT_KEY )
134
+ .shape (KEY_SHAPE .signedWith (sigs (ON , NEGATIVE_DISSOCIATIONS_CONTRACT ))),
135
+ cryptoUpdate (ACCOUNT ).key (CONTRACT_KEY ),
136
+ contractCall (
137
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
138
+ "dissociateTokensWithEmptyTokensArray" ,
139
+ accountAddress .get ())
140
+ .hasKnownStatus (SUCCESS )
141
+ .gas (GAS_TO_OFFER )
142
+ .signingWith (ACCOUNT )
143
+ .via (nonExistingTokenArray )
144
+ .logged (),
145
+ getAccountInfo (ACCOUNT ).hasToken (relationshipWith (TOKEN )),
146
+ getAccountInfo (ACCOUNT ).hasToken (relationshipWith (TOKEN1 )),
147
+ contractCall (NEGATIVE_DISSOCIATIONS_CONTRACT , "dissociateTokensWithNullAccount" , (Object )
148
+ new Address [] {tokenAddress1 .get (), tokenAddress2 .get ()})
149
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
150
+ .gas (GAS_TO_OFFER )
151
+ .via (zeroAccountAddress )
152
+ .logged (),
153
+ getAccountInfo (ACCOUNT ).hasToken (relationshipWith (TOKEN )),
154
+ getAccountInfo (ACCOUNT ).hasToken (relationshipWith (TOKEN1 )),
155
+ contractCall (
156
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
157
+ "dissociateTokensWithNullTokensArray" ,
158
+ accountAddress .get ())
159
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
160
+ .gas (GAS_TO_OFFER )
161
+ .signingWith (ACCOUNT )
162
+ .via (nullTokenArray )
163
+ .logged (),
164
+ contractCall (
165
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
166
+ "dissociateTokensWithNonExistingTokensArray" ,
167
+ accountAddress .get ())
168
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
169
+ .gas (GAS_TO_OFFER )
170
+ .signingWith (ACCOUNT )
171
+ .via (nonExistingTokensInArray )
172
+ .logged (),
173
+ contractCall (
174
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
175
+ "dissociateTokensWithTokensArrayWithSomeNonExistingAddresses" ,
176
+ accountAddress .get (),
177
+ new Address [] {tokenAddress1 .get (), tokenAddress2 .get ()})
178
+ .hasKnownStatus (SUCCESS )
179
+ .gas (GAS_TO_OFFER )
180
+ .signingWith (ACCOUNT )
181
+ .via (someNonExistingTokenArray )
182
+ .logged (),
183
+ getAccountInfo (ACCOUNT ).hasNoTokenRelationship (TOKEN ),
184
+ getAccountInfo (ACCOUNT ).hasNoTokenRelationship (TOKEN1 ))))
185
+ .then (
186
+ childRecordsCheck (
187
+ nonExistingAccount ,
188
+ CONTRACT_REVERT_EXECUTED ,
189
+ recordWith ().status (INVALID_ACCOUNT_ID )),
190
+ childRecordsCheck (
191
+ nonExistingTokenArray , SUCCESS , recordWith ().status (SUCCESS )),
192
+ childRecordsCheck (
193
+ zeroAccountAddress ,
194
+ CONTRACT_REVERT_EXECUTED ,
195
+ recordWith ().status (INVALID_ACCOUNT_ID )),
196
+ childRecordsCheck (
197
+ nullTokenArray ,
198
+ CONTRACT_REVERT_EXECUTED ,
199
+ recordWith ().status (TOKEN_NOT_ASSOCIATED_TO_ACCOUNT )),
200
+ childRecordsCheck (
201
+ nonExistingTokensInArray ,
202
+ CONTRACT_REVERT_EXECUTED ,
203
+ recordWith ().status (TOKEN_NOT_ASSOCIATED_TO_ACCOUNT )),
204
+ childRecordsCheck (
205
+ someNonExistingTokenArray , SUCCESS , recordWith ().status (SUCCESS )));
206
+ }
207
+
208
+ @ HapiTest
209
+ final HapiSpec dissociateTokenNegativeScenarios () {
210
+ final AtomicReference <Address > tokenAddress = new AtomicReference <>();
211
+ final AtomicReference <Address > accountAddress = new AtomicReference <>();
212
+ final var nonExistingAccount = "nonExistingAccount" ;
213
+ final var nullAccount = "nullAccount" ;
214
+ final var nonExistingToken = "nonExistingToken" ;
215
+ final var nullToken = "nullToken" ;
216
+ return defaultHapiSpec ("dissociateTokenNegativeScenarios" )
217
+ .given (
218
+ uploadInitCode (NEGATIVE_DISSOCIATIONS_CONTRACT ),
219
+ contractCreate (NEGATIVE_DISSOCIATIONS_CONTRACT ),
220
+ cryptoCreate (TOKEN_TREASURY ),
221
+ tokenCreate (TOKEN )
222
+ .tokenType (TokenType .FUNGIBLE_COMMON )
223
+ .initialSupply (50L )
224
+ .supplyKey (TOKEN_TREASURY )
225
+ .adminKey (TOKEN_TREASURY )
226
+ .treasury (TOKEN_TREASURY )
227
+ .exposingAddressTo (tokenAddress ::set ),
228
+ cryptoCreate (ACCOUNT ).exposingCreatedIdTo (id -> accountAddress .set (idAsHeadlongAddress (id ))))
229
+ .when (withOpContext ((spec , custom ) -> allRunFor (
230
+ spec ,
231
+ newKeyNamed (CONTRACT_KEY )
232
+ .shape (KEY_SHAPE .signedWith (sigs (ON , NEGATIVE_DISSOCIATIONS_CONTRACT ))),
233
+ cryptoUpdate (ACCOUNT ).key (CONTRACT_KEY ),
234
+ contractCall (
235
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
236
+ "dissociateTokenWithNonExistingAccount" ,
237
+ tokenAddress .get ())
238
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
239
+ .gas (GAS_TO_OFFER )
240
+ .via (nonExistingAccount )
241
+ .logged (),
242
+ getAccountInfo (ACCOUNT ).hasNoTokenRelationship (TOKEN ),
243
+ contractCall (
244
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
245
+ "dissociateTokenWithNullAccount" ,
246
+ tokenAddress .get ())
247
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
248
+ .gas (GAS_TO_OFFER )
249
+ .via (nullAccount )
250
+ .logged (),
251
+ getAccountInfo (ACCOUNT ).hasNoTokenRelationship (TOKEN ),
252
+ contractCall (
253
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
254
+ "dissociateTokenWithNonExistingTokenAddress" ,
255
+ accountAddress .get ())
256
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
257
+ .gas (GAS_TO_OFFER )
258
+ .via (nonExistingToken )
259
+ .logged (),
260
+ getAccountInfo (ACCOUNT ).hasNoTokenRelationship (TOKEN ),
261
+ contractCall (
262
+ NEGATIVE_DISSOCIATIONS_CONTRACT ,
263
+ "dissociateTokenWithNullTokenAddress" ,
264
+ accountAddress .get ())
265
+ .hasKnownStatus (CONTRACT_REVERT_EXECUTED )
266
+ .gas (GAS_TO_OFFER )
267
+ .via (nullToken )
268
+ .logged (),
269
+ getAccountInfo (ACCOUNT ).hasNoTokenRelationship (TOKEN ))))
270
+ .then (
271
+ childRecordsCheck (
272
+ nonExistingAccount ,
273
+ CONTRACT_REVERT_EXECUTED ,
274
+ recordWith ().status (INVALID_ACCOUNT_ID )),
275
+ childRecordsCheck (
276
+ nullAccount ,
277
+ CONTRACT_REVERT_EXECUTED ,
278
+ recordWith ().status (INVALID_ACCOUNT_ID )),
279
+ childRecordsCheck (
280
+ nonExistingToken ,
281
+ CONTRACT_REVERT_EXECUTED ,
282
+ recordWith ().status (TOKEN_NOT_ASSOCIATED_TO_ACCOUNT )),
283
+ childRecordsCheck (
284
+ nullToken ,
285
+ CONTRACT_REVERT_EXECUTED ,
286
+ recordWith ().status (TOKEN_NOT_ASSOCIATED_TO_ACCOUNT )));
47
287
}
48
288
49
289
@ Override
0 commit comments