/* * 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.vm.program; 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.vm.DataWord; import org.ethereum.vm.program.invoke.ProgramInvoke; import org.ethereum.vm.program.listener.ProgramListener; import org.ethereum.vm.program.listener.ProgramListenerAware; import javax.annotation.Nullable; import java.math.BigInteger; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; public class Storage implements Repository, ProgramListenerAware { private final Repository repository; private final DataWord address; private ProgramListener programListener; public Storage(ProgramInvoke programInvoke) { this.address = programInvoke.getOwnerAddress(); this.repository = programInvoke.getRepository(); } @Override public void setProgramListener(ProgramListener listener) { this.programListener = listener; } @Override public AccountState createAccount(byte[] addr) { return repository.createAccount(addr); } @Override public boolean isExist(byte[] addr) { return repository.isExist(addr); } @Override public AccountState getAccountState(byte[] addr) { return repository.getAccountState(addr); } @Override public void delete(byte[] addr) { if (canListenTrace(addr)) programListener.onStorageClear(); repository.delete(addr); } @Override public BigInteger increaseNonce(byte[] addr) { return repository.increaseNonce(addr); } @Override public BigInteger setNonce(byte[] addr, BigInteger nonce) { return repository.setNonce(addr, nonce); } @Override public BigInteger getNonce(byte[] addr) { return repository.getNonce(addr); } @Override public ContractDetails getContractDetails(byte[] addr) { return repository.getContractDetails(addr); } @Override public boolean hasContractDetails(byte[] addr) { return repository.hasContractDetails(addr); } @Override public void saveCode(byte[] addr, byte[] code) { repository.saveCode(addr, code); } @Override public byte[] getCode(byte[] addr) { return repository.getCode(addr); } @Override public byte[] getCodeHash(byte[] addr) { return repository.getCodeHash(addr); } @Override public void addStorageRow(byte[] addr, DataWord key, DataWord value) { if (canListenTrace(addr)) programListener.onStoragePut(key, value); repository.addStorageRow(addr, key, value); } private boolean canListenTrace(byte[] address) { return (programListener != null) && this.address.equals(new DataWord(address)); } @Override public DataWord getStorageValue(byte[] addr, DataWord key) { return repository.getStorageValue(addr, key); } @Override public BigInteger getBalance(byte[] addr) { return repository.getBalance(addr); } @Override public BigInteger addBalance(byte[] addr, BigInteger value) { return repository.addBalance(addr, value); } @Override public Set<byte[]> getAccountsKeys() { return repository.getAccountsKeys(); } @Override public void dumpState(Block block, long gasUsed, int txNumber, byte[] txHash) { repository.dumpState(block, gasUsed, txNumber, txHash); } @Override public Repository startTracking() { return repository.startTracking(); } @Override public void flush() { repository.flush(); } @Override public void flushNoReconnect() { throw new UnsupportedOperationException(); } @Override public void commit() { repository.commit(); } @Override public void rollback() { repository.rollback(); } @Override public void syncToRoot(byte[] root) { repository.syncToRoot(root); } @Override public boolean isClosed() { return repository.isClosed(); } @Override public void close() { repository.close(); } @Override public void reset() { repository.reset(); } @Override public void updateBatch(HashMap<ByteArrayWrapper, AccountState> accountStates, HashMap<ByteArrayWrapper, ContractDetails> contractDetails) { for (ByteArrayWrapper address : contractDetails.keySet()) { if (!canListenTrace(address.getData())) return; ContractDetails details = contractDetails.get(address); if (details.isDeleted()) { programListener.onStorageClear(); } else if (details.isDirty()) { for (Map.Entry<DataWord, DataWord> entry : details.getStorage().entrySet()) { programListener.onStoragePut(entry.getKey(), entry.getValue()); } } } repository.updateBatch(accountStates, contractDetails); } @Override public byte[] getRoot() { return repository.getRoot(); } @Override public void loadAccount(byte[] addr, HashMap<ByteArrayWrapper, AccountState> cacheAccounts, HashMap<ByteArrayWrapper, ContractDetails> cacheDetails) { repository.loadAccount(addr, cacheAccounts, cacheDetails); } @Override public Repository getSnapshotTo(byte[] root) { throw new UnsupportedOperationException(); } @Override public int getStorageSize(byte[] addr) { return repository.getStorageSize(addr); } @Override public Set<DataWord> getStorageKeys(byte[] addr) { return repository.getStorageKeys(addr); } @Override public Map<DataWord, DataWord> getStorage(byte[] addr, @Nullable Collection<DataWord> keys) { return repository.getStorage(addr, keys); } }