Skip to content

Commit 3747e61

Browse files
fix(0.53): cherrypick selfdestruct fix (#14890)
Signed-off-by: David S Bakin <[email protected]>
1 parent 559da65 commit 3747e61

File tree

5 files changed

+242
-26
lines changed

5 files changed

+242
-26
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ private void complete(@NonNull final MessageFrame frame, @NonNull final MessageF
177177
frame.setState(MessageFrame.State.CODE_EXECUTING);
178178
frame.incrementRemainingGas(childFrame.getRemainingGas());
179179
frame.addLogs(childFrame.getLogs());
180+
frame.addCreates(childFrame.getCreates());
180181
frame.addSelfDestructs(childFrame.getSelfDestructs());
181182
frame.incrementGasRefund(childFrame.getGasRefund());
182183
frame.popStackItems(getStackItemsConsumed());

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public void start(@NonNull final MessageFrame frame, @NonNull final OperationTra
9999
halt(frame, tracer, maybeReasonToHalt);
100100
} else {
101101
contract.setNonce(INITIAL_CONTRACT_NONCE);
102+
frame.addCreate(addressToCreate);
102103
frame.setState(MessageFrame.State.CODE_EXECUTING);
103104
}
104105
}

hedera-node/test-clients/SpecCookbook.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ throughout.
3131
- [DON'T extract string literals to constants, even if they are repeated](#dont-extract-string-literals-to-constants-even-if-they-are-repeated)
3232
- [DON'T add any `HapiSpecOperation` modifier not essential to the test](#dont-add-any-hapispecoperation-modifier-not-essential-to-the-test)
3333
- [DON'T start by copying a test that uses `withOpContext()`](#dont-start-by-copying-a-test-that-uses-withopcontext)
34+
- [Rare techniques](#rare-techniques)
35+
- [Predicting an entity number](#predicting-an-entity-number)
3436

3537
## Patterns
3638

@@ -266,3 +268,31 @@ Though this approach offers flexibility, it makes the resulting tests significan
266268
and search. Therefore, avoid starting by copying a test that uses `withOpContext()`. Only use this
267269
device when truly necessary, and even then, adhere to the limits prescribed by the
268270
[`@HapiTest checklist`](README.md#the-hapitest-checklist).
271+
272+
## Rare Techniques
273+
274+
### Predicting an entity number
275+
276+
It can happen you need the entity number of something created within a contract, e.g., another contract,
277+
or a token created via use of the HTS system contract. You can't actually _get_ this number from
278+
anywhere. You have to _predict_ it, knowing that entity numbers are generated in compact ascending
279+
order. The problem is that when declaring entities using the annotations (e.g., `@Account` or
280+
`@Contract`) they're actually created _lazily_ only when they're first used. So it's very fragile
281+
to know what order they're created in. For this use case (contract will create an entity) you'd really
282+
like the contract to be created last of all the entities you're declaring, then the entities it
283+
creates (when you call methods on it) can be predicted.
284+
285+
Use `SpecEntity.forceCreateAndRegister` to enumerate all the entities you're creating for the test,
286+
in the specific order you want to create them. Then you can do something to predict the _next_
287+
entity number, such as:
288+
289+
```java
290+
withOpContext((spec, opLog) -> {
291+
final var fromNum = spec.registry().getContractId(contract.name()).getContractNum();
292+
final var nextId = "0.0." + (fromNum + 1);
293+
... getContractInfo(nextId) ...
294+
})
295+
```
296+
297+
See `SelfDestructSuite` for examples, e.g., `selfDestructedContractIsDeletedInSameTx` and helper
298+
method `getNthNextContractInfoFrom`.

hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntity.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,28 @@ default void registerOrCreateWith(@NonNull final HapiSpec spec) {
7171
.registerWith(spec);
7272
}
7373

74+
/**
75+
* Given a list of entities, create them (if not already created) and register them all, in
76+
* the order listed. That is, perform {@link SpecEntity#registerOrCreateWith} for each entity given, in order.
77+
*
78+
* <p>Useful to force entities to be created at a specific time and in a specific order, if you need
79+
* to predict the Hedera entity numbers of entities dynamically created during a test; e.g., by
80+
* smart contracts which themselves create contracts, or which use HTS.
81+
*
82+
* <p>Always use with <code>&#64;LeakyHapiTest(requirement = NO_CONCURRENT_CREATIONS)</code>.
83+
*
84+
* <p>(See, e.g., {@link com.hedera.services.bdd.suites.contract.opcodes.SelfDestructSuite#selfDestructedContractIsDeletedInSameTx(String,SpecAccount,SpecContract,SpecContract)}.)
85+
*
86+
* @param spec the spec to use to create an entity if it is not already created
87+
* @param entities - the entities to create, in order
88+
*/
89+
static void forceCreateAndRegister(@NonNull final HapiSpec spec, final SpecEntity... entities) {
90+
requireNonNull(spec);
91+
for (final var entity : entities) {
92+
requireNonNull(entity).registerOrCreateWith(spec);
93+
}
94+
}
95+
7496
/**
7597
* Creates this entity with the given {@link HapiSpec}, returning the registrar
7698
* for the spec's target network.

0 commit comments

Comments
 (0)