Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

04632 Fuzzing test for LazyCreate through precompiles #6527

Merged
merged 20 commits into from May 23, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
58352b9
04632: added transfer fungible token case lazy create fuzzing
agadzhalov Apr 25, 2023
3b205c1
04632: added ECDSAKeysUtils class
agadzhalov Apr 25, 2023
ae7681a
04632: added fuzz test case for non fungible token transfer
agadzhalov Apr 26, 2023
35b5501
04632: added failing fuzz test case for ERC20 transfer
agadzhalov Apr 26, 2023
2b2346a
04632: incremented initial supply of fungible token and fix for ERC20…
agadzhalov Apr 28, 2023
b3d340c
04632: cryptoUpdateAliased - maxAutomaticAssociations
agadzhalov May 4, 2023
40db107
04632: hbar transfer lazy create through precompile
agadzhalov May 4, 2023
b9d844a
04632: added test for ERC721 transfer lazy create
agadzhalov May 5, 2023
b6b701c
04632: removed unused methods
agadzhalov May 5, 2023
7ceb26c
04632: renamed providers with more straightforward names
agadzhalov May 5, 2023
2258905
04632: hbar transfer lazy create through precompile
agadzhalov May 9, 2023
6bbdf15
04632: init given for fungible token
agadzhalov May 9, 2023
0fbe1ae
04632: added full prefix for alias and finalize with sigMapPrefixes
agadzhalov May 9, 2023
142998d
04632: overriding atomicCryptoTransfer property through PreservingHap…
agadzhalov May 9, 2023
df90084
04632: initOperations divided into smaller methods for better readabi…
agadzhalov May 10, 2023
dbd2457
04632: added test case for non fungible transfer
agadzhalov May 10, 2023
a897510
04632: added sonar fix - private constructor
agadzhalov May 11, 2023
27e0399
Merge branch 'develop' into 04632-fuzztest-lazycreate-precompile-tran…
agadzhalov May 11, 2023
7202762
04632: refactoring - moved logic to AddressAliasIdFuzzing and IdFuzzi…
agadzhalov May 15, 2023
97b6b06
04632: Reduced maxAutomaticAssociations
agadzhalov May 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.services.bdd.spec.infrastructure.providers.ops.precompile;

import static com.hedera.node.app.service.evm.utils.EthSigsUtils.recoverAddressFromPubKey;
import static com.hedera.services.bdd.spec.keys.TrieSigMapGenerator.uniqueWithFullPrefixesFor;
import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.*;
import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext;
import static com.hedera.services.bdd.suites.HapiSuite.GENESIS;
import static com.hedera.services.bdd.suites.contract.Utils.asAddress;
import static com.hedera.services.bdd.suites.crypto.AutoCreateUtils.updateSpecFor;
import static com.hedera.services.bdd.suites.regression.factories.IdFuzzingProviderFactory.*;
import static com.hedera.services.bdd.suites.utils.ECDSAKeysUtils.getEvmAddressFromString;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS;

import com.hedera.services.bdd.spec.HapiSpecOperation;
import com.hedera.services.bdd.spec.infrastructure.EntityNameProvider;
import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry;
import com.hedera.services.bdd.spec.infrastructure.OpProvider;
import com.hedera.services.bdd.spec.queries.meta.HapiGetTxnRecord;
import com.hedera.services.bdd.spec.transactions.contract.HapiParserUtil;
import com.hederahashgraph.api.proto.java.Key;
import java.math.BigInteger;
import java.util.Optional;

