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

Storage rent 2022 #1798

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
aa92be0
introduce TrackedNode class, useful to track used trie-nodes from rep…
Mar 21, 2022
875a207
add OperationType, useful to distinguish between read/write operations
Mar 21, 2022
cc7b70f
add to MutableRepository node-tracking capability
Mar 21, 2022
d7fdee2
tracking trie-nodes for each executed transaction
Mar 21, 2022
d28d10f
adapted MinerServerTest to use trackedRepositoryAt
Mar 21, 2022
b61d1f6
renamed TransactionExecutor internal repositories (track & cacheTrack…
Mar 21, 2022
adb5781
add @VisibleForTesting anotation to MutableTrie & Trie
Mar 21, 2022
46834c6
add comment to TrackedNode
Mar 21, 2022
5f40ef6
add rskip240 activation
Mar 23, 2022
9950813
World supports custom time between blocks & saves used transaction ex…
Mar 23, 2022
3064c19
trie supports timestamped nodes
Mar 23, 2022
056fede
new MutableTrie new methods (find, nodeSize & put/getRentTimestamp)
Mar 23, 2022
27009e1
new Repository methods (putRentTimestamp, updateRents, setTrackedTxHa…
Mar 23, 2022
9546d5f
add storage rent RSKIP240
Mar 25, 2022
13b5a52
refactor into mutable repository tracked
May 7, 2022
d136f18
adapting tests
May 7, 2022
9b39a17
refactor, removed unnecesary consutrctor from MutableRepositoryTracked
May 23, 2022
0fe9ee1
fix sonarcloud, lgtm & code cleanup
Jun 2, 2022
03ff52d
add trace logs
Jun 7, 2022
b984ed6
remove axiliary methods from RentedNode
Jun 7, 2022
074ad6d
StorageRentManager as stateless class
Jun 7, 2022
0bac4b3
refactor trie serialization tests
Jun 30, 2022
6d34e9c
remove old & not relevant todos
Jul 4, 2022
29a70fc
avoid remasc transaction for rent payment
Jul 4, 2022
958a981
fix sonarcloud errors (StorageRentManager as utility class & removed …
Jul 8, 2022
971d55f
profiling storage rent
Jul 4, 2022
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 @@ -410,4 +410,8 @@ public boolean fastBlockPropagation() {
public Integer getMessageQueueMaxSize() {
return configFromFiles.getInt("peer.messageQueue.maxSizePerPeer");
}

public boolean getStorageRentEnabled() {
return configFromFiles.getBoolean("storageRent.enabled");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ public TransactionExecutor newInstance(
Transaction tx,
int txindex,
RskAddress coinbase,
Repository track,
Repository blockTrack,
Block block,
long totalGasUsed) {
return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, false, 0, new HashSet<>());
return newInstance(tx, txindex, coinbase, blockTrack, block, totalGasUsed, false, 0, new HashSet<>());
}

public TransactionExecutor newInstance(
Transaction tx,
int txindex,
RskAddress coinbase,
Repository track,
Repository blockTrack,
Block block,
long totalGasUsed,
boolean vmTrace,
Expand All @@ -92,13 +92,13 @@ public TransactionExecutor newInstance(
);
}

return new TransactionExecutor(
TransactionExecutor transactionExecutor = new TransactionExecutor(
config.getNetworkConstants(),
config.getActivationConfig(),
tx,
txindex,
coinbase,
track,
blockTrack,
blockStore,
receiptStore,
blockFactory,
Expand All @@ -111,5 +111,9 @@ public TransactionExecutor newInstance(
deletedAccounts,
blockTxSignatureCache
);

transactionExecutor.setStorageRentEnabled(config.getStorageRentEnabled());

return transactionExecutor;
}
}
1 change: 1 addition & 0 deletions rskj-core/src/main/java/co/rsk/core/bc/BlockChainImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public ImportResult tryToConnect(Block block) {
String timeInSeconds = FormatUtils.formatNanosecondsToSeconds(totalTime);

if (BlockUtils.tooMuchProcessTime(totalTime)) {

logger.warn("block: num: [{}] hash: [{}], processed after: [{}]seconds, result {}", block.getNumber(), block.getPrintableHash(), timeInSeconds, result);
}
else {
Expand Down
30 changes: 20 additions & 10 deletions rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.ethereum.config.blockchain.upgrades.ActivationConfig;
import org.ethereum.config.blockchain.upgrades.ConsensusRule;
import org.ethereum.core.*;
import org.ethereum.db.MutableRepositoryTracked;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.PrecompiledContracts;
import org.ethereum.vm.program.ProgramResult;
Expand Down Expand Up @@ -265,9 +266,9 @@ private BlockResult executeInternal(
// the state prior execution again.
Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE);

Repository track = repositoryLocator.startTrackingAt(parent);
MutableRepositoryTracked blockTrack = repositoryLocator.trackedRepositoryAt(parent);

maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber()));
maintainPrecompiledContractStorageRoots(blockTrack, activationConfig.forBlock(block.getNumber()));

int i = 1;
long totalGasUsed = 0;
Expand All @@ -281,17 +282,19 @@ private BlockResult executeInternal(
for (Transaction tx : block.getTransactionsList()) {
logger.trace("apply block: [{}] tx: [{}] ", block.getNumber(), i);

blockTrack.setTrackedTransactionHash(tx.getHash().toHexString());

TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(
tx,
txindex++,
block.getCoinbase(),
track,
blockTrack,
block,
totalGasUsed,
vmTrace,
vmTraceOptions,
deletedAccounts);
boolean transactionExecuted = txExecutor.executeTransaction();
boolean transactionExecuted = executeTransaction(txExecutor);

if (!acceptInvalidTransactions && !transactionExecuted) {
if (discardInvalidTxs) {
Expand All @@ -317,9 +320,9 @@ private BlockResult executeInternal(

logger.trace("tx executed");

// No need to commit the changes here. track.commit();
// No need to commit the changes here. blockTrack.commit();

logger.trace("track commit");
logger.trace("blockTrack commit");

long gasUsed = txExecutor.getGasUsed();
totalGasUsed += gasUsed;
Expand Down Expand Up @@ -352,9 +355,9 @@ private BlockResult executeInternal(

logger.trace("End txs executions.");
if (!vmTrace) {
logger.trace("Saving track.");
track.save();
logger.trace("End saving track.");
logger.trace("Saving blockTrack.");
blockTrack.save();
logger.trace("End saving blockTrack.");
}

logger.trace("Building execution results.");
Expand All @@ -364,13 +367,20 @@ private BlockResult executeInternal(
receipts,
totalGasUsed,
totalPaidFees,
vmTrace ? null : track.getTrie()
vmTrace ? null : blockTrack.getTrie()
);
profiler.stop(metric);
logger.trace("End executeInternal.");
return result;
}

/**
* This method is then overridden for testing purposes at BlockExecutorDSL
* */
protected boolean executeTransaction(TransactionExecutor txExecutor) {
return txExecutor.executeTransaction();
}

/**
* Precompiled contracts storage is setup like any other contract for consistency. Here, we apply this logic on the
* exact activation block.
Expand Down
43 changes: 40 additions & 3 deletions rskj-core/src/main/java/co/rsk/db/MutableTrieCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import co.rsk.trie.MutableTrie;
import co.rsk.trie.Trie;
import co.rsk.trie.TrieKeySlice;
import com.google.common.annotations.VisibleForTesting;
import org.ethereum.crypto.Keccak256Helper;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.TrieKeyMapper;
Expand All @@ -37,18 +38,23 @@ public class MutableTrieCache implements MutableTrie {

private final TrieKeyMapper trieKeyMapper = new TrieKeyMapper();

// this is a MutableTrieImpl instance, it is the access point to the real Trie
private MutableTrie trie;

// We use a single cache to mark both changed elements and removed elements.
// null value means the element has been removed.
private final Map<ByteArrayWrapper, Map<ByteArrayWrapper, byte[]>> cache;

private final Map<ByteArrayWrapper, Long> cacheTimestamps;

// this logs recursive delete operations to be performed at commit time
private final Set<ByteArrayWrapper> deleteRecursiveLog;

public MutableTrieCache(MutableTrie parentTrie) {
trie = parentTrie;
cache = new HashMap<>();
deleteRecursiveLog = new HashSet<>();
cacheTimestamps = new HashMap<>();
}

@Override
Expand All @@ -74,7 +80,6 @@ private <T> Optional<T> internalGet(
Function<byte[], T> cacheTransformer) {
ByteArrayWrapper wrapper = new ByteArrayWrapper(key);
ByteArrayWrapper accountWrapper = getAccountWrapper(wrapper);

Map<ByteArrayWrapper, byte[]> accountItems = cache.get(accountWrapper);
boolean isDeletedAccount = deleteRecursiveLog.contains(accountWrapper);
if (accountItems == null || !accountItems.containsKey(wrapper)) {
Expand All @@ -95,6 +100,22 @@ private <T> Optional<T> internalGet(
return Optional.ofNullable(cacheTransformer.apply(cacheItem));
}

public Optional<Long> getRentTimestamp(byte[] key) {
ByteArrayWrapper wrappedKey = new ByteArrayWrapper(key);

boolean isDeletedAccount = deleteRecursiveLog.contains(getAccountWrapper(wrappedKey));
if (isDeletedAccount) {
return Optional.empty();
}

Long lastRentPaidTimestamp = cacheTimestamps.get(wrappedKey);
if(lastRentPaidTimestamp != null) {
return Optional.of(lastRentPaidTimestamp);
}

return this.trie.getRentTimestamp(wrappedKey.getData());
}

public Iterator<DataWord> getStorageKeys(RskAddress addr) {
byte[] accountStoragePrefixKey = trieKeyMapper.getAccountStoragePrefixKey(addr);
ByteArrayWrapper accountWrapper = getAccountWrapper(new ByteArrayWrapper(accountStoragePrefixKey));
Expand Down Expand Up @@ -143,7 +164,7 @@ public void put(ByteArrayWrapper wrapper, byte[] value) {
accountMap.put(wrapper, value);
}

@Override
@Override @VisibleForTesting
public void put(String key, byte[] value) {
byte[] keybytes = key.getBytes(StandardCharsets.UTF_8);
put(keybytes, value);
Expand Down Expand Up @@ -182,9 +203,12 @@ public void commit() {
accountData.forEach((realKey, value) -> this.trie.put(realKey, value));
}
});

cacheTimestamps.forEach((key, timestamp) ->
this.trie.putRentTimestamp(key.getData(), timestamp)
);
deleteRecursiveLog.clear();
cache.clear();
cacheTimestamps.clear();
}

@Override
Expand All @@ -197,6 +221,7 @@ public void save() {
public void rollback() {
cache.clear();
deleteRecursiveLog.clear();
cacheTimestamps.clear();
}

@Override
Expand Down Expand Up @@ -240,6 +265,18 @@ public Optional<Keccak256> getValueHash(byte[] key) {
cachedBytes -> new Keccak256(Keccak256Helper.keccak256(cachedBytes)));
}


@Override
public void putRentTimestamp(byte[] key, long updatedTimestamp) {
this.cacheTimestamps.put(new ByteArrayWrapper(key), updatedTimestamp);
}

@Override
public MutableTrie find(byte[] key) {
// todo(fedejinich) I'm not using any cache
return this.trie.find(key);
}

private static class StorageKeysIterator implements Iterator<DataWord> {
private final Iterator<DataWord> keysIterator;
private final Map<ByteArrayWrapper, byte[]> accountItems;
Expand Down
33 changes: 30 additions & 3 deletions rskj-core/src/main/java/co/rsk/db/MutableTrieImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
import co.rsk.core.types.ints.Uint24;
import co.rsk.crypto.Keccak256;
import co.rsk.trie.*;
import com.google.common.annotations.VisibleForTesting;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.TrieKeyMapper;
import org.ethereum.vm.DataWord;

import java.util.*;

public class MutableTrieImpl implements MutableTrie {

private Trie trie;
private TrieKeyMapper trieKeyMapper = new TrieKeyMapper();
private TrieStore trieStore;
Expand All @@ -39,6 +39,10 @@ public MutableTrieImpl(TrieStore trieStore, Trie trie) {
this.trie = trie;
}

public MutableTrieImpl(TrieStore trieStore) {
this(trieStore, new Trie(trieStore));
}

@Override
public Trie getTrie() {
return trie;
Expand All @@ -64,9 +68,9 @@ public void put(ByteArrayWrapper key, byte[] value) {
trie = trie.put(key, value);
}

@Override
@Override // todo(techdebt) this is only used for testing, it should be removed from production
public void put(String key, byte[] value) {
trie = trie.put(key, value);
trie = trie.put(key, value); // NOSONAR (sonar is refusing for @VisibleForTesting)
}

@Override
Expand Down Expand Up @@ -103,6 +107,29 @@ public Iterator<DataWord> getStorageKeys(RskAddress addr) {
return Collections.emptyIterator();
}

@Override
public void putRentTimestamp(byte[] key, long updatedTimestamp) {
this.trie = this.trie.updateLastRentPaidTimestamp(TrieKeySlice.fromKey(key), updatedTimestamp);
}

@Override
public MutableTrie find(byte[] key) {
return new MutableTrieImpl(this.trieStore, this.trie.find(key));
}

@Override
public Optional<Long> getRentTimestamp(byte[] key) {
Trie trie = this.trie.find(key);

if(trie == null) {
return Optional.empty();
}

long lastRentPaidTimestamp = trie.getLastRentPaidTimestamp();

return Optional.of(lastRentPaidTimestamp);
}

@Override
public void deleteRecursive(byte[] key) {
trie = trie.deleteRecursive(key);
Expand Down
16 changes: 15 additions & 1 deletion rskj-core/src/main/java/co/rsk/db/RepositoryLocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.ethereum.core.Repository;
import org.ethereum.crypto.Keccak256Helper;
import org.ethereum.db.MutableRepository;
import org.ethereum.db.MutableRepositoryTracked;
import org.ethereum.util.RLP;

import java.util.Optional;
Expand Down Expand Up @@ -87,11 +88,24 @@ private Optional<MutableTrie> mutableTrieSnapshotAt(BlockHeader header) {
Keccak256 stateRoot = stateRootHandler.translate(header);

if (EMPTY_HASH.equals(stateRoot)) {
return Optional.of(new MutableTrieImpl(trieStore, new Trie(trieStore)));
return Optional.of(new MutableTrieImpl(trieStore));
}

Optional<Trie> trie = trieStore.retrieve(stateRoot.getBytes());

return trie.map(t -> new MutableTrieImpl(trieStore, t));
}

/**
* Retrieves a repository with node tracking capabilities.
* @param header the header to retrieve the state from
* @return a modifiable {@link Repository}
* @throws IllegalArgumentException if the state is not found.
* */
public MutableRepositoryTracked trackedRepositoryAt(BlockHeader header) {
return mutableTrieSnapshotAt(header)
.map(MutableTrieCache::new)
.map(MutableRepositoryTracked::trackedRepository)
.orElseThrow(() -> trieNotFoundException(header));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ enum PROFILING_TYPE {
BUILD_TRIE_FROM_MSG,
TRIE_TO_MESSAGE, //Currently inactive, to measure, add the hooks in Trie::toMessage() and Trie::toMessageOrchid()
TRIE_CONVERTER_GET_ACCOUNT_ROOT,
BLOCKCHAIN_FLUSH
BLOCKCHAIN_FLUSH,
STORAGE_RENT
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public BlockToMineBuilder(
this.minimumGasPriceCalculator = minimumGasPriceCalculator;
this.minerUtils = minerUtils;
}

/**
* Creates a new block to mine based on the previous mainchain blocks.
*
Expand Down
Loading