Skip to content

Commit

Permalink
Support EVM Address in CryptoTransferTransactionBody (#3656)
Browse files Browse the repository at this point in the history
Support create2 evm contract lookup

Signed-off-by: Edwin Greene <edwin.greene@hedera.com>
  • Loading branch information
edwin-greene authored Apr 27, 2022
1 parent b1ce909 commit 952a277
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
@UtilityClass
public class DomainUtils {

private static final int EVM_ADDRESS_LENGTH = 20;
public static final int EVM_ADDRESS_LENGTH = 20;
private static final long NANOS_PER_SECOND = 1_000_000_000L;
private static final char NULL_CHARACTER = (char) 0;
private static final char NULL_REPLACEMENT = '�'; // Standard replacement character 0xFFFD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ private EntityId load(AccountID accountId) {
return EntityId.of(accountId);
case ALIAS:
byte[] alias = DomainUtils.toBytes(accountId.getAlias());
return entityRepository.findByAlias(alias)
.map(id -> EntityId.of(id, ACCOUNT))
.orElse(null);
return alias.length == DomainUtils.EVM_ADDRESS_LENGTH ?
findByEvmAddress(alias, accountId.getShardNum(), accountId.getRealmNum()) :
entityRepository.findByAlias(alias)
.map(id -> EntityId.of(id, ACCOUNT))
.orElse(null);
default:
throw new InvalidDatasetException("Invalid AccountID: " + accountId);
}
Expand All @@ -165,17 +167,17 @@ private EntityId load(ContractID contractId) {
case CONTRACTNUM:
return EntityId.of(contractId);
case EVM_ADDRESS:
return findByEvmAddress(contractId);
byte[] evmAddress = DomainUtils.toBytes(contractId.getEvmAddress());
return findByEvmAddress(evmAddress, contractId.getShardNum(), contractId.getRealmNum());
default:
throw new InvalidDatasetException("Invalid ContractID: " + contractId);
}
}

private EntityId findByEvmAddress(ContractID contractId) {
byte[] evmAddress = DomainUtils.toBytes(contractId.getEvmAddress());
private EntityId findByEvmAddress(byte[] evmAddress, long shardNum, long realmNum) {
return Optional.ofNullable(DomainUtils.fromEvmAddress(evmAddress))
// Verify shard and realm match when assuming evmAddress is in the 'shard.realm.num' form
.filter(e -> e.getShardNum() == contractId.getShardNum() && e.getRealmNum() == contractId.getRealmNum())
.filter(e -> e.getShardNum() == shardNum && e.getRealmNum() == realmNum)
.or(() -> contractRepository.findByEvmAddress(evmAddress).map(id -> EntityId.of(id, CONTRACT)))
.orElse(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ void storeNull() {
assertDoesNotThrow(() -> entityIdService.notify(null));
}

@Test
void lookupAccountWithEvmAddress() {
AccountID accountId = AccountID.newBuilder()
.setAlias(DomainUtils.fromBytes(PARSABLE_EVM_ADDRESS))
.build();
assertThat(entityIdService.lookup(accountId)).isEqualTo(EntityId.of(100, CONTRACT));
}

private AccountID getProtoAccountId(Entity account) {
var accountId = AccountID.newBuilder()
.setShardNum(account.getShard())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EnumSource;

import com.hedera.mirror.common.domain.contract.Contract;
import com.hedera.mirror.common.domain.entity.Entity;
import com.hedera.mirror.common.domain.entity.EntityId;
import com.hedera.mirror.common.domain.token.Nft;
Expand All @@ -82,6 +83,7 @@
import com.hedera.mirror.importer.exception.AliasNotFoundException;
import com.hedera.mirror.importer.parser.PartialDataAction;
import com.hedera.mirror.importer.parser.record.RecordParserProperties;
import com.hedera.mirror.importer.repository.ContractRepository;
import com.hedera.mirror.importer.repository.CryptoAllowanceRepository;
import com.hedera.mirror.importer.repository.NftAllowanceRepository;
import com.hedera.mirror.importer.repository.NftRepository;
Expand All @@ -96,6 +98,9 @@ class EntityRecordItemListenerCryptoTest extends AbstractEntityRecordItemListene
private static final ByteString ALIAS_KEY = ByteString.copyFromUtf8(
"0a2212200aa8e21064c61eab86e2a9c164565b4e7a9a4146106e0a6cd03a8c395a110fff");

@Resource
private ContractRepository contractRepository;

@Resource
private CryptoAllowanceRepository cryptoAllowanceRepository;

Expand Down Expand Up @@ -809,6 +814,35 @@ void cryptoTransferWithAlias() {
);
}

@Test
void cryptoTransferWithEvmAddressAlias() {
Contract contract = domainBuilder.contract().persist();
assertThat(contractRepository.findByEvmAddress(contract.getEvmAddress())).isPresent();

entityProperties.getPersist().setNonFeeTransfers(true);

long transferAmount = 123;
var transfer1 = accountAliasAmount(DomainUtils.fromBytes(contract.getEvmAddress()), transferAmount).build();
Transaction transaction = buildTransaction(builder -> builder.getCryptoTransferBuilder().getTransfersBuilder()
.addAccountAmounts(transfer1));
TransactionBody transactionBody = getTransactionBody(transaction);
TransactionRecord transactionRecord = transactionRecordSuccess(transactionBody);
RecordItem recordItem = new RecordItem(transaction, transactionRecord);
parseRecordItemAndCommit(recordItem);

assertAll(
() -> assertEquals(1, transactionRepository.count()),
() -> assertEquals(1, nonFeeTransferRepository.count()),
() -> assertTransactionAndRecord(transactionBody, transactionRecord),
() -> assertThat(findNonFeeTransfers())
.allSatisfy(nonFeeTransfer -> {
assertThat(nonFeeTransfer.getEntityId()).isEqualTo(contract.toEntityId());
assertThat(nonFeeTransfer.getAmount()).isEqualTo(transferAmount);
assertThat(nonFeeTransfer.getPayerAccountId()).isEqualTo(recordItem.getPayerAccountId());
})
);
}

private Condition<CryptoTransfer> isAccountAmountReceiverAccountAmount(AccountAmount receiver) {
return new Condition<>(
cryptoTransfer ->
Expand Down

0 comments on commit 952a277

Please sign in to comment.