/* * 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.net.eth.message; import org.ethereum.core.BlockIdentifier; import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; import java.math.BigInteger; import static org.ethereum.util.ByteUtil.byteArrayToInt; import static org.ethereum.util.ByteUtil.byteArrayToLong; /** * Wrapper around an Ethereum GetBlockHeaders message on the network * * @see EthMessageCodes#GET_BLOCK_HEADERS * * @author Mikhail Kalinin * @since 04.09.2015 */ public class GetBlockHeadersMessage extends EthMessage { private static final int DEFAULT_SIZE_BYTES = 32; /** * Block number from which to start sending block headers */ private long blockNumber; /** * Block hash from which to start sending block headers <br> * Initial block can be addressed by either {@code blockNumber} or {@code blockHash} */ private byte[] blockHash; /** * The maximum number of headers to be returned. <br> * <b>Note:</b> the peer could return fewer. */ private int maxHeaders; /** * Blocks to skip between consecutive headers. <br> * Direction depends on {@code reverse} param. */ private int skipBlocks; /** * The direction of headers enumeration. <br> * <b>false</b> is for rising block numbers. <br> * <b>true</b> is for falling block numbers. */ private boolean reverse; public GetBlockHeadersMessage(byte[] encoded) { super(encoded); } public GetBlockHeadersMessage(long blockNumber, int maxHeaders) { this(blockNumber, null, maxHeaders, 0, false); } public GetBlockHeadersMessage(long blockNumber, byte[] blockHash, int maxHeaders, int skipBlocks, boolean reverse) { this.blockNumber = blockNumber; this.blockHash = blockHash; this.maxHeaders = maxHeaders; this.skipBlocks = skipBlocks; this.reverse = reverse; parsed = true; encode(); } private void encode() { byte[] maxHeaders = RLP.encodeInt(this.maxHeaders); byte[] skipBlocks = RLP.encodeInt(this.skipBlocks); byte[] reverse = RLP.encodeByte((byte) (this.reverse ? 1 : 0)); if (this.blockHash != null) { byte[] hash = RLP.encodeElement(this.blockHash); this.encoded = RLP.encodeList(hash, maxHeaders, skipBlocks, reverse); } else { byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.blockNumber)); this.encoded = RLP.encodeList(number, maxHeaders, skipBlocks, reverse); } } private synchronized void parse() { if (parsed) return; RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0); byte[] blockBytes = paramsList.get(0).getRLPData(); // it might be either a hash or number if (blockBytes == null) { this.blockNumber = 0; } else if (blockBytes.length == DEFAULT_SIZE_BYTES) { this.blockHash = blockBytes; } else { this.blockNumber = byteArrayToLong(blockBytes); } byte[] maxHeaders = paramsList.get(1).getRLPData(); this.maxHeaders = byteArrayToInt(maxHeaders); byte[] skipBlocks = paramsList.get(2).getRLPData(); this.skipBlocks = byteArrayToInt(skipBlocks); byte[] reverse = paramsList.get(3).getRLPData(); this.reverse = byteArrayToInt(reverse) == 1; parsed = true; } public long getBlockNumber() { parse(); return blockNumber; } public byte[] getBlockHash() { parse(); return blockHash; } public BlockIdentifier getBlockIdentifier() { parse(); return new BlockIdentifier(blockHash, blockNumber); } public int getMaxHeaders() { parse(); return maxHeaders; } public int getSkipBlocks() { parse(); return skipBlocks; } public boolean isReverse() { parse(); return reverse; } @Override public byte[] getEncoded() { if (encoded == null) encode(); return encoded; } @Override public Class<BlockHeadersMessage> getAnswerMessage() { return BlockHeadersMessage.class; } @Override public EthMessageCodes getCommand() { return EthMessageCodes.GET_BLOCK_HEADERS; } @Override public String toString() { parse(); return "[" + this.getCommand().name() + " blockNumber=" + String.valueOf(blockNumber) + " blockHash=" + ByteUtil.toHexString(blockHash) + " maxHeaders=" + maxHeaders + " skipBlocks=" + skipBlocks + " reverse=" + reverse + "]"; } }