org.ethereum.db.ContractDetailsCacheImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ethereum.db.ContractDetailsCacheImpl.java

Source

/*
 * This file is part of RskJ
 * Copyright (C) 2017 RSK Labs Ltd.
 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.ethereum.db;

import co.rsk.db.ContractDetailsImpl;
import co.rsk.trie.Trie;
import co.rsk.trie.TrieImpl;
import org.apache.commons.collections4.MapUtils;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPElement;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.ethereum.vm.DataWord;
import org.spongycastle.util.encoders.Hex;

import java.util.*;

import static java.util.Collections.unmodifiableMap;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;

/**
 * @author Roman Mandeleil
 * @since 24.06.2014
 */
public class ContractDetailsCacheImpl implements ContractDetails {

    private Map<DataWord, DataWord> storage = new HashMap<>();
    private Map<DataWord, byte[]> bytesStorage = new HashMap<>();

    ContractDetails origContract = new ContractDetailsImpl();

    private byte[] code = EMPTY_BYTE_ARRAY;

    private boolean dirty = false;
    private boolean deleted = false;

    public ContractDetailsCacheImpl(ContractDetails origContract) {
        this.origContract = origContract;
        this.code = origContract != null ? origContract.getCode() : EMPTY_BYTE_ARRAY;
    }

    @Override
    public void put(DataWord key, DataWord value) {
        storage.put(key, value);
        this.setDirty(true);
    }

    @Override
    public void putBytes(DataWord key, byte[] value) {
        bytesStorage.put(key, value);
        this.setDirty(true);
    }

    @Override
    public DataWord get(DataWord key) {

        DataWord value = storage.get(key);
        if (value != null)
            value = value.clone();
        else {
            if (origContract == null)
                return null;
            value = origContract.get(key);
            storage.put(key.clone(), value == null ? DataWord.ZERO.clone() : value.clone());
        }

        if (value == null || value.isZero())
            return null;
        else
            return value;
    }

    @Override
    public byte[] getBytes(DataWord key) {

        byte[] value = bytesStorage.get(key);
        if (value != null)
            value = value.clone();
        else {
            if (origContract == null)
                return null;
            value = origContract.getBytes(key);
            bytesStorage.put(key.clone(), value == null ? null : value.clone());
        }

        if (value == null)
            return null;
        else
            return value;
    }

    @Override
    public byte[] getCode() {
        return code;
    }

    @Override
    public void setCode(byte[] code) {
        this.code = code;
    }

    @Override
    public byte[] getStorageHash() { // todo: unsupported
        Trie storageTrie = new TrieImpl(null, true);

        for (DataWord key : storage.keySet()) {

            DataWord value = storage.get(key);

            storageTrie = storageTrie.put(key.getData(), RLP.encodeElement(value.getNoLeadZeroesData()));
        }

        for (DataWord key : bytesStorage.keySet()) {
            byte[] value = bytesStorage.get(key);

            storageTrie = storageTrie.put(key.getData(), RLP.encodeElement(value));
        }

        return storageTrie.getHash();
    }

    @Override
    public void decode(byte[] rlpCode) {
        RLPList data = RLP.decode2(rlpCode);
        RLPList rlpList = (RLPList) data.get(0);

        RLPList keys = (RLPList) rlpList.get(0);
        RLPList values = (RLPList) rlpList.get(1);
        RLPElement code = rlpList.get(2);

        for (int i = 0; i < keys.size(); ++i) {

            RLPItem key = (RLPItem) keys.get(i);
            RLPItem value = (RLPItem) values.get(i);

            storage.put(new DataWord(key.getRLPData()), new DataWord(value.getRLPData()));
        }

        this.code = (code.getRLPData() == null) ? EMPTY_BYTE_ARRAY : code.getRLPData();

        if (rlpList.size() > 3) {
            RLPList keys2 = (RLPList) rlpList.get(3);
            RLPList values2 = (RLPList) rlpList.get(4);

            for (int i = 0; i < keys2.size(); ++i) {

                RLPItem key = (RLPItem) keys2.get(i);
                RLPItem value = (RLPItem) values2.get(i);

                bytesStorage.put(new DataWord(key.getRLPData()), value.getRLPData());
            }
        }
    }

    @Override
    public void setDirty(boolean dirty) {
        this.dirty = dirty;
    }

