Repository: hashgraph/hedera-services
Issue: ContractCallLocalQuery does not use sender_id field

### Description

The `ContractCallLocalQuery` call does not use the sender_id, instead it always uses the context of the transaction caller for the query.

Also, the accounts should be de-aliased.

### Steps to reproduce

Pseudo code...
```
var call = new ContractCallQuery()
    .setFunction("theFunction")
    .setContractId(theContractId)
    .setGas(400000)
    .setSenderAccountId(senderAccountId)
    .execute(clientWithOperatorNotSenderId);
```
For this the senderAcountId will not be the 'tx.origin' but the operator in the client will be the operator.

### Additional context

_No response_

### Hedera network

mainnet, testnet, previewnet

### Version

0.25, 0.26

### Operating system

_No response_

Changed files:
hedera-node/src/test/java/com/hedera/services/utils/EntityIdUtilsTest.java
hedera-node/src/test/java/com/hedera/services/contracts/execution/CallLocalExecutorTest.java
hedera-node/src/test/java/com/hedera/services/fees/calculation/contract/queries/ContractCallLocalResourceUsageTest.java
hedera-node/src/main/java/com/hedera/services/contracts/execution/CallLocalExecutor.java
hedera-node/src/main/java/com/hedera/services/utils/EntityIdUtils.java

diff --git a/hedera-node/src/main/java/com/hedera/services/contracts/execution/CallLocalExecutor.java b/hedera-node/src/main/java/com/hedera/services/contracts/execution/CallLocalExecutor.java
index c799d25f74..ee889d1dc0 100644
--- a/hedera-node/src/main/java/com/hedera/services/contracts/execution/CallLocalExecutor.java
+++ b/hedera-node/src/main/java/com/hedera/services/contracts/execution/CallLocalExecutor.java
@@ -26,7 +26,6 @@ import com.hedera.services.ledger.accounts.AliasManager;
 import com.hedera.services.store.AccountStore;
 import com.hedera.services.store.contracts.EntityAccess;
 import com.hedera.services.store.models.Account;
-import com.hedera.services.store.models.Id;
 import com.hedera.services.utils.EntityIdUtils;
 import com.hedera.services.utils.ResponseCodeUtil;
 import com.hedera.services.utils.accessors.SignedTxnAccessor;
@@ -74,7 +73,9 @@ public class CallLocalExecutor {
 	) {
 		try {
 			final var paymentTxn = SignedTxnAccessor.uncheckedFrom(op.getHeader().getPayment()).getTxn();
-			final var senderId = Id.fromGrpcAccount(paymentTxn.getTransactionID().getAccountID());
+			final var senderId = EntityIdUtils.unaliased(op.hasSenderId()
+					? op.getSenderId()
+					: paymentTxn.getTransactionID().getAccountID(), aliasManager).toId();
 			final var idOrAlias = op.getContractID();
 			final var contractId = EntityIdUtils.unaliased(idOrAlias, aliasManager).toId();
 
diff --git a/hedera-node/src/main/java/com/hedera/services/utils/EntityIdUtils.java b/hedera-node/src/main/java/com/hedera/services/utils/EntityIdUtils.java
index 0bb0dac37c..e795c2467d 100644
--- a/hedera-node/src/main/java/com/hedera/services/utils/EntityIdUtils.java
+++ b/hedera-node/src/main/java/com/hedera/services/utils/EntityIdUtils.java
@@ -357,4 +357,29 @@ public final class EntityIdUtils {
 			return EntityNum.fromContractId(idOrAlias);
 		}
 	}
+
+	public static EntityNum unaliased(final AccountID idOrAlias, final AliasManager aliasManager) {
+		return unaliased(idOrAlias, aliasManager, null);
+	}
+
+	public static EntityNum unaliased(
+			final AccountID idOrAlias,
+			final AliasManager aliasManager,
+			@Nullable final Consumer<ByteString> aliasObs
+	) {
+		if (isAlias(idOrAlias)) {
+			final var alias = idOrAlias.getAlias();
+			final var evmAddress = alias.toByteArray();
+			if (aliasManager.isMirror(evmAddress)) {
+				final var accountNum = Longs.fromByteArray(Arrays.copyOfRange(evmAddress, 12, 20));
+				return EntityNum.fromLong(accountNum);
+			}
+			if (aliasObs != null) {
+				aliasObs.accept(alias);
+			}
+			return aliasManager.lookupIdBy(alias);
+		} else {
+			return EntityNum.fromAccountId(idOrAlias);
+		}
+	}
 }
