Skip to content

Commit 893c0b6

Browse files
authored
Merge branch 'develop' into cherry-pick-12444
2 parents dc8ef5f + 42e87a9 commit 893c0b6

File tree

65 files changed

+1830
-894
lines changed

Some content is hidden

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

65 files changed

+1830
-894
lines changed

hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/HederaSoftwareVersion.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,19 @@ public class HederaSoftwareVersion implements SoftwareVersion {
4242

4343
public static final long CLASS_ID = 0x6f2b1bc2df8cbd0cL;
4444
public static final int RELEASE_027_VERSION = 1;
45+
public static final int RELEASE_048_VERSION = 2;
4546
public static final Pattern ALPHA_PRE_PATTERN = Pattern.compile("alpha[.](\\d+)");
4647

4748
private int configVersion;
4849
private SemanticVersion hapiVersion;
4950
private SemanticVersion servicesVersion;
5051

52+
/**
53+
* The version of this object that was deserialized. When serializing a software version, we need to write it
54+
* to the stream using the same format as when it was deserialized.
55+
*/
56+
private int deserializedVersion = RELEASE_048_VERSION;
57+
5158
public HederaSoftwareVersion() {
5259
// For ConstructableRegistry. Do not use.
5360
}
@@ -74,7 +81,7 @@ public long getClassId() {
7481

7582
@Override
7683
public int getVersion() {
77-
return RELEASE_027_VERSION;
84+
return deserializedVersion;
7885
}
7986

8087
@Override
@@ -100,9 +107,14 @@ public int compareTo(@NonNull final SoftwareVersion other) {
100107
}
101108

102109
@Override
103-
public void deserialize(SerializableDataInputStream in, int i) throws IOException {
110+
public void deserialize(@NonNull final SerializableDataInputStream in, final int version) throws IOException {
111+
deserializedVersion = version;
112+
104113
hapiVersion = deserializeSemVer(in);
105114
servicesVersion = deserializeSemVer(in);
115+
if (version >= RELEASE_048_VERSION) {
116+
configVersion = in.readInt();
117+
}
106118
}
107119

108120
private static SemanticVersion deserializeSemVer(final SerializableDataInputStream in) throws IOException {
@@ -121,6 +133,9 @@ private static SemanticVersion deserializeSemVer(final SerializableDataInputStre
121133
public void serialize(SerializableDataOutputStream out) throws IOException {
122134
serializeSemVer(hapiVersion, out);
123135
serializeSemVer(servicesVersion, out);
136+
if (deserializedVersion >= RELEASE_048_VERSION) {
137+
out.writeInt(configVersion);
138+
}
124139
}
125140

126141
private static void serializeSemVer(final SemanticVersion semVer, final SerializableDataOutputStream out)
@@ -161,7 +176,7 @@ public String toString() {
161176
// This is called by the platform when printing information on saved states to logs
162177
return "HederaSoftwareVersion{" + "hapiVersion="
163178
+ HapiUtils.toString(hapiVersion) + ", servicesVersion="
164-
+ HapiUtils.toString(servicesVersion) + '}';
179+
+ HapiUtils.toString(servicesVersion) + (configVersion == 0 ? "" : "-c" + configVersion) + '}';
165180
}
166181

167182
/**

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java

+17-19
Original file line numberDiff line numberDiff line change
@@ -829,25 +829,6 @@ private ValidationResult validate(
829829
}
830830
}
831831

832-
// verify all the keys
833-
for (final var key : preHandleResult.getRequiredKeys()) {
834-
final var verification = verifier.verificationFor(key);
835-
if (verification.failed()) {
836-
utilizationManager.trackFeePayments(consensusNow, state);
837-
final var fees = dispatcher.dispatchComputeFees(context);
838-
return new ValidationResult(PRE_HANDLE_FAILURE, INVALID_SIGNATURE, fees);
839-
}
840-
}
841-
// If there are any hollow accounts whose signatures need to be verified, verify them
842-
for (final var hollowAccount : preHandleResult.getHollowAccounts()) {
843-
final var verification = verifier.verificationFor(hollowAccount.alias());
844-
if (verification.failed()) {
845-
utilizationManager.trackFeePayments(consensusNow, state);
846-
final var fees = dispatcher.dispatchComputeFees(context);
847-
return new ValidationResult(PRE_HANDLE_FAILURE, INVALID_SIGNATURE, fees);
848-
}
849-
}
850-
851832
// Notice that above, we computed fees assuming network utilization for
852833
// just a fee payment. Here we instead calculate fees based on tracking the
853834
// user transaction. This is for mono-service fidelity, but does not have any
@@ -909,6 +890,23 @@ private ValidationResult validate(
909890
return new ValidationResult(PRE_HANDLE_FAILURE, ENTITY_NOT_ALLOWED_TO_DELETE, fees);
910891
}
911892

893+
// verify all the keys
894+
for (final var key : preHandleResult.getRequiredKeys()) {
895+
final var verification = verifier.verificationFor(key);
896+
if (verification.failed()) {
897+
utilizationManager.trackFeePayments(consensusNow, state);
898+
return new ValidationResult(PRE_HANDLE_FAILURE, INVALID_SIGNATURE, fees);
899+
}
900+
}
901+
// If there are any hollow accounts whose signatures need to be verified, verify them
902+
for (final var hollowAccount : preHandleResult.getHollowAccounts()) {
903+
final var verification = verifier.verificationFor(hollowAccount.alias());
904+
if (verification.failed()) {
905+
utilizationManager.trackFeePayments(consensusNow, state);
906+
return new ValidationResult(PRE_HANDLE_FAILURE, INVALID_SIGNATURE, fees);
907+
}
908+
}
909+
912910
return new ValidationResult(SO_FAR_SO_GOOD, OK, fees);
913911
}
914912

hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java

+75
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,24 @@
1616

1717
package com.hedera.node.app.version;
1818

19+
import static com.hedera.node.app.version.HederaSoftwareVersion.RELEASE_027_VERSION;
1920
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertNotNull;
2024

2125
import com.hedera.hapi.node.base.SemanticVersion;
2226
import com.hedera.node.config.converter.SemanticVersionConverter;
27+
import com.swirlds.common.constructable.ClassConstructorPair;
28+
import com.swirlds.common.constructable.ConstructableRegistry;
29+
import com.swirlds.common.constructable.ConstructableRegistryException;
2330
import com.swirlds.common.io.streams.SerializableDataInputStream;
2431
import com.swirlds.common.io.streams.SerializableDataOutputStream;
2532
import edu.umd.cs.findbugs.annotations.NonNull;
2633
import java.io.ByteArrayInputStream;
2734
import java.io.ByteArrayOutputStream;
2835
import java.io.IOException;
36+
import java.io.InputStream;
2937
import java.util.ArrayList;
3038
import java.util.Collections;
3139
import java.util.Random;
@@ -66,6 +74,73 @@ void compareTo(@NonNull final String a, @NonNull final String b, final String ex
6674
}
6775
}
6876

77+
@Test
78+
void serializationRoundTripWithConfigVersionTest() throws IOException, ConstructableRegistryException {
79+
ConstructableRegistry.getInstance()
80+
.registerConstructable(
81+
new ClassConstructorPair(HederaSoftwareVersion.class, HederaSoftwareVersion::new));
82+
83+
final HederaSoftwareVersion v1 = new HederaSoftwareVersion(
84+
new SemanticVersion(0, 48, 0, "alpha.5", ""), new SemanticVersion(0, 48, 0, "", ""), 1);
85+
86+
final HederaSoftwareVersion v2 = new HederaSoftwareVersion(
87+
new SemanticVersion(0, 48, 0, "alpha.5", ""), new SemanticVersion(0, 48, 0, "", ""), 1);
88+
89+
assertEquals(0, v1.compareTo(v2));
90+
91+
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
92+
final SerializableDataOutputStream out = new SerializableDataOutputStream(byteOut);
93+
out.writeSerializable(v1, true);
94+
95+
final SerializableDataInputStream in =
96+
new SerializableDataInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
97+
final HederaSoftwareVersion v3 = in.readSerializable();
98+
99+
assertEquals(0, v1.compareTo(v3));
100+
}
101+
102+
@Test
103+
void byteFormatDoesNotChangeAfterMigration() throws IOException, ConstructableRegistryException {
104+
ConstructableRegistry.getInstance()
105+
.registerConstructable(
106+
new ClassConstructorPair(HederaSoftwareVersion.class, HederaSoftwareVersion::new));
107+
108+
/*
109+
// The following code was used to generate the serialized software version on disk.
110+
// File was generated using the branch release/0.47.
111+
112+
final HederaSoftwareVersion version = new HederaSoftwareVersion(semver("1.2.3"), semver("4.5.6"));
113+
final FileOutputStream fos = new FileOutputStream("hederaSoftwareVersion_27.dat");
114+
final SerializableDataOutputStream out = new SerializableDataOutputStream(fos);
115+
out.writeSerializable(version, true);
116+
out.close();
117+
*/
118+
119+
final byte[] legacyBytes;
120+
try (final InputStream legacyFile =
121+
HederaSoftwareVersion.class.getClassLoader().getResourceAsStream("hederaSoftwareVersion_27.dat")) {
122+
assertNotNull(legacyFile);
123+
legacyBytes = legacyFile.readAllBytes();
124+
}
125+
126+
final SerializableDataInputStream legacyIn =
127+
new SerializableDataInputStream(new ByteArrayInputStream(legacyBytes));
128+
final HederaSoftwareVersion deserializedVersion = legacyIn.readSerializable();
129+
130+
assertEquals(RELEASE_027_VERSION, deserializedVersion.getVersion());
131+
assertEquals(semver("1.2.3"), deserializedVersion.getHapiVersion());
132+
assertEquals(semver("4.5.6"), deserializedVersion.getServicesVersion());
133+
134+
// Write the deserialized version back to a byte array. It should exactly match the original byte array.
135+
final ByteArrayOutputStream newBytes = new ByteArrayOutputStream();
136+
final SerializableDataOutputStream newOut = new SerializableDataOutputStream(newBytes);
137+
newOut.writeSerializable(deserializedVersion, true);
138+
newOut.close();
139+
final byte[] newBytesArray = newBytes.toByteArray();
140+
141+
assertArrayEquals(legacyBytes, newBytesArray);
142+
}
143+
69144
@Test
70145
@DisplayName("Sorting HederaSoftwareVersions")
71146
void sorting() {
Binary file not shown.

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public HederaEvmTransactionResult processTransaction(
113113
@NonNull final HederaEvmContext context,
114114
@NonNull final ActionSidecarContentTracer tracer,
115115
@NonNull final Configuration config) {
116-
final var parties = safeComputeInvolvedParties(transaction, updater, config, context);
116+
final var parties = computeInvolvedPartiesOrAbort(transaction, updater, config);
117117
try {
118118
return processTransactionWithParties(
119119
transaction, updater, feesOnlyUpdater, context, tracer, config, parties);
@@ -162,11 +162,10 @@ private HederaEvmTransactionResult processTransactionWithParties(
162162
return safeCommit(result, transaction, updater, feesOnlyUpdater, context, config);
163163
}
164164

165-
private InvolvedParties safeComputeInvolvedParties(
165+
private InvolvedParties computeInvolvedPartiesOrAbort(
166166
@NonNull final HederaEvmTransaction transaction,
167167
@NonNull final HederaWorldUpdater updater,
168-
@NonNull final Configuration config,
169-
@NonNull final HederaEvmContext context) {
168+
@NonNull final Configuration config) {
170169
try {
171170
return computeInvolvedParties(transaction, updater, config);
172171
} catch (AbortException e) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public AbortException(
4848
@NonNull final ResponseCodeEnum status,
4949
@NonNull final AccountID senderId,
5050
@Nullable final AccountID relayerId,
51-
@NonNull final boolean isChargeable) {
51+
final boolean isChargeable) {
5252
super(status);
5353
this.senderId = requireNonNull(senderId);
5454
this.relayerId = relayerId;

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ public enum DispatchType {
5555
UNFREEZE(HederaFunctionality.TOKEN_UNFREEZE_ACCOUNT, DEFAULT),
5656
WIPE_FUNGIBLE(HederaFunctionality.TOKEN_ACCOUNT_WIPE, TOKEN_FUNGIBLE_COMMON),
5757
WIPE_NFT(HederaFunctionality.TOKEN_ACCOUNT_WIPE, TOKEN_NON_FUNGIBLE_UNIQUE),
58-
UPDATE(HederaFunctionality.TOKEN_UPDATE, DEFAULT);
58+
UPDATE(HederaFunctionality.TOKEN_UPDATE, DEFAULT),
59+
UTIL_PRNG(HederaFunctionality.UTIL_PRNG, DEFAULT);
5960

6061
private final HederaFunctionality functionality;
6162
private final SubType subtype;

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

+9
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ public long topLevelGasRequirement(@NonNull final TransactionBody body, @NonNull
8888
return feeCalculator.applyAsLong(body, payer) / tinybarValues.topLevelTinybarGasPrice();
8989
}
9090

91+
/**
92+
* Returns the gas price for the top-level HAPI operation.
93+
*
94+
* @return the gas price for the top-level HAPI operation
95+
*/
96+
public long topLevelGasPrice() {
97+
return tinybarValues.topLevelTinybarGasPrice();
98+
}
99+
91100
/**
92101
* Given a dispatch type, returns the canonical gas requirement for that dispatch type.
93102
* Useful when providing a ballpark gas requirement in the absence of a valid

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

+13-7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import java.util.Map;
4848
import java.util.Objects;
4949
import java.util.Optional;
50+
import org.apache.tuweni.bytes.Bytes;
5051
import org.hyperledger.besu.datatypes.Address;
5152
import org.hyperledger.besu.evm.EVM;
5253
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
@@ -167,17 +168,21 @@ private void doExecutePrecompile(
167168
@NonNull final MessageFrame frame,
168169
@NonNull final OperationTracer tracer) {
169170
final var gasRequirement = precompile.gasRequirement(frame.getInputData());
171+
final PrecompileContractResult result;
170172
if (frame.getRemainingGas() < gasRequirement) {
171-
doHalt(frame, INSUFFICIENT_GAS);
173+
result = PrecompileContractResult.halt(Bytes.EMPTY, Optional.of(INSUFFICIENT_GAS));
172174
} else {
173175
frame.decrementRemainingGas(gasRequirement);
174-
final var result = precompile.computePrecompile(frame.getInputData(), frame);
175-
tracer.tracePrecompileCall(frame, gasRequirement, result.getOutput());
176+
result = precompile.computePrecompile(frame.getInputData(), frame);
176177
if (result.isRefundGas()) {
177178
frame.incrementRemainingGas(gasRequirement);
178179
}
179-
finishPrecompileExecution(frame, result, PRECOMPILE, (ActionSidecarContentTracer) tracer);
180180
}
181+
// We must always call tracePrecompileResult() to ensure the tracer is in a consistent
182+
// state, because AbstractMessageProcessor.process() will not invoke the tracer's
183+
// tracePostExecution() method unless start() returns with a state of CODE_EXECUTING;
184+
// but for a precompile call this never happens.
185+
finishPrecompileExecution(frame, result, PRECOMPILE, (ActionSidecarContentTracer) tracer);
181186
}
182187

183188
/**
@@ -195,15 +200,16 @@ private void doExecuteSystemContract(
195200
@NonNull final OperationTracer tracer) {
196201
final var fullResult = systemContract.computeFully(frame.getInputData(), frame);
197202
final var gasRequirement = fullResult.gasRequirement();
198-
tracer.tracePrecompileCall(frame, gasRequirement, fullResult.output());
203+
final PrecompileContractResult result;
199204
if (frame.getRemainingGas() < gasRequirement) {
200-
doHalt(frame, INSUFFICIENT_GAS);
205+
result = PrecompileContractResult.halt(Bytes.EMPTY, Optional.of(INSUFFICIENT_GAS));
201206
} else {
202207
if (!fullResult.isRefundGas()) {
203208
frame.decrementRemainingGas(gasRequirement);
204209
}
205-
finishPrecompileExecution(frame, fullResult.result(), SYSTEM, (ActionSidecarContentTracer) tracer);
210+
result = fullResult.result();
206211
}
212+
finishPrecompileExecution(frame, result, SYSTEM, (ActionSidecarContentTracer) tracer);
207213
}
208214

209215
private void finishPrecompileExecution(

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

+9-11
Original file line numberDiff line numberDiff line change
@@ -189,20 +189,18 @@ public long contractCreationLimit() {
189189
public long lazyCreationCostInGas(@NonNull final Address recipient) {
190190
final var payerId = context.payer();
191191
// Calculate gas for a CryptoCreateTransactionBody with an alias address
192-
final var createFee = gasCalculator.topLevelGasRequirement(
193-
TransactionBody.newBuilder()
194-
.cryptoCreateAccount(CREATE_TXN_BODY_BUILDER.alias(tuweniToPbjBytes(recipient)))
195-
.build(),
196-
payerId);
192+
final var synthCreation = TransactionBody.newBuilder()
193+
.cryptoCreateAccount(CREATE_TXN_BODY_BUILDER.alias(tuweniToPbjBytes(recipient)))
194+
.build();
195+
final var createFee = gasCalculator.canonicalPriceInTinybars(synthCreation, payerId);
197196

198197
// Calculate gas for an update TransactionBody
199-
final var updateFee = gasCalculator.topLevelGasRequirement(
200-
TransactionBody.newBuilder()
201-
.cryptoUpdateAccount(UPDATE_TXN_BODY_BUILDER)
202-
.build(),
203-
payerId);
198+
final var synthUpdate = TransactionBody.newBuilder()
199+
.cryptoUpdateAccount(UPDATE_TXN_BODY_BUILDER)
200+
.build();
201+
final var updateFee = gasCalculator.canonicalPriceInTinybars(synthUpdate, payerId);
204202

205-
return createFee + updateFee;
203+
return (createFee + updateFee) / gasCalculator.topLevelGasPrice();
206204
}
207205

208206
/**

0 commit comments

Comments
 (0)