Skip to content

Commit a2632d4

Browse files
test: Add negative test cases to DissociatePrecompileSuite (#12272)
Signed-off-by: Stanimir Stoyanov <[email protected]> Co-authored-by: Mustafa Uzun <[email protected]>
1 parent 6a62b45 commit a2632d4

File tree

4 files changed

+578
-1
lines changed

4 files changed

+578
-1
lines changed

hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/DissociatePrecompileSuite.java

+241-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,40 @@
1717
package com.hedera.services.bdd.suites.contract.precompile;
1818

1919
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;
2044

45+
import com.esaulpaugh.headlong.abi.Address;
46+
import com.hedera.services.bdd.junit.HapiTest;
2147
import com.hedera.services.bdd.junit.HapiTestSuite;
2248
import com.hedera.services.bdd.spec.HapiSpec;
49+
import com.hedera.services.bdd.spec.keys.KeyShape;
2350
import com.hedera.services.bdd.suites.HapiSuite;
51+
import com.hederahashgraph.api.proto.java.TokenType;
2452
import java.util.List;
53+
import java.util.concurrent.atomic.AtomicReference;
2554
import org.apache.logging.log4j.LogManager;
2655
import org.apache.logging.log4j.Logger;
2756
import org.junit.jupiter.api.Tag;
@@ -31,6 +60,13 @@
3160
public class DissociatePrecompileSuite extends HapiSuite {
3261

3362
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);
3470

3571
public static void main(String... args) {
3672
new DissociatePrecompileSuite().runSuiteAsync();
@@ -43,7 +79,211 @@ public boolean canRunConcurrent() {
4379

4480
@Override
4581
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)));
47287
}
48288

49289
@Override

0 commit comments

Comments
 (0)