Skip to content

Commit

Permalink
fix(0.53): cherrypick selfdestruct fix (#14890)
Browse files Browse the repository at this point in the history
Signed-off-by: David S Bakin <117694041+david-bakin-sl@users.noreply.github.com>
  • Loading branch information
david-bakin-sl authored Aug 16, 2024
1 parent 559da65 commit 3747e61
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ private void complete(@NonNull final MessageFrame frame, @NonNull final MessageF
frame.setState(MessageFrame.State.CODE_EXECUTING);
frame.incrementRemainingGas(childFrame.getRemainingGas());
frame.addLogs(childFrame.getLogs());
frame.addCreates(childFrame.getCreates());
frame.addSelfDestructs(childFrame.getSelfDestructs());
frame.incrementGasRefund(childFrame.getGasRefund());
frame.popStackItems(getStackItemsConsumed());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public void start(@NonNull final MessageFrame frame, @NonNull final OperationTra
halt(frame, tracer, maybeReasonToHalt);
} else {
contract.setNonce(INITIAL_CONTRACT_NONCE);
frame.addCreate(addressToCreate);
frame.setState(MessageFrame.State.CODE_EXECUTING);
}
}
Expand Down
30 changes: 30 additions & 0 deletions hedera-node/test-clients/SpecCookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ throughout.
- [DON'T extract string literals to constants, even if they are repeated](#dont-extract-string-literals-to-constants-even-if-they-are-repeated)
- [DON'T add any `HapiSpecOperation` modifier not essential to the test](#dont-add-any-hapispecoperation-modifier-not-essential-to-the-test)
- [DON'T start by copying a test that uses `withOpContext()`](#dont-start-by-copying-a-test-that-uses-withopcontext)
- [Rare techniques](#rare-techniques)
- [Predicting an entity number](#predicting-an-entity-number)

## Patterns

Expand Down Expand Up @@ -266,3 +268,31 @@ Though this approach offers flexibility, it makes the resulting tests significan
and search. Therefore, avoid starting by copying a test that uses `withOpContext()`. Only use this
device when truly necessary, and even then, adhere to the limits prescribed by the
[`@HapiTest checklist`](README.md#the-hapitest-checklist).

## Rare Techniques

### Predicting an entity number

It can happen you need the entity number of something created within a contract, e.g., another contract,
or a token created via use of the HTS system contract. You can't actually _get_ this number from
anywhere. You have to _predict_ it, knowing that entity numbers are generated in compact ascending
order. The problem is that when declaring entities using the annotations (e.g., `@Account` or
`@Contract`) they're actually created _lazily_ only when they're first used. So it's very fragile
to know what order they're created in. For this use case (contract will create an entity) you'd really
like the contract to be created last of all the entities you're declaring, then the entities it
creates (when you call methods on it) can be predicted.

Use `SpecEntity.forceCreateAndRegister` to enumerate all the entities you're creating for the test,
in the specific order you want to create them. Then you can do something to predict the _next_
entity number, such as:

```java
withOpContext((spec, opLog) -> {
final var fromNum = spec.registry().getContractId(contract.name()).getContractNum();
final var nextId = "0.0." + (fromNum + 1);
... getContractInfo(nextId) ...
})
```

See `SelfDestructSuite` for examples, e.g., `selfDestructedContractIsDeletedInSameTx` and helper
method `getNthNextContractInfoFrom`.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ default void registerOrCreateWith(@NonNull final HapiSpec spec) {
.registerWith(spec);
}

/**
* Given a list of entities, create them (if not already created) and register them all, in
* the order listed. That is, perform {@link SpecEntity#registerOrCreateWith} for each entity given, in order.
*
* <p>Useful to force entities to be created at a specific time and in a specific order, if you need
* to predict the Hedera entity numbers of entities dynamically created during a test; e.g., by
* smart contracts which themselves create contracts, or which use HTS.
*
* <p>Always use with <code>&#64;LeakyHapiTest(requirement = NO_CONCURRENT_CREATIONS)</code>.
*
* <p>(See, e.g., {@link com.hedera.services.bdd.suites.contract.opcodes.SelfDestructSuite#selfDestructedContractIsDeletedInSameTx(String,SpecAccount,SpecContract,SpecContract)}.)
*
* @param spec the spec to use to create an entity if it is not already created
* @param entities - the entities to create, in order
*/
static void forceCreateAndRegister(@NonNull final HapiSpec spec, final SpecEntity... entities) {
requireNonNull(spec);
for (final var entity : entities) {
requireNonNull(entity).registerOrCreateWith(spec);
}
}

/**
* Creates this entity with the given {@link HapiSpec}, returning the registrar
* for the spec's target network.
Expand Down
Loading

0 comments on commit 3747e61

Please sign in to comment.