Skip to content

Commit

Permalink
fix: Cherry-Pick (0.56) HIP 904 SetUnlimitedAutoAssociations System C…
Browse files Browse the repository at this point in the history
…ontract Implementation (#16196)

Signed-off-by: Stanimir Stoyanov <stanimir.stoyanov@limechain.tech>
  • Loading branch information
stoyanov-st authored Nov 1, 2024
1 parent 3cb48da commit 6c6b036
Show file tree
Hide file tree
Showing 12 changed files with 530 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -99,4 +100,13 @@ static CallTranslator<HasCallAttempt> provideIsAuthorizedRawTranslator(
@NonNull final IsAuthorizedRawTranslator translator) {
return translator;
}

@Provides
@Singleton
@IntoSet
@Named("HasTranslators")
static CallTranslator<HasCallAttempt> provideSetUnlimitedAutoAssociationsTranslator(
@NonNull final SetUnlimitedAutoAssociationsTranslator translator) {
return translator;
}
}
Original file line number Diff line number Diff line change
@@ -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())));
}
}
Original file line number Diff line number Diff line change
@@ -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<HasCallAttempt> {

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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,27 @@ public static HasCallAttempt prepareHasAttemptWithSelector(
List.of(translator),
false);
}

public static HasCallAttempt prepareHasAttemptWithSelectorAndCustomConfig(
final Function function,
final CallTranslator<HasCallAttempt> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading

0 comments on commit 6c6b036

Please sign in to comment.