/* * 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.core.AccountState; import org.ethereum.core.Block; import org.ethereum.core.Repository; import org.ethereum.db.ByteArrayWrapper; import org.ethereum.db.ContractDetails; import org.ethereum.util.ByteArrayMap; import org.ethereum.util.ByteArraySet; import org.ethereum.vm.DataWord; import javax.annotation.Nullable; import java.math.BigInteger; import java.util.*; /** * Created by Anton Nashatyrev on 01.12.2016. */ public class IterableTestRepository implements Repository { Repository src; Set<byte[]> accounts = new ByteArraySet(); Map<byte[], Set<DataWord>> storageKeys = new ByteArrayMap<>(); boolean environmental; private IterableTestRepository(Repository src, IterableTestRepository parent) { this.src = src; if (parent != null) { this.accounts = parent.accounts; this.storageKeys = parent.storageKeys; this.environmental = parent.environmental; } } public IterableTestRepository(Repository src) { this(src, null); } void addAccount(byte[] addr) { accounts.add(addr); } private void addStorageKey(byte[] acct, DataWord key) { addAccount(acct); Set<DataWord> keys = storageKeys.get(acct); if (keys == null) { keys = new HashSet<>(); storageKeys.put(acct, keys); } keys.add(key); } @Override public Repository startTracking() { return new IterableTestRepository(src.startTracking(), this); } @Override public Repository getSnapshotTo(byte[] root) { return new IterableTestRepository(src.getSnapshotTo(root), this); } @Override public AccountState createAccount(byte[] addr) { addAccount(addr); return src.createAccount(addr); } @Override public boolean isExist(byte[] addr) { return src.isExist(addr); } @Override public AccountState getAccountState(byte[] addr) { return src.getAccountState(addr); } @Override public void delete(byte[] addr) { addAccount(addr); src.delete(addr); } @Override public BigInteger increaseNonce(byte[] addr) { addAccount(addr); return src.increaseNonce(addr); } @Override public BigInteger setNonce(byte[] addr, BigInteger nonce) { return src.setNonce(addr, nonce); } @Override public BigInteger getNonce(byte[] addr) { return src.getNonce(addr); } @Override public ContractDetails getContractDetails(byte[] addr) { return new IterableContractDetails(src.getContractDetails(addr)); } @Override public boolean hasContractDetails(byte[] addr) { return src.hasContractDetails(addr); } @Override public void saveCode(byte[] addr, byte[] code) { addAccount(addr); src.saveCode(addr, code); } @Override public byte[] getCode(byte[] addr) { if (environmental) { if (!src.isExist(addr)) { createAccount(addr); } } return src.getCode(addr); } @Override public byte[] getCodeHash(byte[] addr) { return src.getCodeHash(addr); } @Override public void addStorageRow(byte[] addr, DataWord key, DataWord value) { addStorageKey(addr, key); src.addStorageRow(addr, key, value); } @Override public DataWord getStorageValue(byte[] addr, DataWord key) { return src.getStorageValue(addr, key); } @Override public BigInteger getBalance(byte[] addr) { if (environmental) { if (!src.isExist(addr)) { createAccount(addr); } } return src.getBalance(addr); } @Override public BigInteger addBalance(byte[] addr, BigInteger value) { addAccount(addr); return src.addBalance(addr, value); } @Override public Set<byte[]> getAccountsKeys() { Set<byte[]> ret = new ByteArraySet(); for (byte[] account : accounts) { if (isExist(account)) { ret.add(account); } } return ret; } @Override public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash) { src.dumpState(block, gasUsed, txNumber, txHash); } @Override public void flush() { src.flush(); } @Override public void flushNoReconnect() { src.flushNoReconnect(); } @Override public void commit() { src.commit(); } @Override public void rollback() { src.rollback(); } @Override public void syncToRoot(byte[] root) { src.syncToRoot(root); } @Override public boolean isClosed() { return src.isClosed(); } @Override public void close() { src.close(); } @Override public void reset() { src.reset(); } @Override public void updateBatch(HashMap<ByteArrayWrapper, AccountState> accountStates, HashMap<ByteArrayWrapper, ContractDetails> contractDetailes) { src.updateBatch(accountStates, contractDetailes); for (ByteArrayWrapper wrapper : accountStates.keySet()) { addAccount(wrapper.getData()); } for (Map.Entry<ByteArrayWrapper, ContractDetails> entry : contractDetailes.entrySet()) { for (DataWord key : entry.getValue().getStorageKeys()) { addStorageKey(entry.getKey().getData(), key); } } } @Override public byte[] getRoot() { return src.getRoot(); } @Override public void loadAccount(byte[] addr, HashMap<ByteArrayWrapper, AccountState> cacheAccounts, HashMap<ByteArrayWrapper, ContractDetails> cacheDetails) { src.loadAccount(addr, cacheAccounts, cacheDetails); } @Override public int getStorageSize(byte[] addr) { return src.getStorageSize(addr); } @Override public Set<DataWord> getStorageKeys(byte[] addr) { return src.getStorageKeys(addr); } @Override public Map<DataWord, DataWord> getStorage(byte[] addr, @Nullable Collection<DataWord> keys) { return src.getStorage(addr, keys); } private class IterableContractDetails implements ContractDetails { ContractDetails src; public IterableContractDetails(ContractDetails src) { this.src = src; } @Override public void put(DataWord key, DataWord value) { addStorageKey(getAddress(), key); src.put(key, value); } @Override public DataWord get(DataWord key) { return src.get(key); } @Override public byte[] getCode() { return src.getCode(); } @Override public byte[] getCode(byte[] codeHash) { return src.getCode(codeHash); } @Override public void setCode(byte[] code) { addAccount(getAddress()); src.setCode(code); } @Override public byte[] getStorageHash() { return src.getStorageHash(); } @Override public void decode(byte[] rlpCode) { src.decode(rlpCode); } @Override public void setDirty(boolean dirty) { src.setDirty(dirty); } @Override public void setDeleted(boolean deleted) { src.setDeleted(deleted); } @Override public boolean isDirty() { return src.isDirty(); } @Override public boolean isDeleted() { return src.isDeleted(); } @Override public byte[] getEncoded() { return src.getEncoded(); } @Override public int getStorageSize() { Set<DataWord> set = storageKeys.get(getAddress()); return set == null ? 0 : set.size(); } @Override public Set<DataWord> getStorageKeys() { return getStorage().keySet(); } @Override public Map<DataWord, DataWord> getStorage(@Nullable Collection<DataWord> keys) { throw new RuntimeException(); } @Override public Map<DataWord, DataWord> getStorage() { Map<DataWord, DataWord> ret = new HashMap<>(); Set<DataWord> set = storageKeys.get(getAddress()); if (set == null) return Collections.emptyMap(); for (DataWord key : set) { DataWord val = get(key); if (val != null && !val.isZero()) { ret.put(key, get(key)); } } return ret; } @Override public void setStorage(List<DataWord> storageKeys, List<DataWord> storageValues) { src.setStorage(storageKeys, storageValues); } @Override public void setStorage(Map<DataWord, DataWord> storage) { src.setStorage(storage); } @Override public byte[] getAddress() { return src.getAddress(); } @Override public void setAddress(byte[] address) { src.setAddress(address); } @Override public ContractDetails clone() { return new IterableContractDetails(src.clone()); } @Override public String toString() { return src.toString(); } @Override public void syncStorage() { src.syncStorage(); } @Override public ContractDetails getSnapshotTo(byte[] hash) { return new IterableContractDetails(src.getSnapshotTo(hash)); } } }