Skip to content

fix: treat an out-of-range allowance amount as an invalid op #12445

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

Merged
merged 3 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ public FullResult computeFully(@NonNull final Bytes input, @NonNull final Messag
// without setting a halt reason to simulate mono-service for differential testing
return haltResult(contractsConfigOf(frame).precompileHtsDefaultGasCost());
}
} catch (final Exception e) {
log.warn("Failed to create HTS call from input {}", input, e);
} catch (final Exception ignore) {
// Input that cannot be translated to an executable call, for any
// reason, halts the frame and consumes all remaining gas
return haltResult(INVALID_OPERATION, frame.getRemainingGas());
}
return resultOfExecuting(attempt, call, input, frame);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,14 @@
import com.hedera.node.app.service.contract.impl.records.ContractCallRecordBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.math.BigInteger;

public abstract class AbstractGrantApprovalCall extends AbstractHtsCall {
protected final VerificationStrategy verificationStrategy;
protected final AccountID senderId;
protected final TokenID tokenId;
protected final AccountID spenderId;
protected final BigInteger amount;
protected final TokenType tokenType;
protected final long amount;

// too many parameters
@SuppressWarnings("java:S107")
Expand All @@ -55,7 +54,7 @@ protected AbstractGrantApprovalCall(
@NonNull final AccountID senderId,
@NonNull final TokenID tokenId,
@NonNull final AccountID spenderId,
@NonNull final BigInteger amount,
final long amount,
@NonNull final TokenType tokenType,
final boolean isViewCall) {
super(gasCalculator, enhancement, isViewCall);
Expand Down Expand Up @@ -98,7 +97,7 @@ private CryptoDeleteAllowanceTransactionBody remove(AccountID ownerId) {
.nftAllowances(NftRemoveAllowance.newBuilder()
.tokenId(tokenId)
.owner(ownerId)
.serialNumbers(amount.longValue())
.serialNumbers(amount)
.build())
.build();
}
Expand All @@ -110,7 +109,7 @@ private CryptoApproveAllowanceTransactionBody approveDelegate(AccountID ownerId,
.spender(spenderId)
.delegatingSpender(delegateSpenderId)
.owner(ownerId)
.serialNumbers(amount.longValue())
.serialNumbers(amount)
.build())
.build();
}
Expand All @@ -122,15 +121,15 @@ private CryptoApproveAllowanceTransactionBody approve(AccountID ownerId) {
.tokenId(tokenId)
.spender(spenderId)
.owner(ownerId)
.amount(amount.longValue())
.amount(amount)
.build())
.build()
: CryptoApproveAllowanceTransactionBody.newBuilder()
.nftAllowances(NftAllowance.newBuilder()
.tokenId(tokenId)
.spender(spenderId)
.owner(ownerId)
.serialNumbers(amount.longValue())
.serialNumbers(amount)
.build())
.build();
}
Expand All @@ -144,7 +143,7 @@ private TransactionBody buildCryptoApproveAllowance(CryptoApproveAllowanceTransa
}

