/* * 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"); } }