public class RandomERC20TransferLazyCreate implements OpProvider {
private final HapiSpecRegistry registry;
private static final long GAS_TO_OFFER = 5_000_000L;
private static final String TRANSFER = "transfer";
private static final String TRANSFER_TXN = "transferTxn";
private final EntityNameProvider<Key> keys;

public RandomERC20TransferLazyCreate(HapiSpecRegistry registry, EntityNameProvider<Key> keys) {
this.registry = registry;
this.keys = keys;
}

private Optional<String> randomKey() {
return keys.getQualifying().filter(k -> !k.isEmpty());
}

@Override
public Optional<HapiSpecOperation> get() {
return randomKey().map(this::generateLazyCreateTransferOfERC20);
}

private HapiSpecOperation generateLazyCreateTransferOfERC20(String evmAddressRecipient) {
final var addressBytes = recoverAddressFromPubKey(getEvmAddressFromString(registry, evmAddressRecipient));
return withOpContext((spec, opLog) -> {
final var opContractCall = contractCall(
ERC_20_CONTRACT,
TRANSFER,
HapiParserUtil.asHeadlongAddress(asAddress(registry.getTokenID(ERC_FUNGIBLE_TOKEN))),
HapiParserUtil.asHeadlongAddress(addressBytes),
BigInteger.valueOf(2))
.via(TRANSFER_TXN)
.gas(GAS_TO_OFFER)
.hasKnownStatus(SUCCESS);

final HapiGetTxnRecord hapiGetTxnRecord =
getTxnRecord(TRANSFER_TXN).andAllChildRecords().assertingNothingAboutHashes();

allRunFor(spec, opContractCall, hapiGetTxnRecord);

if (!hapiGetTxnRecord.getChildRecords().isEmpty()) {

updateSpecFor(spec, evmAddressRecipient);
final var opUpdate = cryptoUpdateAliased(evmAddressRecipient)
.maxAutomaticAssociations(1000000000)
.payingWith(GENESIS)
.signedBy(evmAddressRecipient, GENESIS)
.sigMapPrefixes(uniqueWithFullPrefixesFor(evmAddressRecipient))
.hasPrecheckFrom(STANDARD_PERMISSIBLE_PRECHECKS)
.hasKnownStatusFrom(STANDARD_PERMISSIBLE_OUTCOMES)
.logged();

allRunFor(spec, opUpdate);
}
});
}
}
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.services.bdd.spec.infrastructure.providers.ops.precompile;

import static com.hedera.node.app.service.evm.utils.EthSigsUtils.recoverAddressFromPubKey;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCall;
import static com.hedera.services.bdd.suites.contract.Utils.asAddress;
import static com.hedera.services.bdd.suites.regression.factories.IdFuzzingProviderFactory.*;
import static com.hedera.services.bdd.suites.utils.ECDSAKeysUtils.getEvmAddressFromString;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.*;

import com.hedera.services.bdd.spec.HapiSpecOperation;
import com.hedera.services.bdd.spec.infrastructure.EntityNameProvider;
import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry;
import com.hedera.services.bdd.spec.infrastructure.OpProvider;
import com.hedera.services.bdd.spec.transactions.contract.HapiContractCall;
import com.hedera.services.bdd.spec.transactions.contract.HapiParserUtil;
import com.hederahashgraph.api.proto.java.Key;
import java.math.BigInteger;
import java.util.Optional;

