/*
* 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.core.AccountState;
import org.ethereum.core.Repository;
import org.ethereum.datasource.*;
import org.ethereum.trie.*;
import org.ethereum.vm.DataWord;
/**
* Created by Anton Nashatyrev on 07.10.2016.
*/
public class RepositoryRoot extends RepositoryImpl {
private static class StorageCache extends ReadWriteCache<DataWord, DataWord> {
Trie<byte[]> trie;
public StorageCache(Trie<byte[]> trie) {
super(new SourceCodec<>(trie, Serializers.StorageKeySerializer, Serializers.StorageValueSerializer), WriteCache.CacheType.SIMPLE);
this.trie = trie;
}
}
private class MultiStorageCache extends MultiCache<StorageCache> {
public MultiStorageCache() {
super(null);
}
@Override
protected synchronized StorageCache create(byte[] key, StorageCache srcCache) {
AccountState accountState = accountStateCache.get(key);
TrieImpl storageTrie = createTrie(trieCache, accountState == null ? null : accountState.getStateRoot());
return new StorageCache(storageTrie);
}
@Override
protected synchronized boolean flushChild(byte[] key, StorageCache childCache) {
if (super.flushChild(key, childCache)) {
if (childCache != null) {
AccountState storageOwnerAcct = accountStateCache.get(key);
// need to update account storage root
childCache.trie.flush();
byte[] rootHash = childCache.trie.getRootHash();
accountStateCache.put(key, storageOwnerAcct.withStateRoot(rootHash));
return true;
} else {
// account was deleted
return true;
}
} else {
// no storage changes
return false;
}
}
}
private Source<byte[], byte[]> stateDS;
private CachedSource.BytesKey<byte[]> trieCache;
private Trie<byte[]> stateTrie;
public RepositoryRoot(Source<byte[], byte[]> stateDS) {
this(stateDS, null);
}
/**
* Building the following structure for snapshot Repository:
*
* stateDS --> trieCacheCodec --> trieCache --> stateTrie --> accountStateCodec --> accountStateCache
* \ \
* \ \-->>> contractStorageTrie --> storageCodec --> StorageCache
* \--> codeCache
*
*
* @param stateDS
* @param root
*/
public RepositoryRoot(final Source<byte[], byte[]> stateDS, byte[] root) {
this.stateDS = stateDS;
trieCache = new WriteCache.BytesKey<>(stateDS, WriteCache.CacheType.COUNTING);
stateTrie = new SecureTrie(trieCache, root);
SourceCodec.BytesKey<AccountState, byte[]> accountStateCodec = new SourceCodec.BytesKey<>(stateTrie, Serializers.AccountStateSerializer);
final ReadWriteCache.BytesKey<AccountState> accountStateCache = new ReadWriteCache.BytesKey<>(accountStateCodec, WriteCache.CacheType.SIMPLE);
final MultiCache<StorageCache> storageCache = new MultiStorageCache();
// counting as there can be 2 contracts with the same code, 1 can suicide
Source<byte[], byte[]> codeCache = new WriteCache.BytesKey<>(stateDS, WriteCache.CacheType.COUNTING);
init(accountStateCache, codeCache, storageCache);
}
@Override
public synchronized void commit() {
super.commit();
stateTrie.flush();
trieCache.flush();
}
@Override
public synchronized byte[] getRoot() {
storageCache.flush();
accountStateCache.flush();
return stateTrie.getRootHash();
}
@Override
public synchronized void flush() {
commit();
}
@Override
public Repository getSnapshotTo(byte[] root) {
return new RepositoryRoot(stateDS, root);
}
@Override
public synchronized String dumpStateTrie() {
return ((TrieImpl) stateTrie).dumpTrie();
}
@Override
public synchronized void syncToRoot(byte[] root) {
stateTrie.setRoot(root);
}
protected TrieImpl createTrie(CachedSource.BytesKey<byte[]> trieCache, byte[] root) {
return new SecureTrie(trieCache, root);
}
}