Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

03640 Contract create nonce externalization #6934

Merged
merged 40 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9a23dd5
03640: Added Solidity smart contract and suite for test purposes
agadzhalov May 25, 2023
f13d7c3
03640: Updated suite to deploy child contract
agadzhalov May 25, 2023
a512340
03640: Added global dynamic property enableContractsNoncesExternaliza…
agadzhalov May 29, 2023
9c16c42
03640: Added suite for deployParentContractAndRevert
agadzhalov May 29, 2023
954cfc1
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jun 1, 2023
5b4c856
03640: Added initial implementation of contract nonce externalization
agadzhalov Jun 1, 2023
419f9ee
03640: Added suite for contract create happy path and new Transaction…
agadzhalov Jun 5, 2023
eee7876
03640: Implementation of parent contract nonce externalization
agadzhalov Jun 5, 2023
89a2633
03640: Moved code block
agadzhalov Jun 5, 2023
171c095
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jun 6, 2023
a5dec55
03640: Usage of RecordsHistorian instead of TxAwareRecordsHistorian. …
agadzhalov Jun 6, 2023
48234f5
03640: MockRecordsHistorian overrides setContractNonces
agadzhalov Jun 6, 2023
637bfc5
03640: Added unit tests for GlobalDynamicProperties and EvmFnResultTest
agadzhalov Jun 7, 2023
ec7a10f
03640: Updated if condtion with HederaFunctionality enum and overridi…
agadzhalov Jun 8, 2023
70753be
03640: setContractNonces replaced with updateContractNonces. Unit tes…
agadzhalov Jun 8, 2023
177579f
03640: changed unit test on TxAwareRecordsHistorian
agadzhalov Jun 8, 2023
0c42317
03640: propertyPreservingHapiSpec update for suite
agadzhalov Jun 8, 2023
f60557c
03640: updated suite for retrieving newly deployed contract addresses
agadzhalov Jun 9, 2023
9ee733b
03640: HederaCreateOperationExternalizerTest increased coverage
agadzhalov Jun 13, 2023
024d993
03640: sonar fixed issues
agadzhalov Jun 13, 2023
6e441fa
03640: contractCreateNoncesExternalizationHappyPath and spotless fix
agadzhalov Jun 13, 2023
11ec6df
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jun 14, 2023
3946228
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jun 14, 2023
9bf0f71
Merge remote-tracking branch 'origin/03640-contracts-nonce-externaliz…
agadzhalov Jun 14, 2023
c07e3a3
03640: moved test to LeakyContractTestsSuite
agadzhalov Jun 14, 2023
785f3ab
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jun 26, 2023
85268c1
Fix contract nonce reset to zero (#7078)
MiroslavGatsanoga Jun 26, 2023
cefc351
Removed logging code block and improved unit test for ContractCallTra…
agadzhalov Jun 26, 2023
940eff3
Added unit test and increased coverage for HederaWorldState.
agadzhalov Jun 26, 2023
3d64e7b
Added unit tests for ContractNonceInfo
agadzhalov Jun 27, 2023
d5f2e2f
Increased test coverage for trackContractNonces in HederaWorldState
agadzhalov Jun 27, 2023
97be356
Unit tests. Coverage in ContractNonceInfoTest and HederaWorldStateTest
agadzhalov Jun 27, 2023
71e81cf
Added feature flags and unit tests for ContractCreate and ContractCal…
agadzhalov Jun 27, 2023
4a6e341
Sonar minor fixes
agadzhalov Jun 27, 2023
5809286
Added suite case when feature flag is disabled
agadzhalov Jun 27, 2023
f5f24e3
Improved suite test using contractWithNonce
agadzhalov Jun 28, 2023
14a7e0e
Sonar fix replaced collect(Collectors.toList()) with toList()
agadzhalov Jun 28, 2023
c0294f6
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jun 28, 2023
de3d67a
Merge branch 'develop' into 03640-contracts-nonce-externalization
agadzhalov Jul 3, 2023
7bb8b24
03640: Merge dev and fix conflicts
agadzhalov Jul 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@
import org.hyperledger.besu.datatypes.Address;

/**
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
* When an account's balance is changed by an EVM call, it is necessary to ensure that any Hedera system contract running
* within the same frame can immediately see the change. This means that waiting for commit() to be called on a WorldUpdater
* is not an option; instead, the "parallel" Hedera world state must be updated immediately.
* When an account's balance is changed by an EVM call, it is necessary to ensure that any Hedera system contract
* running within the same frame can immediately see the change. This means that waiting for commit() to be called on a
* WorldUpdater is not an option; instead, the "parallel" Hedera world state must be updated immediately.
*
* <p>This class defines the callback mechanism that a mutated account uses to report its balance changes to the Hedera
* world state when executing an EVM transaction on a consensus node. Accounts that are "mutated" during an eth_estimateGas
* call running on a mirror node do not actually change, so a null implementation of the callback can be provided in such cases.
* */
* world state when executing an EVM transaction on a consensus node. Accounts that are "mutated" during an
* eth_estimateGas call running on a mirror node do not actually change, so a null implementation of the callback can be
* provided in such cases.
*/
public interface UpdateAccountTracker {

void setBalance(final Address accountAddess, final long balance);

void setNonce(Address address, long nonce);
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public interface HederaEvmEntityAccess {

long getBalance(Address address);

default long getNonce(Address address) {
return 0;
}

boolean isTokenAccount(Address address);

ByteString alias(Address address);
Expand All @@ -35,8 +39,8 @@ public interface HederaEvmEntityAccess {
Bytes getStorage(Address address, Bytes key);

/**
* Returns the bytecode for the contract with the given account id; or null if there is no byte
* present for this contract.
* Returns the bytecode for the contract with the given account id; or null if there is no byte present for this
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
* contract.
*
* @param address the account's address of the target contract
* @return the target contract's bytecode, or null if it is not present
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public Hash getAddressHash() {

@Override
public long getNonce() {
return 0;
return entityAccess.getNonce(address);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,29 @@
import org.hyperledger.besu.evm.account.MutableAccount;

/**
* A mutable and updatable implementation of the {@link MutableAccount} interface, that tracks account updates
* since the creation of the updated it's linked to.
*
* Contains {@code updateAccountTracker} for immediate set of balance in the world state.
* Note that in practice this only track the modified account values, but doesn't remind if they were modified or not.
* A mutable and updatable implementation of the {@link MutableAccount} interface, that tracks account updates since the
* creation of the updated it's linked to.
* <p>
* Contains {@code updateAccountTracker} for immediate set of balance in the world state. Note that in practice this
* only track the modified account values, but doesn't remind if they were modified or not.
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
*/
public class UpdateTrackingAccount<A extends Account> implements MutableAccount, EvmAccount {
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
@Nullable
protected final A account;

private final Address address;
private final Hash addressHash;
private long nonce;
private Wei balance;
private HederaEvmEntityAccess hederaEvmEntityAccess;
private boolean storageWasCleared = false;
private final UpdateAccountTracker updateAccountTracker;
private final NavigableMap<UInt256, UInt256> updatedStorage;

@Nullable
protected final A account;

@Nullable
protected Bytes updatedCode;

private long nonce;
private Wei balance;
private HederaEvmEntityAccess hederaEvmEntityAccess;
private boolean storageWasCleared = false;
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved

@Nullable
private Hash updatedCodeHash;

Expand Down Expand Up @@ -92,8 +93,7 @@ public UpdateTrackingAccount(final A account, @Nullable final UpdateAccountTrack
/**
* The original account over which this tracks updates.
*
* @return The original account over which this tracks updates, or {@code null} if this is a
* newly created account.
* @return The original account over which this tracks updates, or {@code null} if this is a newly created account.
*/
public A getWrappedAccount() {
return account;
Expand All @@ -115,8 +115,8 @@ public boolean wrappedAccountIsTokenProxy() {
/**
* A map of the storage entries that were modified.
*
* @return a map containing all entries that have been modified. This <b>may</b> contain entries
* with a value of 0 to signify deletion.
* @return a map containing all entries that have been modified. This <b>may</b> contain entries with a value of 0
* to signify deletion.
*/
@Override
public Map<UInt256, UInt256> getUpdatedStorage() {
Expand All @@ -141,6 +141,9 @@ public long getNonce() {
@Override
public void setNonce(final long nonce) {
this.nonce = nonce;
if (updateAccountTracker != null) {
updateAccountTracker.setNonce(address, nonce);
}
}

@Override
Expand All @@ -166,6 +169,12 @@ public Bytes getCode() {
return updatedCode == null ? account.getCode() : updatedCode;
}

@Override
public void setCode(Bytes code) {
this.updatedCode = code;
this.updatedCodeHash = null;
}

agadzhalov marked this conversation as resolved.
Show resolved Hide resolved
@Override
public Hash getCodeHash() {
if (updatedCode == null) {
Expand All @@ -187,12 +196,6 @@ public boolean hasCode() {
return updatedCode == null ? account.hasCode() : !updatedCode.isEmpty();
}

@Override
public void setCode(Bytes code) {
this.updatedCode = code;
this.updatedCodeHash = null;
}

@Override
public UInt256 getStorageValue(final UInt256 key) {
final UInt256 value = updatedStorage.get(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;

import com.hedera.node.app.service.evm.store.UpdateAccountTracker;
import com.hedera.node.app.service.evm.store.contracts.AbstractCodeCache;
import com.hedera.node.app.service.evm.store.contracts.HederaEvmEntityAccess;
import com.hedera.node.app.service.evm.store.contracts.HederaEvmWorldStateTokenAccount;
Expand All @@ -42,12 +44,16 @@
@ExtendWith(MockitoExtension.class)
class UpdateTrackingAccountTest {
private static final long newBalance = 200_000L;
private static final long newNonce = 2L;
private static final long initialBalance = 100_000L;
private static final Address targetAddress = Address.fromHexString("0x000000000000000000000000000000000000066e");

@Mock
private HederaEvmEntityAccess entityAccess;

@Mock
private UpdateAccountTracker updateAccountTracker;

private AbstractCodeCache codeCache;

@BeforeEach
Expand Down Expand Up @@ -85,6 +91,18 @@ void justPropagatesBalanceChangeWithNullTrackingAccounts() {
assertFalse(subject.wrappedAccountIsTokenProxy());
}

@Test
void justPropagatesNonceChangeWithTrackingAccounts() {
final var account = new WorldStateAccount(targetAddress, Wei.of(initialBalance), codeCache, entityAccess);

final var subject = new UpdateTrackingAccount<>(account, updateAccountTracker);

subject.setNonce(newNonce);

assertEquals(newNonce, subject.getNonce());
verify(updateAccountTracker).setNonce(targetAddress, newNonce);
}

@Test
void reusesAddressHashWhenConstructedWithTracker() {
final var account = new WorldStateAccount(targetAddress, Wei.of(initialBalance), codeCache, entityAccess);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_MAX_NUM;
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_MAX_NUM_WITH_HAPI_SIGS_ACCESS;
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_MAX_REFUND_PERCENT_OF_GAS_LIMIT;
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_NONCES_EXTERNALIZATION_ENABLED;
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_PERMITTED_DELEGATE_CALLERS;
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_PRECOMPILE_ATOMIC_CRYPTO_TRANSFER_ENABLED;
import static com.hedera.node.app.service.mono.context.properties.PropertyNames.CONTRACTS_PRECOMPILE_EXCHANGE_RATE_GAS_COST;
Expand Down Expand Up @@ -256,6 +257,7 @@ public class GlobalDynamicProperties implements EvmProperties {
private Set<CustomFeeType> htsUnsupportedCustomFeeReceiverDebits;
private boolean atomicCryptoTransferEnabled;
private boolean enableHRCAssociate;
private boolean enableContractsNoncesExternalization;
private KnownBlockValues knownBlockValues;
private long exchangeRateGasReq;
private long stakingRewardRate;
Expand Down Expand Up @@ -407,6 +409,7 @@ public void reload() {
atomicCryptoTransferEnabled =
properties.getBooleanProperty(CONTRACTS_PRECOMPILE_ATOMIC_CRYPTO_TRANSFER_ENABLED);
enableHRCAssociate = properties.getBooleanProperty(CONTRACTS_PRECOMPILE_HRC_FACADE_ASSOCIATE_ENABLED);
enableContractsNoncesExternalization = properties.getBooleanProperty(CONTRACTS_NONCES_EXTERNALIZATION_ENABLED);
knownBlockValues = properties.getBlockValuesProperty(CONTRACTS_KNOWN_BLOCK_HASH);
exchangeRateGasReq = properties.getLongProperty(CONTRACTS_PRECOMPILE_EXCHANGE_RATE_GAS_COST);
stakingMaxStakeRewarded = properties.getLongProperty(STAKING_MAX_STAKE_REWARDED);
Expand Down Expand Up @@ -788,6 +791,10 @@ public boolean isHRCAssociateEnabled() {
return enableHRCAssociate;
}

public boolean isContractsNoncesExternalizationEnabled() {
return enableContractsNoncesExternalization;
}

public KnownBlockValues knownBlockValues() {
return knownBlockValues;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
Expand All @@ -41,6 +42,7 @@ public class TransactionProcessingResult extends HederaEvmTransactionProcessingR
private List<SolidityAction> actions;

private List<ContractID> createdContracts = Collections.emptyList();
private Map<ContractID, Long> contractNonces = new TreeMap<>();
agadzhalov marked this conversation as resolved.
Show resolved Hide resolved

public static TransactionProcessingResult failed(
final long gasUsed,
Expand Down Expand Up @@ -113,10 +115,18 @@ public void setCreatedContracts(List<ContractID> createdContracts) {
this.createdContracts = createdContracts;
}

public void setContractNonces(Map<ContractID, Long> contractNonces) {
this.contractNonces = contractNonces;
}

public List<ContractID> getCreatedContracts() {
return createdContracts;
}

public Map<ContractID, Long> getContractNonces() {
return contractNonces;
}

public Map<Address, Map<Bytes, Pair<Bytes, Bytes>>> getStateChanges() {
return stateChanges;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.ALIAS;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.AUTO_RENEW_PERIOD;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.BALANCE;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.ETHEREUM_NONCE;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.EXPIRY;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.IS_DELETED;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.IS_RECEIVER_SIG_REQUIRED;
Expand Down Expand Up @@ -73,21 +74,19 @@
import org.apache.commons.lang3.tuple.Pair;

/**
* Provides a ledger for Hedera Services crypto and smart contract accounts with transactional
* semantics. Changes to the ledger are <b>only</b> allowed in the scope of a transaction.
* Provides a ledger for Hedera Services crypto and smart contract accounts with transactional semantics. Changes to the
* ledger are <b>only</b> allowed in the scope of a transaction.
*
* <p>All changes that are made during a transaction are summarized as per-account changesets. These
* changesets are committed to a wrapped {@link TransactionalLedger}; or dropped entirely in case of
* a rollback.
* changesets are committed to a wrapped {@link TransactionalLedger}; or dropped entirely in case of a rollback.
*
* <p>The ledger delegates history of each transaction to an injected {@link RecordsHistorian} by
* invoking its {@code addNewRecords} immediately before the final {@link
* TransactionalLedger#commit()}.
* invoking its {@code addNewRecords} immediately before the final {@link TransactionalLedger#commit()}.
*
* <p>We should think of the ledger as using double-booked accounting, (e.g., via the {@link
* HederaLedger#doTransfer(AccountID, AccountID, long)} method); but it is necessary to provide
* "unsafe" single-booked methods like {@link HederaLedger#adjustBalance(AccountID, long)} in order
* to match transfer semantics the EVM expects.
* HederaLedger#doTransfer(AccountID, AccountID, long)} method); but it is necessary to provide "unsafe" single-booked
* methods like {@link HederaLedger#adjustBalance(AccountID, long)} in order to match transfer semantics the EVM
* expects.
*/
public class HederaLedger {
public static final String NO_ACTIVE_TXN_CHANGE_SET = "{*NO ACTIVE TXN*}";
Expand All @@ -111,13 +110,11 @@ public class HederaLedger {
private final RecordsHistorian historian;
private final TransactionalLedger<TokenID, TokenProperty, MerkleToken> tokensLedger;
private final TransactionalLedger<AccountID, AccountProperty, HederaAccount> accountsLedger;

private final AutoCreationLogic autoCreationLogic;
private MutableEntityAccess mutableEntityAccess;
private TransactionalLedger<NftId, NftProperty, UniqueTokenAdapter> nftsLedger = null;
private TransactionalLedger<Pair<AccountID, TokenID>, TokenRelProperty, HederaTokenRel> tokenRelsLedger = null;

private final AutoCreationLogic autoCreationLogic;

public HederaLedger(
final TokenStore tokenStore,
final EntityIdSource ids,
Expand Down Expand Up @@ -149,15 +146,6 @@ public void setMutableEntityAccess(final MutableEntityAccess mutableEntityAccess
this.mutableEntityAccess = mutableEntityAccess;
}

public void setNftsLedger(final TransactionalLedger<NftId, NftProperty, UniqueTokenAdapter> nftsLedger) {
this.nftsLedger = nftsLedger;
}

public void setTokenRelsLedger(
final TransactionalLedger<Pair<AccountID, TokenID>, TokenRelProperty, HederaTokenRel> tokenRelsLedger) {
this.tokenRelsLedger = tokenRelsLedger;
}

public TransactionalLedger<AccountID, AccountProperty, HederaAccount> getAccountsLedger() {
return accountsLedger;
}
Expand All @@ -166,10 +154,19 @@ public TransactionalLedger<NftId, NftProperty, UniqueTokenAdapter> getNftsLedger
return nftsLedger;
}

public void setNftsLedger(final TransactionalLedger<NftId, NftProperty, UniqueTokenAdapter> nftsLedger) {
this.nftsLedger = nftsLedger;
}

public TransactionalLedger<Pair<AccountID, TokenID>, TokenRelProperty, HederaTokenRel> getTokenRelsLedger() {
return tokenRelsLedger;
}

public void setTokenRelsLedger(
final TransactionalLedger<Pair<AccountID, TokenID>, TokenRelProperty, HederaTokenRel> tokenRelsLedger) {
this.tokenRelsLedger = tokenRelsLedger;
}

/* -- TRANSACTIONAL SEMANTICS -- */
public void begin() {
autoCreationLogic.reset();
Expand Down Expand Up @@ -230,6 +227,10 @@ public String currentChangeSet() {
}
}

public long getNonce(final AccountID id) {
return (long) accountsLedger.get(id, ETHEREUM_NONCE);
}

/* -- CURRENCY MANIPULATION -- */
public long getBalance(final AccountID id) {
return (long) accountsLedger.get(id, BALANCE);
Expand Down Expand Up @@ -349,10 +350,10 @@ public void customize(final AccountID id, final HederaAccountCustomizer customiz
}

/**
* Updates the provided {@link AccountID} with the {@link HederaAccountCustomizer}. All
* properties from the customizer are applied to the {@link MerkleAccount} provisionally
* Updates the provided {@link AccountID} with the {@link HederaAccountCustomizer}. All properties from the
* customizer are applied to the {@link MerkleAccount} provisionally
*
* @param id target account
* @param id target account
* @param customizer properties to update
*/
public void customizePotentiallyDeleted(final AccountID id, final HederaAccountCustomizer customizer) {
Expand Down
Loading