public class RandomERC721TransferLazyCreate implements OpProvider {
private final HapiSpecRegistry registry;
private static final long GAS_TO_OFFER = 5_000_000L;
private static final String TRANSFER_FROM_ACCOUNT_TXN = "transferFromAccountTxn";
private static final String TRANSFER_FROM = "transferFrom";
private final EntityNameProvider<Key> keys;

public RandomERC721TransferLazyCreate(HapiSpecRegistry registry, EntityNameProvider<Key> keys) {
this.registry = registry;
this.keys = keys;
}

private Optional<String> randomKey() {
return keys.getQualifying().filter(k -> !k.isEmpty());
}

@Override
public Optional<HapiSpecOperation> get() {
return randomKey().map(this::generateERC721TransferLazyCreate);
}

private HapiContractCall generateERC721TransferLazyCreate(String evmAddressRecipient) {
final var addressBytes = recoverAddressFromPubKey(getEvmAddressFromString(registry, evmAddressRecipient));
/**
* CONTRACT_REVERT_EXECUTED is also included in the outcomes, because we have a predefined amount of
* ERC721 tokens and predefined allowances. As more and more transfers happen in the fuzzing test once the
* predefined allowance limit is reached transfers are impossible and the actual error in the precompile is
* SPENDER_DOES_NOT_HAVE_ALLOWANCE
*/
return contractCall(
ERC_721_CONTRACT,
TRANSFER_FROM,
HapiParserUtil.asHeadlongAddress(asAddress(registry.getTokenID(ERC_NON_FUNGIBLE_TOKEN))),
HapiParserUtil.asHeadlongAddress(asAddress(registry.getAccountID(OWNER))),
HapiParserUtil.asHeadlongAddress(addressBytes),
BigInteger.valueOf(1))
.via(TRANSFER_FROM_ACCOUNT_TXN)
.gas(GAS_TO_OFFER)
.hasPrecheckFrom(STANDARD_PERMISSIBLE_PRECHECKS)
.hasKnownStatusFrom(SUCCESS, CONTRACT_REVERT_EXECUTED);
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.services.bdd.spec.infrastructure.providers.ops.precompile;

import static com.hedera.node.app.service.evm.utils.EthSigsUtils.recoverAddressFromPubKey;
import static com.hedera.services.bdd.spec.keys.TrieSigMapGenerator.uniqueWithFullPrefixesFor;
import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCall;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoUpdateAliased;
import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext;
import static com.hedera.services.bdd.suites.HapiSuite.GENESIS;
import static com.hedera.services.bdd.suites.contract.Utils.asAddress;
import static com.hedera.services.bdd.suites.crypto.AutoCreateUtils.updateSpecFor;
import static com.hedera.services.bdd.suites.regression.factories.IdFuzzingProviderFactory.*;
import static com.hedera.services.bdd.suites.utils.ECDSAKeysUtils.getEvmAddressFromString;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.*;

import com.hedera.services.bdd.spec.HapiSpecOperation;
import com.hedera.services.bdd.spec.infrastructure.EntityNameProvider;
import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry;
import com.hedera.services.bdd.spec.infrastructure.OpProvider;
import com.hedera.services.bdd.spec.queries.meta.HapiGetTxnRecord;
import com.hedera.services.bdd.spec.transactions.contract.HapiParserUtil;
import com.hederahashgraph.api.proto.java.Key;
import java.util.Optional;

public class RandomFungibleTransferLazyCreate implements OpProvider {
private final HapiSpecRegistry registry;
private static final long GAS_TO_OFFER = 5_000_000L;

private final EntityNameProvider<Key> keys;

public RandomFungibleTransferLazyCreate(HapiSpecRegistry registry, EntityNameProvider<Key> keys) {
this.registry = registry;
this.keys = keys;
}

private Optional<String> randomKey() {
return keys.getQualifying().filter(k -> !k.isEmpty());
}

@Override
public Optional<HapiSpecOperation> get() {
return randomKey().map(this::generateLazyCreateTransferOfFungibleToken);
}

private HapiSpecOperation generateLazyCreateTransferOfFungibleToken(String evmAddressRecipient) {
final var addressBytes = recoverAddressFromPubKey(getEvmAddressFromString(registry, evmAddressRecipient));

return withOpContext((spec, opLog) -> {
final var opContractCall = contractCall(
TRANSFER_TO_ALIAS_PRECOMPILE_CONTRACT,
"transferTokenCallNestedThenAgain",
HapiParserUtil.asHeadlongAddress(asAddress(registry.getTokenID(FUNGIBLE_TOKEN))),
HapiParserUtil.asHeadlongAddress(asAddress(registry.getAccountID(OWNER))),
HapiParserUtil.asHeadlongAddress(addressBytes),
2L,
2L)
.via(TRANSFER_TOKEN_TXN)
.alsoSigningWithFullPrefix(OWNER)
.gas(GAS_TO_OFFER)
.hasKnownStatus(SUCCESS);

final HapiGetTxnRecord hapiGetTxnRecord =
getTxnRecord(TRANSFER_TOKEN_TXN).andAllChildRecords().assertingNothingAboutHashes();

allRunFor(spec, opContractCall, hapiGetTxnRecord);

if (!hapiGetTxnRecord.getChildRecords().isEmpty()) {
updateSpecFor(spec, evmAddressRecipient);
final var opUpdate = cryptoUpdateAliased(evmAddressRecipient)
.maxAutomaticAssociations(1000000000)
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
.payingWith(GENESIS)
.signedBy(evmAddressRecipient, GENESIS)
.sigMapPrefixes(uniqueWithFullPrefixesFor(evmAddressRecipient))
.hasPrecheckFrom(OK)
.hasKnownStatusFrom(SUCCESS, INVALID_SIGNATURE)
.logged();

allRunFor(spec, opUpdate);
}
});
}
}
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.services.bdd.spec.infrastructure.providers.ops.precompile;

import static com.hedera.node.app.service.evm.utils.EthSigsUtils.recoverAddressFromPubKey;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCall;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.accountAmount;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.transferList;
import static com.hedera.services.bdd.suites.regression.factories.IdFuzzingProviderFactory.*;
import static com.hedera.services.bdd.suites.utils.ECDSAKeysUtils.getEvmAddressFromString;

import com.esaulpaugh.headlong.abi.Tuple;
import com.hedera.services.bdd.spec.HapiSpecOperation;
import com.hedera.services.bdd.spec.infrastructure.EntityNameProvider;
import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry;
import com.hedera.services.bdd.spec.infrastructure.OpProvider;
import com.hedera.services.bdd.spec.transactions.contract.HapiContractCall;
import com.hedera.services.bdd.spec.utilops.UtilVerbs;
import com.hederahashgraph.api.proto.java.Key;
import java.util.Optional;

public class RandomHbarTransferLazyCreate implements OpProvider {
private static final String TRANSFER_TXN = "transferTxn";

private final HapiSpecRegistry registry;
private static final long GAS_TO_OFFER = 5_000_000L;
private static final Tuple[] EMPTY_TUPLE_ARRAY = new Tuple[] {};
private final EntityNameProvider<Key> keys;

public RandomHbarTransferLazyCreate(HapiSpecRegistry registry, EntityNameProvider<Key> keys) {
this.registry = registry;
this.keys = keys;
}

private Optional<String> randomKey() {
return keys.getQualifying().filter(k -> !k.isEmpty());
}

@Override
public Optional<HapiSpecOperation> get() {
return randomKey().map(this::generateHbarTransferLazyCreate);
}

private HapiContractCall generateHbarTransferLazyCreate(String evmAddressRecipient) {
final var cryptoTransferV2LazyCreateFn = "cryptoTransferV2LazyCreate";
final var sender = registry.getAccountID(SENDER);
final var amountToBeSent = 50L;

final var addressBytes = recoverAddressFromPubKey(getEvmAddressFromString(registry, evmAddressRecipient));
return contractCall(
NESTED_LAZY_PRECOMPILE_CONTRACT,
cryptoTransferV2LazyCreateFn,
transferList()
.withAccountAmounts(
accountAmount(sender, -amountToBeSent, false),
UtilVerbs.accountAmountAlias(addressBytes, amountToBeSent, false))
.build(),
EMPTY_TUPLE_ARRAY,
transferList()
.withAccountAmounts(
accountAmount(sender, -amountToBeSent, false),
UtilVerbs.accountAmountAlias(addressBytes, amountToBeSent, false))
.build(),
EMPTY_TUPLE_ARRAY)
.payingWith(UNIQUE_PAYER_ACCOUNT)
.via(TRANSFER_TXN)
.signedBy(UNIQUE_PAYER_ACCOUNT, MULTI_KEY)
.alsoSigningWithFullPrefix(MULTI_KEY)
.hasPrecheckFrom(STANDARD_PERMISSIBLE_PRECHECKS)
.gas(GAS_TO_OFFER);
}
}