Skip to content

Commit

Permalink
feat: address account nonce discrepancies mono (#11045)
Browse files Browse the repository at this point in the history
Signed-off-by: nikolay <[email protected]>
Signed-off-by: Ivo Yankov <[email protected]>
Co-authored-by: Ivo Yankov <[email protected]>
  • Loading branch information
natanasow and Ivo-Yankov committed Feb 8, 2024
1 parent 995f5a2 commit 66a3a36
Show file tree
Hide file tree
Showing 64 changed files with 1,283 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ protected TransactionProcessingResult execute(

super.setupFields(payload, contractCreation);

final var chargingResult = chargeForGas(
final var chargingResult = chargeForGasAndIncrementEthereumNonce(
gasCost,
upfrontCost,
value,
Expand Down Expand Up @@ -247,7 +247,7 @@ private void sendToCoinbase(
mutableCoinbase.incrementBalance(Wei.of(amount * gasPrice));
}

private ChargingResult chargeForGas(
private ChargingResult chargeForGasAndIncrementEthereumNonce(
final Wei gasCost,
final Wei upfrontCost,
final long value,
Expand Down Expand Up @@ -308,7 +308,14 @@ private ChargingResult chargeForGas(
final var senderCanAffordValue = senderAccount.getBalance().compareTo(Wei.of(value)) >= 0;
validateTrue(senderCanAffordValue, INSUFFICIENT_PAYER_BALANCE);
}

// increment sender's ethereum nonce right after all checks
// and before entering the evm for non-static calls
if (relayer != null) {
senderAccount.incrementNonce();
}
}

return new ChargingResult(senderAccount, mutableRelayer, allowanceCharged);
}

Expand All @@ -325,7 +332,7 @@ private void handleResourceLimitExceeded(
final Wei upfrontCost) {
// Consume all gas on resource exhaustion, using a clean updater
final var feesOnlyUpdater = (HederaWorldState.Updater) worldState.updater();
chargeForGas(
chargeForGasAndIncrementEthereumNonce(
gasCost,
upfrontCost,
value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class TransactionProcessingResult extends HederaEvmTransactionProcessingR

private List<ContractID> createdContracts = Collections.emptyList();
private Map<ContractID, Long> contractNonces = new TreeMap<>();
private Long signerNonce;

public static TransactionProcessingResult failed(
final long gasUsed,
Expand Down Expand Up @@ -143,6 +144,14 @@ public void setActions(final List<SolidityAction> actions) {
this.actions = actions;
}

public Long getSignerNonce() {
return signerNonce;
}

public void setSignerNonce(Long signerNonce) {
this.signerNonce = signerNonce;
}

/**
* Converts the {@link TransactionProcessingResult} into {@link ContractFunctionResult} gRPC
* model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.hedera.node.app.service.mono.legacy.core.jproto.JKey;
import com.hedera.node.app.service.mono.legacy.core.jproto.JKeySerializer;
import com.swirlds.common.io.SelfSerializable;
import com.swirlds.common.io.streams.AugmentedDataInputStream;
import com.swirlds.common.io.streams.SerializableDataInputStream;
import com.swirlds.common.io.streams.SerializableDataOutputStream;
import edu.umd.cs.findbugs.annotations.Nullable;
Expand All @@ -41,6 +42,16 @@ public static void writeNullableString(@Nullable final String msg, final Seriali
writeNullable(msg, out, (msgVal, outVal) -> outVal.writeNormalisedString(msgVal));
}

@Nullable
public static Long readNullableLong(final SerializableDataInputStream in) throws IOException {
return readNullable(in, AugmentedDataInputStream::readLong);
}

public static void writeNullableLong(@Nullable final Long num, final SerializableDataOutputStream out)
throws IOException {
writeNullable(num, out, (numVal, outVal) -> outVal.writeLong(numVal));
}

@Nullable
public static <T> T readNullable(final SerializableDataInputStream in, final IoReadingFunction<T> reader)
throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package com.hedera.node.app.service.mono.state.submerkle;

import static com.hedera.node.app.service.evm.accounts.HederaEvmContractAliases.isMirror;
import static com.hedera.node.app.service.mono.state.serdes.IoUtils.readNullableLong;
import static com.hedera.node.app.service.mono.state.serdes.IoUtils.readNullableSerializable;
import static com.hedera.node.app.service.mono.state.serdes.IoUtils.readNullableString;
import static com.hedera.node.app.service.mono.state.serdes.IoUtils.writeNullableLong;
import static com.hedera.node.app.service.mono.state.serdes.IoUtils.writeNullableSerializable;
import static com.hedera.node.app.service.mono.state.serdes.IoUtils.writeNullableString;
import static com.swirlds.common.utility.CommonUtils.hex;
Expand All @@ -27,6 +29,7 @@
import com.google.common.base.MoreObjects;
import com.google.protobuf.ByteString;
import com.google.protobuf.BytesValue;
import com.google.protobuf.Int64Value;
import com.hedera.node.app.hapi.utils.ethereum.EthTxData;
import com.hedera.node.app.service.mono.contracts.execution.TransactionProcessingResult;
import com.hederahashgraph.api.proto.java.ContractFunctionResult;
Expand Down Expand Up @@ -54,7 +57,8 @@ public class EvmFnResult implements SelfSerializable {
static final int RELEASE_0260_VERSION = 5;
static final int RELEASE_0290_VERSION = 6;
static final int RELEASE_0400_VERSION = 7;
static final int CURRENT_VERSION = RELEASE_0400_VERSION;
static final int RELEASE_0470_VERSION = 8;
static final int CURRENT_VERSION = RELEASE_0470_VERSION;

static final long RUNTIME_CONSTRUCTABLE_ID = 0x2055c5c03ff84eb4L;

Expand All @@ -79,6 +83,7 @@ public class EvmFnResult implements SelfSerializable {
private long amount;
private byte[] functionParameters = EMPTY;
private EntityId senderId;
private Long signerNonce;

public EvmFnResult() {
// RuntimeConstructable
Expand All @@ -105,12 +110,13 @@ private static EvmFnResult from(final TransactionProcessingResult result, final
recipient,
serializableIdsFrom(result.getCreatedContracts()),
serializableContractNoncesFrom(result.getContractNonces()),
evmAddress);
evmAddress,
result.getSignerNonce());
} else {
final var error = result.getRevertReason()
.map(Object::toString)
.orElse(result.getHaltReason().map(Object::toString).orElse(null));
return failure(result.getGasUsed(), error);
return failure(result.getGasUsed(), error, result.getSignerNonce());
}
}

Expand All @@ -127,7 +133,8 @@ public EvmFnResult(
final long gas,
final long amount,
final byte[] functionParameters,
final EntityId senderId) {
final EntityId senderId,
final Long signerNonce) {
this.contractId = contractId;
this.result = result;
this.error = error;
Expand All @@ -141,6 +148,7 @@ public EvmFnResult(
this.amount = amount;
this.functionParameters = functionParameters;
this.senderId = senderId;
this.signerNonce = signerNonce;
}

/* --- SelfSerializable --- */
Expand Down Expand Up @@ -197,6 +205,9 @@ public void deserialize(final SerializableDataInputStream in, final int version)
if (version >= RELEASE_0400_VERSION) {
contractNonces = in.readSerializableList(MAX_CREATED_CONTRACT_NONCES, true, ContractNonceInfo::new);
}
if (version >= RELEASE_0470_VERSION) {
signerNonce = readNullableLong(in);
}
}

@Override
Expand All @@ -214,6 +225,7 @@ public void serialize(final SerializableDataOutputStream out) throws IOException
out.writeByteArray(functionParameters);
writeNullableSerializable(senderId, out);
out.writeSerializableList(contractNonces, true, true);
writeNullableLong(signerNonce, out);
}

/* --- Object --- */
Expand All @@ -238,7 +250,8 @@ public boolean equals(final Object o) {
&& gas == that.gas
&& amount == that.amount
&& Arrays.equals(functionParameters, that.functionParameters)
&& Objects.equals(senderId, that.senderId);
&& Objects.equals(senderId, that.senderId)
&& Objects.equals(signerNonce, that.signerNonce);
}

@Override
Expand All @@ -265,6 +278,7 @@ public String toString() {
.add("amount", amount)
.add("functionParameters", hex(functionParameters))
.add("senderId", senderId)
.add("signerNonce", signerNonce)
.toString();
}

Expand Down Expand Up @@ -325,6 +339,10 @@ public EntityId getSenderId() {
return senderId;
}

public Long getSignerNonce() {
return signerNonce;
}

public void setEvmAddress(final byte[] evmAddress) {
this.evmAddress = evmAddress;
}
Expand All @@ -345,6 +363,10 @@ public void setSenderId(final EntityId senderId) {
this.senderId = senderId;
}

public void setSignerNonce(Long signerNonce) {
this.signerNonce = signerNonce;
}

public void setContractNonces(List<ContractNonceInfo> contractNonces) {
this.contractNonces = contractNonces;
}
Expand Down Expand Up @@ -391,6 +413,9 @@ public ContractFunctionResult toGrpc() {
if (senderId != null) {
grpc.setSenderId(senderId.toGrpcAccountId());
}
if (signerNonce != null) {
grpc.setSignerNonce(Int64Value.newBuilder().setValue(signerNonce).build());
}
return grpc.build();
}

Expand Down Expand Up @@ -433,7 +458,8 @@ private static EvmFnResult success(
final Address recipient,
final List<EntityId> createdContractIds,
final List<ContractNonceInfo> contractNonces,
final byte[] evmAddress) {
final byte[] evmAddress,
final Long signerNonce) {
return new EvmFnResult(
isMirror(recipient.toArray()) ? EntityId.fromAddress(recipient) : null,
output.toArrayUnsafe(),
Expand All @@ -447,10 +473,11 @@ private static EvmFnResult success(
0L,
0L,
EMPTY,
null);
null,
signerNonce);
}

private static EvmFnResult failure(final long gasUsed, final String error) {
private static EvmFnResult failure(final long gasUsed, final String error, final Long signerNonce) {
return new EvmFnResult(
null,
EMPTY,
Expand All @@ -464,6 +491,7 @@ private static EvmFnResult failure(final long gasUsed, final String error) {
0L,
0L,
EMPTY,
null);
null,
signerNonce);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public class ExpirableTxnRecord implements FastCopyable, SerializableHashable {
static final int RELEASE_0280_VERSION = 11;
// Debatable -- 0.33/0.34?
static final int RELEASE_0340_VERSION = 12;
static final int CURRENT_VERSION = RELEASE_0340_VERSION;
static final int RELEASE_0470_VERSION = 13;
static final int CURRENT_VERSION = RELEASE_0470_VERSION;
static final long RUNTIME_CONSTRUCTABLE_ID = 0x8b9ede7ca8d8db93L;

static final int MAX_MEMO_BYTES = 32 * 1_024;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public static void addContractCallResultToRecord(
traceabilityOn ? messageFrame.getRemainingGas() : 0L,
traceabilityOn ? messageFrame.getValue().toLong() : 0L,
traceabilityOn ? messageFrame.getInputData().toArrayUnsafe() : EvmFnResult.EMPTY,
EntityId.fromAddress(senderAddress));
EntityId.fromAddress(senderAddress),
null);
childRecord.setContractCallResult(evmFnResult);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,6 @@ public void doStateTransitionOperation(
result = evmTxProcessor.execute(
sender, receiver.canonicalAddress(), op.getGas(), op.getAmount(), callData, txnCtx.consensusTime());
} else {
sender.incrementEthereumNonce();
accountStore.commitAccount(sender);

result = evmTxProcessor.executeEth(
sender,
receiver.canonicalAddress(),
Expand All @@ -171,6 +168,7 @@ public void doStateTransitionOperation(
offeredGasPrice,
accountStore.loadAccount(relayerId),
maxGasAllowanceInTinybars);
result.setSignerNonce(worldState.get(senderId.asEvmAddress()).getNonce());
}

/* --- Externalise result --- */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,6 @@ public void doStateTransitionOperation(
codeWithConstructorArgs,
consensusTime);
} else {
sender.incrementEthereumNonce();
accountStore.commitAccount(sender);

result = evmTxProcessor.executeEth(
sender,
newContractAddress,
Expand All @@ -224,6 +221,7 @@ public void doStateTransitionOperation(
accountStore.loadAccount(relayerId),
userOfferedGasPrice,
maxGasAllowance);
result.setSignerNonce(worldState.get(senderId.asEvmAddress()).getNonce());
}
} finally {
worldState.resetHapiSenderCustomizer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public void doStateTransition() {
} catch (InvalidTransactionException e) {
var result = TransactionProcessingResult.failed(
0, 0, 0, Optional.of(e.messageBytes()), Optional.empty(), Collections.emptyMap(), List.of());
result.setSignerNonce((Long) accountsLedger.get(callerNum.toGrpcAccountId(), ETHEREUM_NONCE));
recordService.externaliseEvmCallTransaction(result);
throw e;
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ protected EvmFnResult getExpectedObject(
// Always empty before 0.40
seeded.setContractNonces(Collections.emptyList());
}
if (version < EvmFnResult.RELEASE_0470_VERSION) {
seeded.setSignerNonce(null);
}
return seeded;
}

Expand Down
Loading

0 comments on commit 66a3a36

Please sign in to comment.