    @Override
    public void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }

    @Override
    public boolean isDirty() {
        return dirty;
    }

    @Override
    public boolean isDeleted() {
        return deleted;
    }

    @Override
    public byte[] getEncoded() {

        byte[][] keys = new byte[storage.size()][];
        byte[][] values = new byte[storage.size()][];
        byte[][] keys2 = new byte[storage.size()][];
        byte[][] values2 = new byte[storage.size()][];

        int i = 0;
        for (DataWord key : storage.keySet()) {

            DataWord value = storage.get(key);

            keys[i] = RLP.encodeElement(key.getData());
            values[i] = RLP.encodeElement(value.getNoLeadZeroesData());

            ++i;
        }

        for (DataWord key : bytesStorage.keySet()) {
            byte[] value = bytesStorage.get(key);

            keys[i] = RLP.encodeElement(key.getData());
            values[i] = RLP.encodeElement(value);

            ++i;
        }

        byte[] rlpKeysList = RLP.encodeList(keys);
        byte[] rlpValuesList = RLP.encodeList(values);
        byte[] rlpCode = RLP.encodeElement(code);

        if (!bytesStorage.isEmpty()) {
            i = 0;
            for (DataWord key : bytesStorage.keySet()) {
                byte[] value = bytesStorage.get(key);

                keys2[i] = RLP.encodeElement(key.getData());
                values2[i] = RLP.encodeElement(value);

                ++i;
            }

            byte[] rlpKeysList2 = RLP.encodeList(keys2);
            byte[] rlpValuesList2 = RLP.encodeList(values2);

            return RLP.encodeList(rlpKeysList, rlpValuesList, rlpCode, rlpKeysList, rlpValuesList);
        } else
            return RLP.encodeList(rlpKeysList, rlpValuesList, rlpCode);
    }

    @Override
    public Map<DataWord, DataWord> getStorage() {
        return unmodifiableMap(storage);
    }

    @Override
    public Map<DataWord, DataWord> getStorage(Collection<DataWord> keys) {
        if (keys == null)
            return getStorage();

        Map<DataWord, DataWord> result = new HashMap<>();
        for (DataWord key : keys) {
            result.put(key, storage.get(key));
        }
        return unmodifiableMap(result);
    }

    @Override
    public int getStorageSize() {
        return (origContract == null) ? storage.size() : origContract.getStorageSize();
    }

    @Override
    public Set<DataWord> getStorageKeys() {
        return (origContract == null) ? storage.keySet() : origContract.getStorageKeys();
    }

    @Override
    public void setStorage(List<DataWord> storageKeys, List<DataWord> storageValues) {

        for (int i = 0; i < storageKeys.size(); ++i) {

            DataWord key = storageKeys.get(i);
            DataWord value = storageValues.get(i);

            if (value.isZero())
                storage.put(key, null);
        }

    }

    @Override
    public void setStorage(Map<DataWord, DataWord> storage) {
        this.storage = storage;
    }

    @Override
    public byte[] getAddress() {
        return (origContract == null) ? null : origContract.getAddress();
    }

    @Override
    public void setAddress(byte[] address) {
        if (origContract != null)
            origContract.setAddress(address);
    }

    @Override
    public ContractDetails clone() {

        ContractDetailsCacheImpl contractDetails = new ContractDetailsCacheImpl(origContract);

        Object storageClone = ((HashMap<DataWord, DataWord>) storage).clone();

        contractDetails.setCode(this.getCode());
        contractDetails.setStorage((HashMap<DataWord, DataWord>) storageClone);
        //WARNING bytesStorage is not cloned. Is this a bug?
        return contractDetails;
    }

    @Override
    public String toString() {

        String ret = "  Code: " + Hex.toHexString(code) + "\n";
        ret += "  Storage: " + getStorage().toString();

        return ret;
    }

    @Override
    public void syncStorage() {
        if (origContract != null)
            origContract.syncStorage();
    }

    public void commit() {

        if (origContract == null)
            return;

        for (DataWord key : storage.keySet()) {
            origContract.put(key, storage.get(key));
        }

        for (DataWord key : bytesStorage.keySet()) {
            origContract.putBytes(key, bytesStorage.get(key));
        }

        origContract.setCode(code);
        origContract.setDirty(this.dirty || origContract.isDirty());
    }

    @Override
    public ContractDetails getSnapshotTo(byte[] hash) {
        throw new UnsupportedOperationException("No snapshot option during cache state");
    }

    @Override
    public boolean isNullObject() {
        return origContract.isNullObject() && (MapUtils.isEmpty(storage));
    }

    public ContractDetails getOriginalContractDetails() {
        return this.origContract;
    }

    public void setOriginalContractDetails(ContractDetails contractDetails) {
        this.origContract = contractDetails;
    }
}