/*
* 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.core;
import org.ethereum.datasource.inmem.HashMapDB;
import org.ethereum.db.RepositoryRoot;
import org.ethereum.db.BlockStoreDummy;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Testing {@link BlockchainImpl#getListOfHeadersStartFrom(BlockIdentifier, int, int, boolean)}
*/
public class BlockchainGetHeadersTest {
private class BlockStoreMock extends BlockStoreDummy {
private List<Block> dummyBlocks = new ArrayList<>();
public BlockStoreMock() {
byte [] emptyArray = new byte[0];
byte [] recentHash = emptyArray;
for (long i = 0; i < 10; ++i) {
BlockHeader blockHeader = new BlockHeader(recentHash, emptyArray, emptyArray, emptyArray, emptyArray,
i, emptyArray, 0L, 0L, emptyArray, emptyArray, emptyArray);
recentHash = blockHeader.getHash();
Block block = new Block(blockHeader, new ArrayList<Transaction>(), new ArrayList<BlockHeader>());
dummyBlocks.add(block);
}
}
@Override
public Block getBlockByHash(byte[] hash) {
for (Block block: dummyBlocks) {
if (Arrays.equals(block.getHash(), hash)) {
return block;
}
}
return null;
}
@Override
public Block getChainBlockByNumber(long blockNumber) {
return blockNumber < dummyBlocks.size() ? dummyBlocks.get((int) blockNumber) : null;
}
@Override
public List<BlockHeader> getListHeadersEndWith(byte[] hash, long qty) {
List<BlockHeader> headers = new ArrayList<>();
Block start = getBlockByHash(hash);
if (start != null) {
long i = start.getNumber();
while (i >= 0 && headers.size() < qty) {
headers.add(getChainBlockByNumber(i).getHeader());
--i;
}
}
return headers;
}
@Override
public Block getBestBlock() {
return dummyBlocks.get(dummyBlocks.size() - 1);
}
}
private class BlockchainImplTester extends BlockchainImpl {
public BlockchainImplTester() {
blockStore = new BlockStoreMock();
setRepository(new RepositoryRoot(new HashMapDB<byte[]>()));
setBestBlock(blockStore.getChainBlockByNumber(9));
}
}
private BlockchainImpl blockchain;
public BlockchainGetHeadersTest() {
blockchain = new BlockchainImplTester();
}
@Test
public void singleHeader() {
// Get by number
long blockNumber = 2L;
BlockIdentifier identifier = new BlockIdentifier(null, blockNumber);
List<BlockHeader> headers = blockchain.getListOfHeadersStartFrom(identifier, 0, 1, false);
assert headers.size() == 1;
assert headers.get(0).getNumber() == blockNumber;
// Get by hash
byte[] hash = headers.get(0).getHash();
BlockIdentifier hashIdentifier = new BlockIdentifier(hash, 0L);
List<BlockHeader> headersByHash = blockchain.getListOfHeadersStartFrom(hashIdentifier, 0, 1, false);
assert headersByHash.size() == 1;
assert headersByHash.get(0).getNumber() == blockNumber;
// Reverse doesn't matter for single block
List<BlockHeader> headersReverse = blockchain.getListOfHeadersStartFrom(hashIdentifier, 0, 1, true);
assert headersReverse.size() == 1;
assert headersReverse.get(0).getNumber() == blockNumber;
// Skip doesn't matter for single block
List<BlockHeader> headersSkip = blockchain.getListOfHeadersStartFrom(hashIdentifier, 15, 1, false);
assert headersReverse.size() == 1;
assert headersReverse.get(0).getNumber() == blockNumber;
}
@Test
public void continuousHeaders() {
// Get by number
long blockNumber = 2L;
BlockIdentifier identifier = new BlockIdentifier(null, blockNumber);
List<BlockHeader> headers = blockchain.getListOfHeadersStartFrom(identifier, 0, 3, false);
assert headers.size() == 3;
assert headers.get(0).getNumber() == blockNumber;
assert headers.get(1).getNumber() == blockNumber + 1;
assert headers.get(2).getNumber() == blockNumber + 2;
List<BlockHeader> headersReverse = blockchain.getListOfHeadersStartFrom(identifier, 0, 3, true);
assert headersReverse.size() == 3;
assert headersReverse.get(0).getNumber() == blockNumber;
assert headersReverse.get(1).getNumber() == blockNumber - 1;
assert headersReverse.get(2).getNumber() == blockNumber - 2;
// Requesting more than we have
BlockIdentifier identifierMore = new BlockIdentifier(null, 8L);
List<BlockHeader> headersMore = blockchain.getListOfHeadersStartFrom(identifierMore, 0, 3, false);
assert headersMore.size() == 2;
assert headersMore.get(0).getNumber() == 8L;
assert headersMore.get(1).getNumber() == 9L;
}
@Test
public void gapedHeaders() {
int skip = 2;
BlockIdentifier identifier = new BlockIdentifier(null, 2L);
List<BlockHeader> headers = blockchain.getListOfHeadersStartFrom(identifier, skip, 3, false);
assert headers.size() == 3;
assert headers.get(0).getNumber() == 2L;
assert headers.get(1).getNumber() == 5L; // 2, [3, 4], 5 - skipping []
assert headers.get(2).getNumber() == 8L; // 5, [6, 7], 8 - skipping []
// Same for reverse
BlockIdentifier identifierReverse = new BlockIdentifier(null, 8L);
List<BlockHeader> headersReverse = blockchain.getListOfHeadersStartFrom(identifierReverse, skip, 3, true);
assert headersReverse.size() == 3;
assert headersReverse.get(0).getNumber() == 8L;
assert headersReverse.get(1).getNumber() == 5L;
assert headersReverse.get(2).getNumber() == 2L;
// Requesting more than we have
BlockIdentifier identifierMore = new BlockIdentifier(null, 8L);
List<BlockHeader> headersMore = blockchain.getListOfHeadersStartFrom(identifierMore, skip, 3, false);
assert headersMore.size() == 1;
assert headersMore.get(0).getNumber() == 8L;
}
}