diff --git a/hedera-node/src/test/java/com/hedera/services/contracts/execution/CallLocalExecutorTest.java b/hedera-node/src/test/java/com/hedera/services/contracts/execution/CallLocalExecutorTest.java
index a7f34ea9dd..5ccb96e26a 100644
--- a/hedera-node/src/test/java/com/hedera/services/contracts/execution/CallLocalExecutorTest.java
+++ b/hedera-node/src/test/java/com/hedera/services/contracts/execution/CallLocalExecutorTest.java
@@ -29,6 +29,7 @@ import com.hedera.services.store.contracts.EntityAccess;
 import com.hedera.services.store.models.Account;
 import com.hedera.services.store.models.Id;
 import com.hedera.services.utils.EntityNum;
+import com.hederahashgraph.api.proto.java.AccountID;
 import com.hederahashgraph.api.proto.java.ContractCallLocalQuery;
 import com.hederahashgraph.api.proto.java.ContractCallLocalResponse;
 import com.hederahashgraph.api.proto.java.ContractID;
@@ -69,7 +70,8 @@ class CallLocalExecutorTest {
 	ByteString params = ByteString.copyFrom("Hungry, and...".getBytes());
 	Id callerID = new Id(0, 0, 123);
 	Id contractID = new Id(0, 0, 456);
-
+	Id senderID = new Id(0, 0, 789);
+	
 	ContractCallLocalQuery query;
 
 	@Mock
@@ -114,6 +116,34 @@ class CallLocalExecutorTest {
 		assertEquals(expected, result);
 	}
 
+	@Test
+	void processingSuccessfulWithAccountAlias() {
+		// setup:
+		final var senderAlias = CommonUtils.unhex("6aea3773ea468a814d954e6dec795bfee7d76e25");
+		final var sender = AccountID.newBuilder()
+				.setAlias(ByteString.copyFrom(senderAlias))
+				.build();
+		query = localCallQuery(contractID.asGrpcContract(), sender, ANSWER_ONLY);
+		given(aliasManager.lookupIdBy(sender.getAlias())).willReturn(EntityNum.fromLong(senderID.num()));
+
+		final var transactionProcessingResult = TransactionProcessingResult
+				.successful(new ArrayList<>(), 0, 0, 1, Bytes.EMPTY,
+						callerID.asEvmAddress(), new TreeMap<>());
+		final var expected = response(OK,transactionProcessingResult);
+
+		given(accountStore.loadAccount(any())).willReturn(new Account(callerID));
+		given(accountStore.loadContract(contractID)).willReturn(new Account(contractID));
+		given(evmTxProcessor.execute(any(), any(), anyLong(), anyLong(), any(), any()))
+				.willReturn(transactionProcessingResult);
+
+		// when:
+		final var result =
+				CallLocalExecutor.execute(accountStore, evmTxProcessor, query, aliasManager, entityAccess);
+
+		// then:
+		assertEquals(expected, result);
+	}
+
 	@Test
 	void processingSuccessful() {
 		// setup:
@@ -256,4 +286,16 @@ class CallLocalExecutorTest {
 						.build())
 				.build();
 	}
+
+	private ContractCallLocalQuery localCallQuery(ContractID id, AccountID sender, ResponseType type) {
+		return ContractCallLocalQuery.newBuilder()
+				.setContractID(id)
+				.setGas(gas)
+				.setFunctionParameters(params)
+				.setHeader(QueryHeader.newBuilder()
+						.setResponseType(type)
+						.build())
+				.setSenderId(sender)
+				.build();
+	}
 }