protected @Nullable AccountID getMaybeOwnerId() {
final var nft = enhancement.nativeOperations().getNft(tokenId.tokenNum(), amount.longValue());
final var nft = enhancement.nativeOperations().getNft(tokenId.tokenNum(), amount);
if (nft == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater.Enhancement;
import com.hedera.node.app.service.contract.impl.records.ContractCallRecordBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.math.BigInteger;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.log.Log;
Expand All @@ -48,7 +47,7 @@ public ClassicGrantApprovalCall(
@NonNull final AccountID senderId,
@NonNull final TokenID token,
@NonNull final AccountID spender,
@NonNull final BigInteger amount,
final long amount,
@NonNull final TokenType tokenType) {
super(gasCalculator, enhancement, verificationStrategy, senderId, token, spender, amount, tokenType, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater.Enhancement;
import com.hedera.node.app.service.contract.impl.records.ContractCallRecordBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.math.BigInteger;
import org.hyperledger.besu.evm.frame.MessageFrame;

public class ERCGrantApprovalCall extends AbstractGrantApprovalCall {
Expand All @@ -45,7 +44,7 @@ public ERCGrantApprovalCall(
@NonNull final AccountID senderId,
@NonNull final TokenID tokenId,
@NonNull final AccountID spenderId,
@NonNull final BigInteger amount,
final long amount,
@NonNull final TokenType tokenType) {
super(gasCalculator, enhancement, verificationStrategy, senderId, tokenId, spenderId, amount, tokenType, false);
}
Expand All @@ -66,10 +65,10 @@ public PricedResult execute(@NonNull final MessageFrame frame) {
} else {
if (tokenType.equals(TokenType.NON_FUNGIBLE_UNIQUE)) {
GrantApprovalLoggingUtils.logSuccessfulNFTApprove(
tokenId, senderId, spenderId, amount.longValue(), readableAccountStore(), frame);
tokenId, senderId, spenderId, amount, readableAccountStore(), frame);
} else {
GrantApprovalLoggingUtils.logSuccessfulFTApprove(
tokenId, senderId, spenderId, amount.longValue(), readableAccountStore(), frame);
tokenId, senderId, spenderId, amount, readableAccountStore(), frame);
}
final var encodedOutput = tokenType.equals(TokenType.FUNGIBLE_COMMON)
? GrantApprovalTranslator.ERC_GRANT_APPROVAL.getOutputs().encodeElements(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.grantapproval;

import static java.util.Objects.requireNonNull;

import com.esaulpaugh.headlong.abi.Address;
import com.esaulpaugh.headlong.abi.Function;
import com.hedera.hapi.node.base.AccountID;
Expand All @@ -33,7 +35,6 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Singleton;

Expand Down Expand Up @@ -112,30 +113,35 @@ private ClassicGrantApprovalCall bodyForClassicCall(final HtsCallAttempt attempt
: GrantApprovalTranslator.GRANT_APPROVAL_NFT.decodeCall(attempt.inputBytes());
final var tokenAddress = call.get(0);
final var spender = attempt.addressIdConverter().convert(call.get(1));
final var amount = call.get(2);
final var amount = exactLongFrom(call.get(2));
return new ClassicGrantApprovalCall(
attempt.systemContractGasCalculator(),
attempt.enhancement(),
attempt.defaultVerificationStrategy(),
attempt.senderId(),
ConversionUtils.asTokenId((Address) tokenAddress),
spender,
(BigInteger) amount,
amount,
tokenType);
}

private ERCGrantApprovalCall bodyForErc(final HtsCallAttempt attempt) {
final var call = GrantApprovalTranslator.ERC_GRANT_APPROVAL.decodeCall(attempt.inputBytes());
final var spenderId = attempt.addressIdConverter().convert(call.get(0));
final var amount = call.get(1);
final var amount = exactLongFrom(call.get(1));
return new ERCGrantApprovalCall(
attempt.enhancement(),
attempt.systemContractGasCalculator(),
attempt.defaultVerificationStrategy(),
attempt.senderId(),
Objects.requireNonNull(attempt.redirectTokenId()),
requireNonNull(attempt.redirectTokenId()),
spenderId,
(BigInteger) amount,
Objects.requireNonNull(attempt.redirectTokenType()));
amount,
requireNonNull(attempt.redirectTokenType()));
}

private long exactLongFrom(@NonNull final BigInteger value) {
requireNonNull(value);
return value.longValueExact();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.grantapproval.GrantApprovalTranslator;
import com.hedera.node.app.service.contract.impl.records.ContractCallRecordBuilder;
import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase;
import java.math.BigInteger;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
Expand Down Expand Up @@ -72,7 +71,7 @@ void fungibleApprove() {
OWNER_ID,
FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.FUNGIBLE_COMMON);
given(systemContractOperations.dispatch(any(), any(), any(), any())).willReturn(recordBuilder);
given(recordBuilder.status()).willReturn(ResponseCodeEnum.SUCCESS);
Expand All @@ -95,7 +94,7 @@ void nftApprove() {
OWNER_ID,
NON_FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.NON_FUNGIBLE_UNIQUE);
given(systemContractOperations.dispatch(any(), any(), any(), any())).willReturn(recordBuilder);
given(recordBuilder.status()).willReturn(ResponseCodeEnum.SUCCESS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import com.hedera.node.app.service.contract.impl.records.ContractCallRecordBuilder;
import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase;
import com.hedera.node.app.service.token.ReadableAccountStore;
import java.math.BigInteger;
import org.apache.tuweni.units.bigints.UInt256;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.frame.MessageFrame.State;
Expand Down Expand Up @@ -89,7 +88,7 @@ void erc20approve() {
OWNER_ID,
FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.FUNGIBLE_COMMON);
given(systemContractOperations.dispatch(
any(TransactionBody.class),
Expand Down Expand Up @@ -121,7 +120,7 @@ void erc721approve() {
OWNER_ID,
NON_FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.NON_FUNGIBLE_UNIQUE);
given(systemContractOperations.dispatch(
any(TransactionBody.class),
Expand Down Expand Up @@ -156,7 +155,7 @@ void erc721approveFailsWithInvalidSpenderAllowance() {
OWNER_ID,
NON_FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.NON_FUNGIBLE_UNIQUE);
given(nativeOperations.getNft(NON_FUNGIBLE_TOKEN_ID.tokenNum(), 100L)).willReturn(nft);
given(nativeOperations.getToken(NON_FUNGIBLE_TOKEN_ID.tokenNum())).willReturn(token);
Expand All @@ -182,7 +181,7 @@ void erc721approveFailsWithSenderDoesNotOwnNFTSerialNumber() {
OWNER_ID,
NON_FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.NON_FUNGIBLE_UNIQUE);
// make sure nft is found
given(nativeOperations.getNft(NON_FUNGIBLE_TOKEN_ID.tokenNum(), 100L)).willReturn(nft);
Expand Down Expand Up @@ -213,7 +212,7 @@ void erc721approveFailsWithInvalidTokenNFTSerialNumber() {
OWNER_ID,
NON_FUNGIBLE_TOKEN_ID,
UNAUTHORIZED_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.NON_FUNGIBLE_UNIQUE);
// make sure nft is found
given(nativeOperations.getNft(NON_FUNGIBLE_TOKEN_ID.tokenNum(), 100L)).willReturn(null);
Expand All @@ -240,7 +239,7 @@ void erc721revoke() {
OWNER_ID,
NON_FUNGIBLE_TOKEN_ID,
REVOKE_APPROVAL_SPENDER_ID,
BigInteger.valueOf(100L),
100L,
TokenType.NON_FUNGIBLE_UNIQUE);
given(systemContractOperations.dispatch(
any(TransactionBody.class),
Expand Down