Skip to content

Commit cf8b6ee

Browse files
chore: (Cherry-pick) Add executed_hook_id to EvmTransactionResult (#22017)
Signed-off-by: Neeharika-Sompalli <[email protected]> Signed-off-by: Michael Tinker <[email protected]> Co-authored-by: Michael Tinker <[email protected]>
1 parent 0cee20d commit cf8b6ee

File tree

43 files changed

+755
-221
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+755
-221
lines changed

hapi/hedera-protobuf-java-api/src/main/proto/services/contract_types.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ message EvmTransactionResult {
8080
* internal call producing this result.
8181
*/
8282
InternalCallContext internal_call_context = 6;
83+
84+
/**
85+
* If set, the id of the executed hook.
86+
*/
87+
proto.HookId executed_hook_id = 7;
8388
}
8489

8590
/**

hapi/hedera-protobuf-java-api/src/main/proto/services/response_code.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,11 @@ enum ResponseCodeEnum {
17711771
*/
17721772
INVALID_SERIALIZED_TX_MESSAGE_HASH_ALGORITHM = 401;
17731773

1774+
/**
1775+
* A LambdaSStore referenced an entity number but with the wrong entity type.
1776+
*/
1777+
WRONG_HOOK_ENTITY_TYPE = 499;
1778+
17741779
/**
17751780
* An EVM hook execution was throttled due to high network gas utilization.
17761781
*/

hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/contracts/HookUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public static boolean hasHookExecutions(final @NonNull CryptoTransferTransaction
109109
* @param hookEntityId the HookEntityId
110110
* @return the owner AccountID
111111
*/
112-
public static AccountID getHookOwnerId(final @NonNull HookEntityId hookEntityId) {
112+
public static AccountID getHookOwnerId(@NonNull final HookEntityId hookEntityId) {
113113
return requireNonNull(hookEntityId).hasAccountId()
114114
? hookEntityId.accountIdOrThrow()
115115
: asAccountId(hookEntityId.contractIdOrThrow());
@@ -120,7 +120,8 @@ public static AccountID getHookOwnerId(final @NonNull HookEntityId hookEntityId)
120120
* @param contractID the ContractID to convert
121121
* @return the corresponding AccountID
122122
*/
123-
private static AccountID asAccountId(final ContractID contractID) {
123+
public static AccountID asAccountId(@NonNull final ContractID contractID) {
124+
requireNonNull(contractID);
124125
return AccountID.newBuilder()
125126
.shardNum(contractID.shardNum())
126127
.realmNum(contractID.realmNum())

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/ContextTransactionProcessor.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.hedera.hapi.node.base.AccountID;
88
import com.hedera.hapi.node.base.ContractID;
99
import com.hedera.hapi.node.base.TransactionID;
10+
import com.hedera.hapi.node.hooks.HookDispatchTransactionBody;
1011
import com.hedera.hapi.streams.ContractBytecode;
1112
import com.hedera.node.app.hapi.utils.ethereum.EthTxData;
1213
import com.hedera.node.app.service.contract.impl.annotations.TransactionScope;
@@ -116,17 +117,18 @@ public CallOutcome call() {
116117
// the error.
117118
final var creation = safeCreateHevmTransaction();
118119
final var hevmTransaction = requireNonNull(creation.hevmTransaction());
119-
final var isHookDispatch = creation.isHookDispatch();
120+
final var isHookDispatch = creation.hookDispatch() != null;
120121
if (hevmTransaction.isException()) {
121122
CallOutcome outcome;
122123
if (isHookDispatch) {
123124
var result = HederaEvmTransactionResult.fromAborted(
124125
hevmTransaction.senderId(),
125126
hevmTransaction.contractId(),
126127
requireNonNull(hevmTransaction.exception()).getStatus());
128+
final var hookId = hevmTransaction.maybeHookId();
127129
outcome = CallOutcome.fromResultsWithoutSidecars(
128130
result.asProtoResultOf(null, rootProxyWorldUpdater, null),
129-
result.asEvmTxResultOf(null, null),
131+
result.asEvmTxResultOf(null, null, hookId),
130132
null,
131133
null,
132134
null,
@@ -206,12 +208,13 @@ public CallOutcome call() {
206208
requireNonNull(hederaEvmContext.streamBuilder()).addContractBytecode(contractBytecode, false);
207209
}
208210

211+
final var hookId = hevmTransaction.maybeHookId();
209212
final var callData = (hydratedEthTxData != null && hydratedEthTxData.ethTxData() != null)
210213
? Bytes.wrap(hydratedEthTxData.ethTxData().callData())
211214
: null;
212215
final var outcome = CallOutcome.fromResultsWithMaybeSidecars(
213216
result.asProtoResultOf(ethTxDataIfApplicable(), rootProxyWorldUpdater, callData),
214-
result.asEvmTxResultOf(ethTxDataIfApplicable(), callData),
217+
result.asEvmTxResultOf(ethTxDataIfApplicable(), callData, hookId),
215218
result.isSuccess() ? rootProxyWorldUpdater.getUpdatedContractNonces() : null,
216219
result.isSuccess() ? rootProxyWorldUpdater.getCreatedContractIds() : null,
217220
result.isSuccess() ? result.evmAddressIfCreatedIn(rootProxyWorldUpdater) : null,
@@ -264,16 +267,17 @@ private HevmTransactionCreationResult safeCreateHevmTransaction() {
264267
try {
265268
final var hevmTransaction = hevmTransactionFactory.fromHapiTransaction(context.body(), context.payer());
266269
validatePayloadLength(hevmTransaction);
267-
return new HevmTransactionCreationResult(hevmTransaction, hevmTransaction.hookOwnerAddress() != null);
270+
return new HevmTransactionCreationResult(hevmTransaction, hevmTransaction.hookDispatch());
268271
} catch (HandleException e) {
269272
final var evmTxn = hevmTransactionFactory.fromContractTxException(context.body(), e);
270273
// Return a HederaEvmTransaction that represents the error in order to charge fees to the sender
271-
return new HevmTransactionCreationResult(evmTxn, context.body().hasHookDispatch());
274+
return new HevmTransactionCreationResult(
275+
evmTxn, context.body().hasHookDispatch() ? context.body().hookDispatchOrThrow() : null);
272276
}
273277
}
274278

275279
private record HevmTransactionCreationResult(
276-
@Nullable HederaEvmTransaction hevmTransaction, boolean isHookDispatch) {}
280+
@Nullable HederaEvmTransaction hevmTransaction, @Nullable HookDispatchTransactionBody hookDispatch) {}
277281

278282
private void validatePayloadLength(HederaEvmTransaction hevmTransaction) {
279283
final var maxJumboEthereumCallDataSize =
@@ -306,13 +310,13 @@ private CallOutcome maybeChargeFeesAndReturnOutcome(
306310
if (context.body().hasEthereumTransaction() && sender != null) {
307311
result = result.withSignerNonce(sender.getNonce());
308312
}
309-
313+
final var hookId = hevmTransaction.maybeHookId();
310314
final var ethCallData = (hydratedEthTxData != null && hydratedEthTxData.ethTxData() != null)
311315
? Bytes.wrap(hydratedEthTxData.ethTxData().callData())
312316
: null;
313317
return CallOutcome.fromResultsWithoutSidecars(
314318
result.asProtoResultOf(ethTxDataIfApplicable(), rootProxyWorldUpdater, ethCallData),
315-
result.asEvmTxResultOf(ethTxDataIfApplicable(), ethCallData),
319+
result.asEvmTxResultOf(ethTxDataIfApplicable(), ethCallData, hookId),
316320
null,
317321
null,
318322
null,

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaOperations.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,11 @@ public void updateStorageMetadata(
304304
* {@inheritDoc}
305305
*/
306306
@Override
307-
public void updateLambdaStorageSlots(@NonNull final AccountID accountId, int netChangeInSlotsUsed) {
307+
public void updateLambdaStorageSlots(
308+
@NonNull final AccountID accountId, final int netChangeInSlotsUsed, final boolean requiresContract) {
308309
requireNonNull(accountId);
309310
final var tokenServiceApi = context.storeFactory().serviceApi(TokenServiceApi.class);
310-
tokenServiceApi.updateLambdaStorageSlots(accountId, netChangeInSlotsUsed);
311+
tokenServiceApi.updateLambdaStorageSlots(accountId, netChangeInSlotsUsed, false);
311312
}
312313

313314
/**

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HederaOperations.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,15 @@ public interface HederaOperations {
186186
* @param netChangeInSlotsUsed the net change in the number of storage slots used by the contract
187187
*/
188188
void updateStorageMetadata(ContractID contractID, @NonNull Bytes firstKey, int netChangeInSlotsUsed);
189+
189190
/**
190191
* Updates the storage slots used for the given account.
191192
*
192-
* @param accountId the id of the account
193+
* @param accountId the id of the account
193194
* @param netChangeInSlotsUsed the net change in the number of storage slots used by the account
195+
* @param requiresContract if true, the account must be a contract
194196
*/
195-
void updateLambdaStorageSlots(@NonNull AccountID accountId, int netChangeInSlotsUsed);
197+
void updateLambdaStorageSlots(@NonNull AccountID accountId, int netChangeInSlotsUsed, boolean requiresContract);
196198

197199
/**
198200
* Creates a new contract with the given entity number and EVM address; and also "links" the alias

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/QueryHederaOperations.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ public void updateStorageMetadata(
191191
}
192192

193193
@Override
194-
public void updateLambdaStorageSlots(@NonNull final AccountID accountId, final int netChangeInSlotsUsed) {
194+
public void updateLambdaStorageSlots(
195+
@NonNull final AccountID accountId, final int netChangeInSlotsUsed, boolean requiresContract) {
195196
throw new UnsupportedOperationException("Queries cannot update storage slots");
196197
}
197198

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCreateHandler.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CREATE;
55
import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS;
66
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
7+
import static com.hedera.node.app.hapi.utils.contracts.HookUtils.asAccountId;
78
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.throwIfUnsuccessfulCreate;
89
import static com.hedera.node.app.service.token.HookDispatchUtils.dispatchHookCreations;
910
import static com.hedera.node.app.service.token.HookDispatchUtils.validateHookDuplicates;
@@ -13,6 +14,7 @@
1314
import com.hedera.hapi.node.base.AccountID;
1415
import com.hedera.hapi.node.base.ContractID;
1516
import com.hedera.hapi.node.base.HederaFunctionality;
17+
import com.hedera.hapi.node.base.HookEntityId;
1618
import com.hedera.node.app.hapi.utils.fee.SigValueObj;
1719
import com.hedera.node.app.hapi.utils.fee.SmartContractFeeBuilder;
1820
import com.hedera.node.app.service.contract.impl.ContractServiceComponent;
@@ -126,16 +128,16 @@ public void preHandle(@NonNull final PreHandleContext context) throws PreCheckEx
126128
/**
127129
* If there are any hooks to create, creates them and updates the created contract to point to the first hook.
128130
* @param context the handle context
129-
* @param owner the created contract ID
131+
* @param ownerId the created contract ID
130132
*/
131-
private void createHooksIfAny(final @NonNull HandleContext context, final ContractID owner) {
133+
private void createHooksIfAny(@NonNull final HandleContext context, @NonNull final ContractID ownerId) {
132134
final var op = context.body().contractCreateInstanceOrThrow();
133135
if (!op.hookCreationDetails().isEmpty()) {
134136
final var accountStore = context.storeFactory().readableStore(ReadableAccountStore.class);
135-
final var created = requireNonNull(accountStore.getContractById(owner));
136-
137-
final var deltaSlots = dispatchHookCreations(context, op.hookCreationDetails(), null, created.accountId());
138-
137+
final var hookEntityId =
138+
HookEntityId.newBuilder().accountId(asAccountId(ownerId)).build();
139+
final var deltaSlots = dispatchHookCreations(context, op.hookCreationDetails(), null, hookEntityId);
140+
final var created = requireNonNull(accountStore.getContractById(ownerId));
139141
final var updated = created.copyBuilder()
140142
.firstHookId(op.hookCreationDetails().getFirst().hookId())
141143
.numberHooksInUse(op.hookCreationDetails().size())

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractUpdateHandler.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.hedera.hapi.node.base.AccountID;
2525
import com.hedera.hapi.node.base.ContractID;
2626
import com.hedera.hapi.node.base.HederaFunctionality;
27+
import com.hedera.hapi.node.base.HookEntityId;
2728
import com.hedera.hapi.node.base.Key;
2829
import com.hedera.hapi.node.base.SubType;
2930
import com.hedera.hapi.node.contract.ContractUpdateTransactionBody;
@@ -157,20 +158,24 @@ public void updateHooks(
157158
@NonNull final ContractUpdateTransactionBody op,
158159
@NonNull final Account.Builder builder,
159160
@NonNull final Account originalAccount) {
160-
long headAfterDeletes = originalAccount.firstHookId();
161-
// Dispatch all the hooks to delete
161+
// We persist both account and storage hooks with AccountID entity type internally
162+
final var hookEntityId = HookEntityId.newBuilder()
163+
.accountId(originalAccount.accountIdOrThrow())
164+
.build();
165+
Long headAfterDeletes = originalAccount.numberHooksInUse() == 0 ? null : originalAccount.firstHookId();
162166
if (!op.hookIdsToDelete().isEmpty()) {
163-
HookDispatchUtils.dispatchHookDeletions(
164-
context, op.hookIdsToDelete(), headAfterDeletes, originalAccount.accountIdOrThrow());
167+
headAfterDeletes = HookDispatchUtils.dispatchHookDeletions(
168+
context, op.hookIdsToDelete(), headAfterDeletes, hookEntityId);
165169
}
166170
if (!op.hookCreationDetails().isEmpty()) {
167171
final var numSlotsUpdated = HookDispatchUtils.dispatchHookCreations(
168-
context, op.hookCreationDetails(), headAfterDeletes, originalAccount.accountId());
172+
context, op.hookCreationDetails(), headAfterDeletes, hookEntityId);
169173
builder.firstHookId(op.hookCreationDetails().getFirst().hookId());
170174
final var currentSlots = originalAccount.numberLambdaStorageSlots() + numSlotsUpdated;
171175
builder.numberLambdaStorageSlots(currentSlots);
172176
} else if (!op.hookIdsToDelete().isEmpty()) {
173-
builder.firstHookId(headAfterDeletes);
177+
// If numberLambdaStorageSlots == 0 after deletions, then first hook id is meaningless but set to 0
178+
builder.firstHookId(headAfterDeletes == null ? 0 : headAfterDeletes);
174179
}
175180
if (!op.hookCreationDetails().isEmpty() || !op.hookIdsToDelete().isEmpty()) {
176181
final var current = originalAccount.numberHooksInUse();

hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/HookDispatchHandler.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.hedera.node.app.service.contract.impl.state.WritableEvmHookStore;
2525
import com.hedera.node.app.service.contract.impl.state.hooks.HookEvmFrameStateFactory;
2626
import com.hedera.node.app.service.entityid.EntityIdFactory;
27-
import com.hedera.node.app.service.token.api.TokenServiceApi;
2827
import com.hedera.node.app.service.token.records.HookDispatchStreamBuilder;
2928
import com.hedera.node.app.spi.fees.FeeContext;
3029
import com.hedera.node.app.spi.fees.Fees;
@@ -112,7 +111,6 @@ public void handle(@NonNull final HandleContext context) throws HandleException
112111
final var hook = evmHookStore.getEvmHook(hookKey);
113112
validateTrue(hook != null, HOOK_NOT_FOUND);
114113

115-
final var tokenServiceApi = context.storeFactory().serviceApi(TokenServiceApi.class);
116114
// Build the strategy that will produce a HookEvmFrameStateFactory for this transaction
117115
final EvmFrameStates evmFrameStates = (ops, nativeOps, codeFactory) ->
118116
new HookEvmFrameStateFactory(ops, nativeOps, codeFactory, hook);

0 commit comments

Comments
 (0)