From 6c6b03697183a4dd125b3c43848c55763ed6436f Mon Sep 17 00:00:00 2001 From: Stanimir Stoyanov Date: Fri, 1 Nov 2024 15:14:13 +0200 Subject: [PATCH] fix: Cherry-Pick (0.56) HIP 904 SetUnlimitedAutoAssociations System Contract Implementation (#16196) Signed-off-by: Stanimir Stoyanov --- .../exec/processors/HasTranslatorsModule.java | 10 ++ .../SetUnlimitedAutoAssociationsCall.java | 57 ++++++++ ...etUnlimitedAutoAssociationsTranslator.java | 72 ++++++++++ .../contract/impl/state/ProxyEvmAccount.java | 4 +- .../systemcontracts/CallAttemptHelpers.java | 23 +++ .../SetUnlimitedAutoAssociationsCallTest.java | 92 ++++++++++++ ...limitedAutoAssociationsTranslatorTest.java | 131 ++++++++++++++++++ .../HRCSetUnlimitedAutoAssociationsTest.java | 114 +++++++++++++++ .../contract/contracts/IHRC632/IHRC632.json | 2 +- .../IHRC904UnlimitedAutoAssociations.bin | 0 .../IHRC904UnlimitedAutoAssociations.json | 21 +++ .../IHRC904UnlimitedAutoAssociations.sol | 6 + 12 files changed, 530 insertions(+), 2 deletions(-) create mode 100644 hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCall.java create mode 100644 hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslator.java create mode 100644 hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCallTest.java create mode 100644 hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslatorTest.java create mode 100644 hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/HRCSetUnlimitedAutoAssociationsTest.java create mode 100644 hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.bin create mode 100644 hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.json create mode 100644 hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.sol diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/HasTranslatorsModule.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/HasTranslatorsModule.java index 501130cfba89..c6f376fa7750 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/HasTranslatorsModule.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/HasTranslatorsModule.java @@ -24,6 +24,7 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.hederaaccountnumalias.HederaAccountNumAliasTranslator; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.isauthorizedraw.IsAuthorizedRawTranslator; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.isvalidalias.IsValidAliasTranslator; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations.SetUnlimitedAutoAssociationsTranslator; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoSet; @@ -99,4 +100,13 @@ static CallTranslator provideIsAuthorizedRawTranslator( @NonNull final IsAuthorizedRawTranslator translator) { return translator; } + + @Provides + @Singleton + @IntoSet + @Named("HasTranslators") + static CallTranslator provideSetUnlimitedAutoAssociationsTranslator( + @NonNull final SetUnlimitedAutoAssociationsTranslator translator) { + return translator; + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCall.java new file mode 100644 index 000000000000..92308199bf3c --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCall.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 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.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations; + +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.encodedRc; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.standardized; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.exec.gas.DispatchType; +import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCall; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.HasCallAttempt; +import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.hyperledger.besu.evm.frame.MessageFrame; + +public class SetUnlimitedAutoAssociationsCall extends AbstractCall { + + private final AccountID sender; + private final TransactionBody transactionBody; + private final VerificationStrategy verificationStrategy; + + public SetUnlimitedAutoAssociationsCall( + @NonNull final HasCallAttempt attempt, @NonNull final TransactionBody transactionBody) { + super(attempt.systemContractGasCalculator(), attempt.enhancement(), false); + this.sender = attempt.senderId(); + this.transactionBody = requireNonNull(transactionBody); + this.verificationStrategy = attempt.defaultVerificationStrategy(); + } + + @NonNull + @Override + public PricedResult execute(@NonNull final MessageFrame frame) { + requireNonNull(frame); + final var recordBuilder = systemContractOperations() + .dispatch(transactionBody, verificationStrategy, sender, ContractCallStreamBuilder.class); + + final var gasRequirement = gasCalculator.gasRequirement(transactionBody, DispatchType.CRYPTO_UPDATE, sender); + return completionWith(gasRequirement, recordBuilder, encodedRc(standardized(recordBuilder.status()))); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslator.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslator.java new file mode 100644 index 000000000000..fe9d89716448 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslator.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 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.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations; + +import static java.util.Objects.requireNonNull; + +import com.esaulpaugh.headlong.abi.Function; +import com.hedera.hapi.node.token.CryptoUpdateTransactionBody; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCallTranslator; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.HasCallAttempt; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes; +import com.hedera.node.config.data.ContractsConfig; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class SetUnlimitedAutoAssociationsTranslator extends AbstractCallTranslator { + + public static final Function SET_UNLIMITED_AUTO_ASSOC = + new Function("setUnlimitedAutomaticAssociations(bool)", ReturnTypes.INT_64); + + private static final int UNLIMITED_AUTO_ASSOCIATIONS = -1; + private static final int NO_AUTO_ASSOCIATIONS = 0; + + @Inject + public SetUnlimitedAutoAssociationsTranslator() { + // Dagger2 + } + + @Override + public boolean matches(@NonNull final HasCallAttempt attempt) { + final var setUnlimitedAutoAssocEnabled = attempt.configuration() + .getConfigData(ContractsConfig.class) + .systemContractSetUnlimitedAutoAssociationsEnabled(); + return attempt.isSelectorIfConfigEnabled(SET_UNLIMITED_AUTO_ASSOC, setUnlimitedAutoAssocEnabled); + } + + @Override + public Call callFrom(@NonNull final HasCallAttempt attempt) { + requireNonNull(attempt); + final var call = SET_UNLIMITED_AUTO_ASSOC.decodeCall(attempt.inputBytes()); + final var setUnlimitedAutoAssociations = (boolean) call.get(0); + return new SetUnlimitedAutoAssociationsCall(attempt, bodyFor(attempt, setUnlimitedAutoAssociations)); + } + + @NonNull + private TransactionBody bodyFor(@NonNull final HasCallAttempt attempt, final boolean setUnlimitedAutoAssociations) { + final var cryptoUpdate = CryptoUpdateTransactionBody.newBuilder() + .accountIDToUpdate(attempt.redirectAccountId()) + .maxAutomaticTokenAssociations( + setUnlimitedAutoAssociations ? UNLIMITED_AUTO_ASSOCIATIONS : NO_AUTO_ASSOCIATIONS) + .build(); + return TransactionBody.newBuilder().cryptoUpdateAccount(cryptoUpdate).build(); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java index 043274a00355..ccc2df4bada8 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java @@ -46,7 +46,9 @@ public class ProxyEvmAccount extends AbstractProxyEvmAccount { // hbarAllowance(address spender) 0xbbee989e, // hbarApprove(address spender, int256 amount) - 0x86aff07c); + 0x86aff07c, + // setUnlimitedAutomaticAssociations(bool enableAutoAssociations + 0xf5677e99); // Only pass in a non-null account address if the function selector is eligible for proxy redirection. // A null address will return the 0x bytecode. diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/CallAttemptHelpers.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/CallAttemptHelpers.java index 8f433e8dc903..11d17b1e7a71 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/CallAttemptHelpers.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/CallAttemptHelpers.java @@ -150,4 +150,27 @@ public static HasCallAttempt prepareHasAttemptWithSelector( List.of(translator), false); } + + public static HasCallAttempt prepareHasAttemptWithSelectorAndCustomConfig( + final Function function, + final CallTranslator translator, + final HederaWorldUpdater.Enhancement enhancement, + final AddressIdConverter addressIdConverter, + final VerificationStrategies verificationStrategies, + final SystemContractGasCalculator gasCalculator, + final Configuration config) { + final var input = TestHelpers.bytesForRedirectAccount(function.selector(), NON_SYSTEM_LONG_ZERO_ADDRESS); + return new HasCallAttempt( + input, + OWNER_BESU_ADDRESS, + OWNER_BESU_ADDRESS, + false, + enhancement, + config, + addressIdConverter, + verificationStrategies, + gasCalculator, + List.of(translator), + false); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCallTest.java new file mode 100644 index 000000000000..eecc6b8b1d38 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsCallTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 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.node.app.service.contract.impl.test.exec.systemcontracts.has.setunlimitedautoassociations; + +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.REVERTED_SUCCESS; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import com.hedera.hapi.node.base.ResponseCodeEnum; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.HasCallAttempt; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations.SetUnlimitedAutoAssociationsCall; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations.SetUnlimitedAutoAssociationsTranslator; +import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder; +import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.common.CallTestBase; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.evm.frame.MessageFrame.State; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SetUnlimitedAutoAssociationsCallTest extends CallTestBase { + + @Mock + private HasCallAttempt attempt; + + @Mock + private TransactionBody transactionBody; + + @Mock + private ContractCallStreamBuilder recordBuilder; + + @Mock + private SystemContractGasCalculator gasCalculator; + + private SetUnlimitedAutoAssociationsCall subject; + + @Test + void successCall() { + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(systemContractOperations.dispatch(any(), any(), any(), any())).willReturn(recordBuilder); + given(recordBuilder.status()).willReturn(ResponseCodeEnum.SUCCESS); + + subject = new SetUnlimitedAutoAssociationsCall(attempt, transactionBody); + final var result = subject.execute(frame).fullResult().result(); + assertEquals(State.COMPLETED_SUCCESS, result.getState()); + assertEquals( + Bytes.wrap(SetUnlimitedAutoAssociationsTranslator.SET_UNLIMITED_AUTO_ASSOC + .getOutputs() + .encodeElements((long) SUCCESS.getNumber()) + .array()), + result.getOutput()); + } + + @Test + void revertCall() { + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(systemContractOperations.dispatch(any(), any(), any(), any())).willReturn(recordBuilder); + given(recordBuilder.status()).willReturn(ResponseCodeEnum.REVERTED_SUCCESS); + + subject = new SetUnlimitedAutoAssociationsCall(attempt, transactionBody); + final var result = subject.execute(frame).fullResult().result(); + assertEquals(State.COMPLETED_SUCCESS, result.getState()); + assertEquals( + Bytes.wrap(SetUnlimitedAutoAssociationsTranslator.SET_UNLIMITED_AUTO_ASSOC + .getOutputs() + .encodeElements((long) REVERTED_SUCCESS.getNumber()) + .array()), + result.getOutput()); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslatorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslatorTest.java new file mode 100644 index 000000000000..be0cfd81444b --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/setunlimitedautoassociations/SetUnlimitedAutoAssociationsTranslatorTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 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.node.app.service.contract.impl.test.exec.systemcontracts.has.setunlimitedautoassociations; + +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations.SetUnlimitedAutoAssociationsTranslator.SET_UNLIMITED_AUTO_ASSOC; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.CallAttemptHelpers.prepareHasAttemptWithSelectorAndCustomConfig; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.BDDMockito.given; + +import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; +import com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations; +import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.HasCallAttempt; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations.SetUnlimitedAutoAssociationsCall; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.setunlimitedautoassociations.SetUnlimitedAutoAssociationsTranslator; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; +import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; +import com.hedera.node.config.data.ContractsConfig; +import com.swirlds.config.api.Configuration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SetUnlimitedAutoAssociationsTranslatorTest { + + @Mock + private HasCallAttempt attempt; + + @Mock + private SystemContractGasCalculator gasCalculator; + + @Mock + private AddressIdConverter addressIdConverter; + + @Mock + private HederaWorldUpdater.Enhancement enhancement; + + @Mock + private VerificationStrategies verificationStrategies; + + @Mock + private HederaNativeOperations nativeOperations; + + @Mock + private Configuration configuration; + + @Mock + private ContractsConfig contractsConfig; + + private SetUnlimitedAutoAssociationsTranslator subject; + + @BeforeEach + void setUp() { + subject = new SetUnlimitedAutoAssociationsTranslator(); + } + + @Test + void matchesWhenEnabled() { + given(enhancement.nativeOperations()).willReturn(nativeOperations); + given(configuration.getConfigData(ContractsConfig.class)).willReturn(contractsConfig); + given(contractsConfig.systemContractSetUnlimitedAutoAssociationsEnabled()) + .willReturn(true); + attempt = prepareHasAttemptWithSelectorAndCustomConfig( + SET_UNLIMITED_AUTO_ASSOC, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertTrue(subject.matches(attempt)); + } + + @Test + void matchesWhenDisabled() { + given(enhancement.nativeOperations()).willReturn(nativeOperations); + given(configuration.getConfigData(ContractsConfig.class)).willReturn(contractsConfig); + given(contractsConfig.systemContractSetUnlimitedAutoAssociationsEnabled()) + .willReturn(false); + attempt = prepareHasAttemptWithSelectorAndCustomConfig( + SET_UNLIMITED_AUTO_ASSOC, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertFalse(subject.matches(attempt)); + } + + @Test + void callFromWithTrueValue() { + final var inputBytes = SET_UNLIMITED_AUTO_ASSOC.encodeCallWithArgs(true); + given(attempt.inputBytes()).willReturn(inputBytes.array()); + given(attempt.enhancement()).willReturn(enhancement); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + final var call = subject.callFrom(attempt); + assertThat(call).isInstanceOf(SetUnlimitedAutoAssociationsCall.class); + } + + @Test + void callFromWithFalseValue() { + final var inputBytes = SET_UNLIMITED_AUTO_ASSOC.encodeCallWithArgs(false); + given(attempt.inputBytes()).willReturn(inputBytes.array()); + given(attempt.enhancement()).willReturn(enhancement); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + final var call = subject.callFrom(attempt); + assertThat(call).isInstanceOf(SetUnlimitedAutoAssociationsCall.class); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/HRCSetUnlimitedAutoAssociationsTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/HRCSetUnlimitedAutoAssociationsTest.java new file mode 100644 index 000000000000..188ed48b0338 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/HRCSetUnlimitedAutoAssociationsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 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.suites.contract.precompile; + +import static com.hedera.services.bdd.junit.TestTags.SMART_CONTRACT; +import static com.hedera.services.bdd.spec.HapiPropertySource.idAsHeadlongAddress; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; +import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.isLiteralResult; +import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.resultWith; +import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountInfo; +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCallWithFunctionAbi; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate; +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.ONE_HUNDRED_HBARS; +import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; + +import com.esaulpaugh.headlong.abi.Address; +import com.hedera.services.bdd.junit.HapiTest; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Tag; + +@Tag(SMART_CONTRACT) +public class HRCSetUnlimitedAutoAssociationsTest { + + @HapiTest + public Stream hrcSetUnlimitedAutoAssociations() { + final AtomicReference
accountNum = new AtomicReference<>(); + return hapiTest( + cryptoCreate("account") + .balance(100 * ONE_HUNDRED_HBARS) + .maxAutomaticTokenAssociations(0) + .exposingCreatedIdTo(id -> accountNum.set(idAsHeadlongAddress(id))), + withOpContext((spec, opLog) -> allRunFor( + spec, + contractCallWithFunctionAbi( + "0.0." + accountNum.get().value(), + getABIFor( + com.hedera.services.bdd.suites.contract.Utils.FunctionType.FUNCTION, + "setUnlimitedAutomaticAssociations", + "IHRC904UnlimitedAutoAssociations"), + true) + .via("setUnlimitedAutoAssociations") + .payingWith("account") + .gas(1_000_000L), + getTxnRecord("setUnlimitedAutoAssociations") + .logged() + .hasPriority(recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .resultThruAbi( + getABIFor( + com.hedera.services.bdd.suites.contract.Utils + .FunctionType.FUNCTION, + "setUnlimitedAutomaticAssociations", + "IHRC904UnlimitedAutoAssociations"), + isLiteralResult(new Object[] {Long.valueOf(22)})))), + getAccountInfo("account").hasMaxAutomaticAssociations(-1)))); + } + + @HapiTest + public Stream hrcSetDisabledAutoAssociations() { + final AtomicReference
accountNum = new AtomicReference<>(); + return hapiTest( + cryptoCreate("account") + .balance(100 * ONE_HUNDRED_HBARS) + .maxAutomaticTokenAssociations(10) + .exposingCreatedIdTo(id -> accountNum.set(idAsHeadlongAddress(id))), + withOpContext((spec, opLog) -> allRunFor( + spec, + contractCallWithFunctionAbi( + "0.0." + accountNum.get().value(), + getABIFor( + com.hedera.services.bdd.suites.contract.Utils.FunctionType.FUNCTION, + "setUnlimitedAutomaticAssociations", + "IHRC904UnlimitedAutoAssociations"), + false) + .via("setUnlimitedAutoAssociations") + .payingWith("account") + .gas(1_000_000L), + getTxnRecord("setUnlimitedAutoAssociations") + .logged() + .hasPriority(recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .resultThruAbi( + getABIFor( + com.hedera.services.bdd.suites.contract.Utils + .FunctionType.FUNCTION, + "setUnlimitedAutomaticAssociations", + "IHRC904UnlimitedAutoAssociations"), + isLiteralResult(new Object[] {Long.valueOf(22)})))), + getAccountInfo("account").hasMaxAutomaticAssociations(0)))); + } +} diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC632/IHRC632.json b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC632/IHRC632.json index cfe9e8070875..e9ff2097d28e 100644 --- a/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC632/IHRC632.json +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC632/IHRC632.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"hbarAllowance","outputs":[{"internalType":"int64","name":"responseCode","type":"int64"},{"internalType":"int256","name":"allowance","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"hbarApprove","outputs":[{"internalType":"int64","name":"responseCode","type":"int64"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"hbarAllowance","outputs":[{"internalType":"int64","name":"responseCode","type":"int64"},{"internalType":"int256","name":"allowance","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"hbarApprove","outputs":[{"internalType":"int64","name":"responseCode","type":"int64"}],"stateMutability":"nonpayable", "type":"function"}] \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.bin b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.bin new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.json b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.json new file mode 100644 index 000000000000..d21d29619e29 --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "bool", + "name": "enableAutoAssociations", + "type": "bool" + } + ], + "name": "setUnlimitedAutomaticAssociations", + "outputs": [ + { + "internalType": "int64", + "name": "responseCode", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.sol b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.sol new file mode 100644 index 000000000000..336236f9d4f8 --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/IHRC904UnlimitedAutoAssociations/IHRC904UnlimitedAutoAssociations.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +interface IHRC904UnlimitedAutoAssociations { + function setUnlimitedAutomaticAssociations(bool enableAutoAssociations) external returns (int64 responseCode); +} \ No newline at end of file