Skip to content

Commit

Permalink
Merge pull request #2778 from rsksmart/fmacleal/addition_transient_st…
Browse files Browse the repository at this point in the history
…orage_opcodes

Addition of Transient Storage Opcodes
  • Loading branch information
Vovchyk authored Dec 23, 2024
2 parents fb1957f + a1168bb commit 87bde23
Show file tree
Hide file tree
Showing 48 changed files with 5,549 additions and 183 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ public enum ConsensusRule {
RSKIP434("rskip434"),
RSKIP438("rskip438"),
RSKIP445("rskip445"), // From EIP-5656 MCOPY instruction
RSKIP446("rskip446") ,// Transient storage opcodes addition implementing EIP-1153
RSKIP453("rskip453"),
RSKIP454("rskip454"),
RSKIP454("rskip454")
;

private final String configKey;
Expand Down
2 changes: 1 addition & 1 deletion rskj-core/src/main/java/org/ethereum/core/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

import java.math.BigInteger;

public interface Repository extends RepositorySnapshot {
public interface Repository extends RepositorySnapshot, TransientRepository {
Trie getTrie();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,8 @@ private void finalization() {
// Traverse list of suicides
result.getDeleteAccounts().forEach(address -> track.delete(new RskAddress(address)));

track.clearTransientStorage();

logger.trace("tx listener done");

logger.trace("tx finalization done");
Expand Down
40 changes: 40 additions & 0 deletions rskj-core/src/main/java/org/ethereum/core/TransientRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* This file is part of RskJ
* Copyright (C) 2024 RSK Labs Ltd.
* (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.ethereum.core;

import co.rsk.core.RskAddress;
import org.ethereum.vm.DataWord;

import javax.annotation.Nullable;

public interface TransientRepository {

void addTransientStorageRow(RskAddress addr, DataWord key, DataWord value);

void addTransientStorageBytes(RskAddress addr, DataWord key, byte[] value);

void clearTransientStorage();

@Nullable
DataWord getTransientStorageValue(RskAddress addr, DataWord key);

@Nullable
byte[] getTransientStorageBytes(RskAddress addr, DataWord key);
}
80 changes: 75 additions & 5 deletions rskj-core/src/main/java/org/ethereum/db/MutableRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,29 @@
import co.rsk.crypto.Keccak256;
import co.rsk.db.MutableTrieCache;
import co.rsk.db.MutableTrieImpl;
import co.rsk.trie.*;
import co.rsk.trie.IterationElement;
import co.rsk.trie.MutableTrie;
import co.rsk.trie.Trie;
import co.rsk.trie.TrieKeySlice;
import co.rsk.trie.TrieStore;
import co.rsk.trie.TrieStoreImpl;
import com.google.common.annotations.VisibleForTesting;
import org.ethereum.core.AccountState;
import org.ethereum.core.Repository;
import org.ethereum.crypto.HashUtil;
import org.ethereum.crypto.Keccak256Helper;
import org.ethereum.datasource.HashMapDB;
import org.ethereum.vm.DataWord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.util.*;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;

public class MutableRepository implements Repository {
private static final Logger logger = LoggerFactory.getLogger("repository");
Expand All @@ -47,21 +57,36 @@ public class MutableRepository implements Repository {

private final TrieKeyMapper trieKeyMapper;
private final MutableTrie mutableTrie;
private MutableTrie transientTrie;
private final IReadWrittenKeysTracker tracker;

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

public MutableRepository(MutableTrie mutableTrie) {
this(mutableTrie, aInMemoryMutableTrie());
}

public MutableRepository(MutableTrie mutableTrie, IReadWrittenKeysTracker tracker) {
this(mutableTrie, aInMemoryMutableTrie(), tracker);
}

private static MutableTrieImpl aInMemoryMutableTrie() {
return new MutableTrieImpl(new TrieStoreImpl(new HashMapDB()), new Trie());
}

public MutableRepository(MutableTrie mutableTrie, MutableTrie transientTrie) {
this.trieKeyMapper = new TrieKeyMapper();
this.mutableTrie = mutableTrie;
this.transientTrie = transientTrie;
this.tracker = new DummyReadWrittenKeysTracker();
}

public MutableRepository(MutableTrie mutableTrie, IReadWrittenKeysTracker tracker) {
public MutableRepository(MutableTrie mutableTrie, MutableTrie transientTrie, IReadWrittenKeysTracker tracker) {
this.trieKeyMapper = new TrieKeyMapper();
this.mutableTrie = mutableTrie;
this.transientTrie = transientTrie;
this.tracker = tracker;
}

Expand Down Expand Up @@ -327,7 +352,7 @@ public synchronized Set<RskAddress> getAccountsKeys() {
// To start tracking, a new repository is created, with a MutableTrieCache in the middle
@Override
public synchronized Repository startTracking() {
return new MutableRepository(new MutableTrieCache(mutableTrie), tracker);
return new MutableRepository(new MutableTrieCache(mutableTrie), new MutableTrieCache(transientTrie), tracker);
}

@Override
Expand All @@ -338,11 +363,13 @@ public void save() {
@Override
public synchronized void commit() {
mutableTrie.commit();
transientTrie.commit();
}

@Override
public synchronized void rollback() {
mutableTrie.rollback();
transientTrie.rollback();
}

@Override
Expand Down Expand Up @@ -406,4 +433,47 @@ private Optional<Keccak256> internalGetValueHash(byte[] key) {
tracker.addNewReadKey(new ByteArrayWrapper(key));
return mutableTrie.getValueHash(key);
}

@Override
public void addTransientStorageRow(RskAddress addr, DataWord key, DataWord value) {
addTransientStorageBytes(addr, key, value.getByteArrayForStorage());
}

@Override
public void addTransientStorageBytes(RskAddress addr, DataWord key, byte[] value) {
byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key);

// Special case: if the value is an empty vector, we pass "null" which commands the trie to remove the item.
// Note that if the call comes from addTransientStorageRow(), this method will already have replaced 0 by null, so the
// conversion here only applies if this is called directly. If suppose this only occurs in tests, but it can
// also occur in precompiled contracts that store data directly using this method.
if (value == null || value.length == 0) {
transientTrie.put(triekey, null);
} else {
transientTrie.put(triekey, value);
}
}

@Override
public void clearTransientStorage() {
this.transientTrie = aInMemoryMutableTrie();
}

@Nullable
@Override
public DataWord getTransientStorageValue(RskAddress addr, DataWord key) {
byte[] value = getTransientStorageBytes(addr, key);
if (value == null) {
return null;
}

return DataWord.valueOf(value);
}

@Nullable
@Override
public byte[] getTransientStorageBytes(RskAddress addr, DataWord key) {
byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key);
return transientTrie.get(triekey);
}
}
3 changes: 3 additions & 0 deletions rskj-core/src/main/java/org/ethereum/vm/GasCost.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public class GasCost {
public static final long REFUND_SSTORE = 15000;
public static final long CREATE = 32000;

public static final long TLOAD = 100;
public static final long TSTORE = 100;

public static final long JUMPDEST = 1;
public static final long CREATE_DATA_BYTE = 5;
public static final long CALL = 700;
Expand Down
10 changes: 10 additions & 0 deletions rskj-core/src/main/java/org/ethereum/vm/OpCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,16 @@ public enum OpCode {
*/
MCOPY(0x5e, 3, 0, VERY_LOW_TIER),

/**
* (0x5c) Load word from transient storage at address
*/
TLOAD(0x5c, 1, 1, SPECIAL_TIER), // Will adjust the correct inputs and outputs later

/**
* (0x5c) Store word from transient storage at address
*/
TSTORE(0x5d, 2, 0, SPECIAL_TIER), // Will adjust the correct inputs and outputs later

/* Push Operations */
/**
* (0x5f) Pushes the constant value 0 onto the stack.
Expand Down
8 changes: 8 additions & 0 deletions rskj-core/src/main/java/org/ethereum/vm/OpCodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ private OpCodes() {
* (0x5e)
*/
public static final byte OP_MCOPY = 0x5e;
/**
* (0x5c)
*/
public static final byte OP_TLOAD =0x5c ;
/**
* (0x5d)
*/
public static final byte OP_TSTORE =0x5d ;

/* Push Operations */
/**
Expand Down
Loading

0 comments on commit 87bde23

Please sign in to comment.