类: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);
}
}
唯一的区别就在初始化那里了。