diff --git a/hedera-node/src/test/java/com/hedera/services/fees/calculation/contract/queries/ContractCallLocalResourceUsageTest.java b/hedera-node/src/test/java/com/hedera/services/fees/calculation/contract/queries/ContractCallLocalResourceUsageTest.java
index b3c25ffa0b..dba7537c68 100644
--- a/hedera-node/src/test/java/com/hedera/services/fees/calculation/contract/queries/ContractCallLocalResourceUsageTest.java
+++ b/hedera-node/src/test/java/com/hedera/services/fees/calculation/contract/queries/ContractCallLocalResourceUsageTest.java
@@ -40,6 +40,7 @@ import com.hedera.test.extensions.LogCaptor;
 import com.hedera.test.extensions.LogCaptureExtension;
 import com.hedera.test.extensions.LoggingSubject;
 import com.hedera.test.extensions.LoggingTarget;
+import com.hederahashgraph.api.proto.java.AccountID;
 import com.hederahashgraph.api.proto.java.ContractCallLocalQuery;
 import com.hederahashgraph.api.proto.java.ContractCallLocalResponse;
 import com.hederahashgraph.api.proto.java.ContractID;
diff --git a/hedera-node/src/test/java/com/hedera/services/utils/EntityIdUtilsTest.java b/hedera-node/src/test/java/com/hedera/services/utils/EntityIdUtilsTest.java
index 7d1e7682c6..2886b89c34 100644
--- a/hedera-node/src/test/java/com/hedera/services/utils/EntityIdUtilsTest.java
+++ b/hedera-node/src/test/java/com/hedera/services/utils/EntityIdUtilsTest.java
@@ -45,6 +45,7 @@ import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static com.hedera.services.utils.EntityIdUtils.asEvmAddress;
 import static com.hedera.services.utils.EntityIdUtils.asLiteralString;
@@ -96,6 +97,47 @@ class EntityIdUtilsTest {
 		assertEquals(extantNum, unaliased(input, aliasManager));
 	}
 
+	@Test
+	void echoesUnaliasedAccountId() {
+		final var literalId = AccountID.newBuilder().setAccountNum(1234).build();
+
+		assertEquals(EntityNum.fromLong(1234), unaliased(literalId, aliasManager));
+		assertEquals(EntityNum.MISSING_NUM, unaliased(AccountID.getDefaultInstance(), aliasManager));
+	}
+
+	@Test
+	void useAliasDirectlyIfMirror() {
+		final byte[] mockAddr = unhex("0000000000000000000000009abcdefabcdefbbb");
+		final var num = Longs.fromByteArray(Arrays.copyOfRange(mockAddr, 12, 20));
+		final var expectedId = EntityNum.fromLong(num);
+		final var input = AccountID.newBuilder().setAlias(ByteString.copyFrom(mockAddr)).build();
+
+		given(aliasManager.isMirror(mockAddr)).willReturn(true);
+		assertEquals(expectedId, unaliased(input, aliasManager));
+	}
+
+	@Test
+	void returnsResolvedAccountIdIfNonMirro() {
+		final byte[] mockAddr = unhex("aaaaaaaaaaaaaaaaaaaaaaaa9abcdefabcdefbbb");
+		final var extantNum = EntityNum.fromLong(1_234_567L);
+		final var input = AccountID.newBuilder().setAlias(ByteString.copyFrom(mockAddr)).build();
+		given(aliasManager.lookupIdBy(ByteString.copyFrom(mockAddr))).willReturn(extantNum);
+
+		assertEquals(extantNum, unaliased(input, aliasManager));
+	}
+
+	@Test
+	void observesUnalising() {
+		final byte[] mockAddr = unhex("aaaaaaaaaaaaaaaaaaaaaaaa9abcdefabcdefbbb");
+		final var extantNum = EntityNum.fromLong(1_234_567L);
+		final var input = AccountID.newBuilder().setAlias(ByteString.copyFrom(mockAddr)).build();
+		given(aliasManager.lookupIdBy(ByteString.copyFrom(mockAddr))).willReturn(extantNum);
+
+		AtomicReference<ByteString> observer = new AtomicReference<>();
+		unaliased(input, aliasManager, observer::set);
+		assertEquals(ByteString.copyFrom(mockAddr), observer.get()); 
+	}
+
 	@Test
 	void correctLiteral() {
 		assertEquals("1.2.3", asLiteralString(asAccount("1.2.3")));