/*
* 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.jsontestsuite.suite;
import org.ethereum.config.CommonConfig;
import org.ethereum.config.SystemProperties;
import org.ethereum.datasource.DbSource;
import org.ethereum.datasource.Source;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.ContractDetails;
import org.ethereum.trie.SecureTrie;
import org.ethereum.util.*;
import org.ethereum.vm.DataWord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.*;
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
import static org.ethereum.crypto.HashUtil.sha3;
import static org.ethereum.util.ByteUtil.*;
/**
* @author Roman Mandeleil
* @since 24.06.2014
*/
@Component
@Scope("prototype")
public class ContractDetailsImpl extends AbstractContractDetails {
private static final Logger logger = LoggerFactory.getLogger("general");
CommonConfig commonConfig = CommonConfig.getDefault();
SystemProperties config = SystemProperties.getDefault();
DbSource dataSource;
private byte[] address = EMPTY_BYTE_ARRAY;
private Set<ByteArrayWrapper> keys = new HashSet<>();
private SecureTrie storageTrie = new SecureTrie((byte[]) null);
boolean externalStorage;
private DbSource externalStorageDataSource;
/** Tests only **/
public ContractDetailsImpl() {
}
private ContractDetailsImpl(byte[] address, SecureTrie storageTrie, Map<ByteArrayWrapper, byte[]> codes) {
this.address = address;
this.storageTrie = storageTrie;
setCodes(codes);
}
private void addKey(byte[] key) {
keys.add(wrap(key));
}
private void removeKey(byte[] key) {
// keys.remove(wrap(key)); // TODO: we can't remove keys , because of fork branching
}
@Override
public void put(DataWord key, DataWord value) {
if (value.equals(DataWord.ZERO)) {
storageTrie.delete(key.getData());
removeKey(key.getData());
} else {
storageTrie.put(key.getData(), RLP.encodeElement(value.getNoLeadZeroesData()));
addKey(key.getData());
}
this.setDirty(true);
}
@Override
public DataWord get(DataWord key) {
DataWord result = null;
byte[] data = storageTrie.get(key.getData());
if (data.length > 0) {
byte[] dataDecoded = RLP.decode2(data).get(0).getRLPData();
result = new DataWord(dataDecoded);
}
return result;
}
@Override
public byte[] getStorageHash() {
return storageTrie.getRootHash();
}
@Override
public void decode(byte[] rlpCode) {
throw new RuntimeException("Not supported");
}
@Override
public byte[] getEncoded() {
throw new RuntimeException("Not supported");
}
@Override
public Map<DataWord, DataWord> getStorage(Collection<DataWord> keys) {
Map<DataWord, DataWord> storage = new HashMap<>();
if (keys == null) {
for (ByteArrayWrapper keyBytes : this.keys) {
DataWord key = new DataWord(keyBytes);
DataWord value = get(key);
// we check if the value is not null,
// cause we keep all historical keys
if (value != null)
storage.put(key, value);
}
} else {
for (DataWord key : keys) {
DataWord value = get(key);
// we check if the value is not null,
// cause we keep all historical keys
if (value != null)
storage.put(key, value);
}
}
return storage;
}
@Override
public Map<DataWord, DataWord> getStorage() {
return getStorage(null);
}
@Override
public int getStorageSize() {
return keys.size();
}
@Override
public Set<DataWord> getStorageKeys() {
Set<DataWord> result = new HashSet<>();
for (ByteArrayWrapper key : keys) {
result.add(new DataWord(key));
}
return result;
}
@Override
public void setStorage(List<DataWord> storageKeys, List<DataWord> storageValues) {
for (int i = 0; i < storageKeys.size(); ++i)
put(storageKeys.get(i), storageValues.get(i));
}
@Override
public void setStorage(Map<DataWord, DataWord> storage) {
for (DataWord key : storage.keySet()) {
put(key, storage.get(key));
}
}
@Override
public byte[] getAddress() {
return address;
}
@Override
public void setAddress(byte[] address) {
this.address = address;
}
public SecureTrie getStorageTrie() {
return storageTrie;
}
@Override
public void syncStorage() {
}
@Override
public ContractDetails clone() {
// FIXME: clone is not working now !!!
// FIXME: should be fixed
// storageTrie.getRoot();
return new ContractDetailsImpl(address, null, getCodes());
}
@Override
public ContractDetails getSnapshotTo(byte[] hash){
Source<byte[], byte[]> cache = this.storageTrie.getCache();
SecureTrie snapStorage = wrap(hash).equals(wrap(EMPTY_TRIE_HASH)) ?
new SecureTrie(cache, "".getBytes()):
new SecureTrie(cache, hash);
ContractDetailsImpl details = new ContractDetailsImpl(this.address, snapStorage, getCodes());
details.externalStorage = this.externalStorage;
details.externalStorageDataSource = this.externalStorageDataSource;
details.keys = this.keys;
details.config = config;
details.commonConfig = commonConfig;
details.dataSource = dataSource;
return details;
}
}