/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library 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.
*
* The ethereumJ library 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 the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.db;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.core.Repository;
import org.ethereum.crypto.HashUtil;
import org.ethereum.datasource.*;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.FastByteComparisons;
import org.ethereum.vm.DataWord;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.util.*;
/**
* Created by Anton Nashatyrev on 07.10.2016.
*/
public class RepositoryImpl implements Repository, org.ethereum.facade.Repository {
protected RepositoryImpl parent;
protected Source<byte[], AccountState> accountStateCache;
protected Source<byte[], byte[]> codeCache;
protected MultiCache<? extends CachedSource<DataWord, DataWord>> storageCache;
@Autowired
protected SystemProperties config = SystemProperties.getDefault();
protected RepositoryImpl() {
}
public RepositoryImpl(Source<byte[], AccountState> accountStateCache, Source<byte[], byte[]> codeCache,
MultiCache<? extends CachedSource<DataWord, DataWord>> storageCache) {
init(accountStateCache, codeCache, storageCache);
}
protected void init(Source<byte[], AccountState> accountStateCache, Source<byte[], byte[]> codeCache,
MultiCache<? extends CachedSource<DataWord, DataWord>> storageCache) {
this.accountStateCache = accountStateCache;
this.codeCache = codeCache;
this.storageCache = storageCache;
}
@Override
public synchronized AccountState createAccount(byte[] addr) {
AccountState state = new AccountState(config.getBlockchainConfig().getCommonConstants().getInitialNonce(),
BigInteger.ZERO);
accountStateCache.put(addr, state);
return state;
}
@Override
public synchronized boolean isExist(byte[] addr) {
return getAccountState(addr) != null;
}
@Override
public synchronized AccountState getAccountState(byte[] addr) {
return accountStateCache.get(addr);
}
synchronized AccountState getOrCreateAccountState(byte[] addr) {
AccountState ret = accountStateCache.get(addr);
if (ret == null) {
ret = createAccount(addr);
}
return ret;
}
@Override
public synchronized void delete(byte[] addr) {
accountStateCache.delete(addr);
storageCache.delete(addr);
}
@Override
public synchronized BigInteger increaseNonce(byte[] addr) {
AccountState accountState = getOrCreateAccountState(addr);
accountStateCache.put(addr, accountState.withIncrementedNonce());
return accountState.getNonce();
}
@Override
public synchronized BigInteger setNonce(byte[] addr, BigInteger nonce) {
AccountState accountState = getOrCreateAccountState(addr);
accountStateCache.put(addr, accountState.withNonce(nonce));
return accountState.getNonce();
}
@Override
public synchronized BigInteger getNonce(byte[] addr) {
AccountState accountState = getAccountState(addr);
return accountState == null ? config.getBlockchainConfig().getCommonConstants().getInitialNonce() :
accountState.getNonce();
}
@Override
public synchronized ContractDetails getContractDetails(byte[] addr) {
return new ContractDetailsImpl(addr);
}
@Override
public synchronized boolean hasContractDetails(byte[] addr) {
return getContractDetails(addr) != null;
}
@Override
public synchronized void saveCode(byte[] addr, byte[] code) {
byte[] codeHash = HashUtil.sha3(code);
codeCache.put(codeHash, code);
AccountState accountState = getOrCreateAccountState(addr);
accountStateCache.put(addr, accountState.withCodeHash(codeHash));
}
@Override
public synchronized byte[] getCode(byte[] addr) {
byte[] codeHash = getCodeHash(addr);
return FastByteComparisons.equal(codeHash, HashUtil.EMPTY_DATA_HASH) ?
ByteUtil.EMPTY_BYTE_ARRAY : codeCache.get(codeHash);
}
@Override
public byte[] getCodeHash(byte[] addr) {
AccountState accountState = getAccountState(addr);
return accountState != null ? accountState.getCodeHash() : HashUtil.EMPTY_DATA_HASH;
}
@Override
public synchronized void addStorageRow(byte[] addr, DataWord key, DataWord value) {
getOrCreateAccountState(addr);
Source<DataWord, DataWord> contractStorage = storageCache.get(addr);
contractStorage.put(key, value.isZero() ? null : value);
}
@Override
public synchronized DataWord getStorageValue(byte[] addr, DataWord key) {
AccountState accountState = getAccountState(addr);
return accountState == null ? null : storageCache.get(addr).get(key);
}
@Override
public synchronized BigInteger getBalance(byte[] addr) {
AccountState accountState = getAccountState(addr);
return accountState == null ? BigInteger.ZERO : accountState.getBalance();
}
@Override
public synchronized BigInteger addBalance(byte[] addr, BigInteger value) {
AccountState accountState = getOrCreateAccountState(addr);
accountStateCache.put(addr, accountState.withBalanceIncrement(value));
return accountState.getBalance();
}
@Override
public synchronized RepositoryImpl startTracking() {
Source<byte[], AccountState> trackAccountStateCache = new WriteCache.BytesKey<>(accountStateCache,
WriteCache.CacheType.SIMPLE);
Source<byte[], byte[]> trackCodeCache = new WriteCache.BytesKey<>(codeCache, WriteCache.CacheType.SIMPLE);
MultiCache<CachedSource<DataWord, DataWord>> trackStorageCache = new MultiCache(storageCache) {
@Override
protected CachedSource create(byte[] key, CachedSource srcCache) {
return new WriteCache<>(srcCache, WriteCache.CacheType.SIMPLE);
}
};
RepositoryImpl ret = new RepositoryImpl(trackAccountStateCache, trackCodeCache, trackStorageCache);
ret.parent = this;
return ret;
}
@Override
public synchronized Repository getSnapshotTo(byte[] root) {
return parent.getSnapshotTo(root);
}
@Override
public synchronized void commit() {
Repository parentSync = parent == null ? this : parent;
// need to synchronize on parent since between different caches flush
// the parent repo would not be in consistent state
// when no parent just take this instance as a mock
synchronized (parentSync) {
storageCache.flush();
codeCache.flush();
accountStateCache.flush();
}
}
@Override
public synchronized void rollback() {
// nothing to do, will be GCed
}
@Override
public byte[] getRoot() {
throw new RuntimeException("Not supported");
}
public synchronized String getTrieDump() {
return dumpStateTrie();
}
public String dumpStateTrie() {
throw new RuntimeException("Not supported");
}
class ContractDetailsImpl implements ContractDetails {
private byte[] address;
public ContractDetailsImpl(byte[] address) {
this.address = address;
}
@Override
public void put(DataWord key, DataWord value) {
RepositoryImpl.this.addStorageRow(address, key, value);
}
@Override
public DataWord get(DataWord key) {
return RepositoryImpl.this.getStorageValue(address, key);
}
@Override
public byte[] getCode() {
return RepositoryImpl.this.getCode(address);
}
@Override
public byte[] getCode(byte[] codeHash) {
throw new RuntimeException("Not supported");
}
@Override
public void setCode(byte[] code) {
RepositoryImpl.this.saveCode(address, code);
}
@Override
public byte[] getStorageHash() {
throw new RuntimeException("Not supported");
}
@Override
public void decode(byte[] rlpCode) {
throw new RuntimeException("Not supported");
}
@Override
public void setDirty(boolean dirty) {
throw new RuntimeException("Not supported");
}
@Override
public void setDeleted(boolean deleted) {
RepositoryImpl.this.delete(address);
}
@Override
public boolean isDirty() {
throw new RuntimeException("Not supported");
}
@Override
public boolean isDeleted() {
throw new RuntimeException("Not supported");
}
@Override
public byte[] getEncoded() {
throw new RuntimeException("Not supported");
}
@Override
public int getStorageSize() {
throw new RuntimeException("Not supported");
}
@Override
public Set<DataWord> getStorageKeys() {
throw new RuntimeException("Not supported");
}
@Override
public Map<DataWord, DataWord> getStorage(@Nullable Collection<DataWord> keys) {
throw new RuntimeException("Not supported");
}
@Override
public Map<DataWord, DataWord> getStorage() {
throw new RuntimeException("Not supported");
}
@Override
public void setStorage(List<DataWord> storageKeys, List<DataWord> storageValues) {
throw new RuntimeException("Not supported");
}
@Override
public void setStorage(Map<DataWord, DataWord> storage) {
throw new RuntimeException("Not supported");
}
@Override
public byte[] getAddress() {
return address;
}
@Override
public void setAddress(byte[] address) {
throw new RuntimeException("Not supported");
}
@Override
public ContractDetails clone() {
throw new RuntimeException("Not supported");
}
@Override
public void syncStorage() {
throw new RuntimeException("Not supported");
}
@Override
public ContractDetails getSnapshotTo(byte[] hash) {
throw new RuntimeException("Not supported");
}
}
@Override
public Set<byte[]> getAccountsKeys() {
throw new RuntimeException("Not supported");
}
@Override
public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash) {
throw new RuntimeException("Not supported");
}
@Override
public void flush() {
throw new RuntimeException("Not supported");
}
@Override
public void flushNoReconnect() {
throw new RuntimeException("Not supported");
}
@Override
public void syncToRoot(byte[] root) {
throw new RuntimeException("Not supported");
}
@Override
public boolean isClosed() {
throw new RuntimeException("Not supported");
}
@Override
public void close() {
}
@Override
public void reset() {
throw new RuntimeException("Not supported");
}
@Override
public int getStorageSize(byte[] addr) {
throw new RuntimeException("Not supported");
}
@Override
public Set<DataWord> getStorageKeys(byte[] addr) {
throw new RuntimeException("Not supported");
}
@Override
public Map<DataWord, DataWord> getStorage(byte[] addr, @Nullable Collection<DataWord> keys) {
throw new RuntimeException("Not supported");
}
@Override
public void updateBatch(HashMap<ByteArrayWrapper, AccountState> accountStates, HashMap<ByteArrayWrapper, ContractDetails> contractDetailes) {
for (Map.Entry<ByteArrayWrapper, AccountState> entry : accountStates.entrySet()) {
accountStateCache.put(entry.getKey().getData(), entry.getValue());
}
for (Map.Entry<ByteArrayWrapper, ContractDetails> entry : contractDetailes.entrySet()) {
ContractDetails details = getContractDetails(entry.getKey().getData());
for (DataWord key : entry.getValue().getStorageKeys()) {
details.put(key, entry.getValue().get(key));
}
byte[] code = entry.getValue().getCode();
if (code != null && code.length > 0) {
details.setCode(code);
}
}
}
@Override
public void loadAccount(byte[] addr, HashMap<ByteArrayWrapper, AccountState> cacheAccounts, HashMap<ByteArrayWrapper, ContractDetails> cacheDetails) {
throw new RuntimeException("Not supported");
}
}