类:org.ethereum.db.IndexedBlockStore
此类为数据源映射类,别的业务都通过此类,来统一作为入口,进行查询、存储区块链的区块信息等。此类封装了底层使用的是什么样的资源做存储,数据源可以是缓存,也可以是rockdb,可以是leveldb,也可以是数据库,等等。都由该类统一映射。
## 内部类
BlockInfo
## 方法
init
getBestBlock
getBlockHashByNumber
flush
saveBlock
addInternalBlock
putBlockInfo
getBlocksByNumber
getChainBlockByNumber
getBlockByHash
isBlockExist
getTotalDifficultyForHash
getTotalDifficulty
updateTotDifficulties
getMaxNumber
getListHashesEndWith
getListHeadersEndWith
getListBlocksEndWith
getListBlocksEndWithInner
reBranch
getListHashesStartWith
printChain
getBlockInfoForLevel
setBlockInfoForLevel
getBlockInfoForHash
## 参数
load
close
logger
indexDS
index
blocksDS
blocks
BLOCK_INFO_SERIALIZER
有2个核心的成员变量:
DataSourceArray<List<BlockInfo>> index;
ObjectDataSource<Block> blocks;
- index 存储一个区块链中区块号和区块描述信息。key:value = 区块号:区块描述信息。
- blocks 存储区块链中的区块hash和区块全信息。key:value = 区块hash:区块全信息。
测试
进行一个简单的测试,来感受一下此类的作用。
package org.ethereum.core;
import org.ethereum.config.SystemProperties;
import org.ethereum.datasource.DbSource;
import org.ethereum.datasource.inmem.HashMapDB;
import org.ethereum.datasource.rocksdb.RocksDbDataSource;
import org.ethereum.db.IndexedBlockStore;
import org.ethereum.util.FileUtil;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static java.math.BigInteger.ZERO;
import static org.junit.Assert.assertEquals;
public class IndexedBlockStoreTest {
    private static final Logger logger = LoggerFactory.getLogger("test");
    private List<Block> blocks = new ArrayList<>();
    private BigInteger cumDifficulty = ZERO;
    @AfterClass
    public static void cleanup() {
        SystemProperties.resetToDefault();
    }
    /**
     * 加载`blockstore/load.dmp`,生成一个block集合,为后续测试做准备。
     */
    @Before
    public void setup() throws URISyntaxException, IOException {
        URL scenario1 = ClassLoader.getSystemResource("blockstore/load.dmp");
        File file = new File(scenario1.toURI());
        List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
        Block genesis = Genesis.getInstance();
        blocks.add(genesis);
        cumDifficulty = cumDifficulty.add(genesis.getCumulativeDifficulty());
        for (String blockRLP : strData) {
            Block block = new Block(Hex.decode(blockRLP));
            if (block.getNumber() % 1000 == 0)
                logger.info("adding block.hash: [{}] block.number: [{}]",
                        block.getShortHash(),
                        block.getNumber());
            blocks.add(block);
            cumDifficulty = cumDifficulty.add(block.getCumulativeDifficulty());
        }
        logger.info("total difficulty: {}", cumDifficulty);
        logger.info("total blocks loaded: {}", blocks.size());
    }
    /**
     * 把区块信息写入缓存,并检查它是否存在
     */
    @Test
    public void test1() {
        IndexedBlockStore indexedBlockStore = new IndexedBlockStore();
        // 注意,初始化的参数是new HashMapDB<byte[],是个缓存,所以后续的存储都是保存到缓存里了。唯一的差别就是这里了
        indexedBlockStore.init(new HashMapDB<byte[]>(), new HashMapDB<byte[]>());
        BigInteger cummDiff = BigInteger.ZERO;
        for (Block block : blocks) {
            cummDiff = cummDiff.add(block.getCumulativeDifficulty());
            indexedBlockStore.saveBlock(block, cummDiff, true);
        }
        //  testing:   getTotalDifficulty()
        //  testing:   getMaxNumber()
        long bestIndex = blocks.get(blocks.size() - 1).getNumber();
        assertEquals(bestIndex, indexedBlockStore.getMaxNumber());
        assertEquals(cumDifficulty, indexedBlockStore.getTotalDifficulty());
        //  testing:  getBlockByHash(byte[])
        Block block = blocks.get(50);
        Block block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(150);
        block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(0);
        block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(8003);
        block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block_ = indexedBlockStore.getBlockByHash(Hex.decode("00112233"));
        assertEquals(null, block_);
        //  testing:  getChainBlockByNumber(long)
        block = blocks.get(50);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(150);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(0);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(8003);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block_ = indexedBlockStore.getChainBlockByNumber(10000);
        assertEquals(null, block_);
        //  testing: getBlocksByNumber(long)
        block = blocks.get(50);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(150);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(0);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(8003);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        int blocksNum = indexedBlockStore.getBlocksByNumber(10000).size();
        assertEquals(0, blocksNum);
        //  testing: getListHashesEndWith(byte[], long)
        block = blocks.get(8003);
        List<byte[]> hashList = indexedBlockStore.getListHashesEndWith(block.getHash(), 100);
        for (int i = 0; i < 100; ++i) {
            block = blocks.get(8003 - i);
            String hash = Hex.toHexString(hashList.get(i));
            String hash_ = Hex.toHexString(block.getHash());
            assertEquals(hash_, hash);
        }
        //  testing: getListHashesStartWith(long, long)
        block = blocks.get(7003);
        hashList = indexedBlockStore.getListHashesStartWith(block.getNumber(), 100);
        for (int i = 0; i < 100; ++i) {
            block = blocks.get(7003 + i);
            String hash = Hex.toHexString(hashList.get(i));
            String hash_ = Hex.toHexString(block.getHash());
            assertEquals(hash_, hash);
        }
    }
    /**
     * 把区块信息写入磁盘,并检查它是否存在
     */
    @Test
    public void test4() throws IOException {
        BigInteger bi = new BigInteger(32, new Random());
        String testDir = "test_db_" + bi;
        SystemProperties.getDefault().setDataBaseDir(testDir);
        RocksDbDataSource indexDB = new RocksDbDataSource("index");
        indexDB.init();
        DbSource blocksDB = new RocksDbDataSource("blocks");
        blocksDB.init();
        IndexedBlockStore indexedBlockStore = new IndexedBlockStore();
        // 注意,初始化的参数是rockDB,一个用于index,一个用于blocks。所以后续的所有操作都将直接写到磁盘里啦。
        indexedBlockStore.init(indexDB, blocksDB);
        BigInteger cummDiff = BigInteger.ZERO;
        for (Block block : blocks) {
            cummDiff = cummDiff.add(block.getCumulativeDifficulty());
            indexedBlockStore.saveBlock(block, cummDiff, true);
        }
        //  testing:   getTotalDifficulty()
        //  testing:   getMaxNumber()
        long bestIndex = blocks.get(blocks.size() - 1).getNumber();
        assertEquals(bestIndex, indexedBlockStore.getMaxNumber());
        assertEquals(cumDifficulty, indexedBlockStore.getTotalDifficulty());
        //  testing:  getBlockByHash(byte[])
        Block block = blocks.get(50);
        Block block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(150);
        block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(0);
        block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(8003);
        block_ = indexedBlockStore.getBlockByHash(block.getHash());
        assertEquals(block.getNumber(), block_.getNumber());
        block_ = indexedBlockStore.getBlockByHash(Hex.decode("00112233"));
        assertEquals(null, block_);
        //  testing:  getChainBlockByNumber(long)
        block = blocks.get(50);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(150);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(0);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(8003);
        block_ = indexedBlockStore.getChainBlockByNumber(block.getNumber());
        assertEquals(block.getNumber(), block_.getNumber());
        block_ = indexedBlockStore.getChainBlockByNumber(10000);
        assertEquals(null, block_);
        //  testing: getBlocksByNumber(long)
        block = blocks.get(50);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(150);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(0);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        block = blocks.get(8003);
        block_ = indexedBlockStore.getBlocksByNumber(block.getNumber()).get(0);
        assertEquals(block.getNumber(), block_.getNumber());
        int blocksNum = indexedBlockStore.getBlocksByNumber(10000).size();
        assertEquals(0, blocksNum);
        //  testing: getListHashesEndWith(byte[], long)
        block = blocks.get(8003);
        List<byte[]> hashList = indexedBlockStore.getListHashesEndWith(block.getHash(), 100);
        for (int i = 0; i < 100; ++i) {
            block = blocks.get(8003 - i);
            String hash = Hex.toHexString(hashList.get(i));
            String hash_ = Hex.toHexString(block.getHash());
            assertEquals(hash_, hash);
        }
        //  testing: getListHashesStartWith(long, long)
        block = blocks.get(7003);
        hashList = indexedBlockStore.getListHashesStartWith(block.getNumber(), 100);
        for (int i = 0; i < 100; ++i) {
            block = blocks.get(7003 + i);
            String hash = Hex.toHexString(hashList.get(i));
            String hash_ = Hex.toHexString(block.getHash());
            assertEquals(hash_, hash);
        }
        blocksDB.close();
        indexDB.close();
        // testing after: REOPEN
        indexDB = new RocksDbDataSource("index");
        indexDB.init();
        blocksDB = new RocksDbDataSource("blocks");
        blocksDB.init();
        indexedBlockStore = new IndexedBlockStore();
        indexedBlockStore.init(indexDB, blocksDB);
        //  testing: getListHashesStartWith(long, long)
        block = blocks.get(7003);
        hashList = indexedBlockStore.getListHashesStartWith(block.getNumber(), 100);
        for (int i = 0; i < 100; ++i) {
            block = blocks.get(7003 + i);
            String hash = Hex.toHexString(hashList.get(i));
            String hash_ = Hex.toHexString(block.getHash());
            assertEquals(hash_, hash);
        }
        blocksDB.close();
        indexDB.close();
        FileUtil.recursiveDelete(testDir);
    }
}
唯一的区别就在初始化那里了。
 
                             
        