mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-01 21:17:13 +00:00
Add HeadersMessage and parsing support for it. Patch from Roman Mandeleil.
This commit is contained in:
3
AUTHORS
3
AUTHORS
@@ -9,4 +9,5 @@ John Sample <jwsample@gmail.com>
|
|||||||
Jan Møller <jan.moller@gmail.com>
|
Jan Møller <jan.moller@gmail.com>
|
||||||
Wolfgang Nagele <wolfgang.nagele@gmail.com>
|
Wolfgang Nagele <wolfgang.nagele@gmail.com>
|
||||||
Jonny Heggheim <hegjon@gmail.com>
|
Jonny Heggheim <hegjon@gmail.com>
|
||||||
Steve Coughlan <shadders.del@gmail.com>
|
Steve Coughlan <shadders.del@gmail.com>
|
||||||
|
Roman Mandeleil <roman.mandeleil@gmail.com>
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ public class BitcoinSerializer {
|
|||||||
names.put(VersionAck.class, "verack");
|
names.put(VersionAck.class, "verack");
|
||||||
names.put(GetBlocksMessage.class, "getblocks");
|
names.put(GetBlocksMessage.class, "getblocks");
|
||||||
names.put(GetAddrMessage.class, "getaddr");
|
names.put(GetAddrMessage.class, "getaddr");
|
||||||
|
names.put(HeadersMessage.class, "headers");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -330,6 +332,8 @@ public class BitcoinSerializer {
|
|||||||
return new Ping();
|
return new Ping();
|
||||||
} else if (command.equals("verack")) {
|
} else if (command.equals("verack")) {
|
||||||
return new VersionAck(params, payloadBytes);
|
return new VersionAck(params, payloadBytes);
|
||||||
|
} else if (command.equals("headers")) {
|
||||||
|
return new HeadersMessage(params, payloadBytes);
|
||||||
} else {
|
} else {
|
||||||
throw new ProtocolException("No support for deserializing message with name " + command);
|
throw new ProtocolException("No support for deserializing message with name " + command);
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/com/google/bitcoin/core/HeadersMessage.java
Normal file
82
src/com/google/bitcoin/core/HeadersMessage.java
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A protocol message that contains a repeated series of block headers, sent in response to the "getheaders" command.
|
||||||
|
* This is useful when you want to traverse the chain but know you don't care about the block contents, for example,
|
||||||
|
* because you have a freshly created wallet with no keys.
|
||||||
|
*/
|
||||||
|
public class HeadersMessage extends Message {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(HeadersMessage.class);
|
||||||
|
|
||||||
|
// The main client will never send us more than this number of headers.
|
||||||
|
final static int MAX_HEADERS = 2000;
|
||||||
|
|
||||||
|
private List<Block> blockHeaders;
|
||||||
|
|
||||||
|
public HeadersMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||||
|
super(params, payload, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void parseLite() throws ProtocolException {
|
||||||
|
if (length == UNKNOWN_LENGTH) {
|
||||||
|
long numHeaders = readVarInt();
|
||||||
|
|
||||||
|
// Each header has 80 bytes and one more byte for transactions number which is 00.
|
||||||
|
length = 81 * (int)numHeaders;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void parse() throws ProtocolException {
|
||||||
|
long numHeaders = readVarInt();
|
||||||
|
if (numHeaders > MAX_HEADERS)
|
||||||
|
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
|
||||||
|
MAX_HEADERS);
|
||||||
|
|
||||||
|
blockHeaders = new ArrayList<Block>();
|
||||||
|
|
||||||
|
for (int i = 0; i < numHeaders; ++i) {
|
||||||
|
// Read 80 bytes of the header and one more byte for the transaction list, which is always a 00 because the
|
||||||
|
// transaction list is empty.
|
||||||
|
byte[] blockHeader = readBytes(81);
|
||||||
|
if (blockHeader[80] != 00)
|
||||||
|
throw new ProtocolException("Block header does not end with a null byte");
|
||||||
|
Block newBlockHeader = new Block(this.params, blockHeader);
|
||||||
|
blockHeaders.add(newBlockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
for (int i = 0; i < numHeaders; ++i) {
|
||||||
|
log.debug(this.blockHeaders.get(i).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Block> getBlockHeaders() {
|
||||||
|
return blockHeaders;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -199,5 +199,84 @@ public class BitcoinSerializerTest {
|
|||||||
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
|
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get 1 header of the block number 1 (the first one is 0) in the chain
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testHeaders1() throws Exception {
|
||||||
|
|
||||||
|
BitcoinSerializer bs = new BitcoinSerializer(NetworkParameters.prodNet(), true,
|
||||||
|
null);
|
||||||
|
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(Hex.decode("f9beb4d9686561" +
|
||||||
|
"646572730000000000520000005d4fab8101010000006fe28c0ab6f1b372c1a6a246ae6" +
|
||||||
|
"3f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677b" +
|
||||||
|
"a1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e3629900"));
|
||||||
|
|
||||||
|
HeadersMessage hm = (HeadersMessage) bs.deserialize(bais);
|
||||||
|
|
||||||
|
// The first block after the genesis
|
||||||
|
// http://blockexplorer.com/b/1
|
||||||
|
Block block = hm.getBlockHeaders().get(0);
|
||||||
|
String hash = block.getHashAsString();
|
||||||
|
assertEquals(hash, "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
|
||||||
|
|
||||||
|
assertEquals(block.transactions.size(), 0);
|
||||||
|
|
||||||
|
assertEquals(Utils.bytesToHexString(block.getMerkleRoot().getBytes()),
|
||||||
|
"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
/**
|
||||||
|
* Get 6 headers of blocks 1-6 in the chain
|
||||||
|
*/
|
||||||
|
public void testHeaders2() throws Exception {
|
||||||
|
BitcoinSerializer bs = new BitcoinSerializer(NetworkParameters.prodNet(), true,
|
||||||
|
null);
|
||||||
|
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(Hex.decode("f9beb4d96865616465" +
|
||||||
|
"72730000000000e701000085acd4ea06010000006fe28c0ab6f1b372c1a6a246ae63f74f931e" +
|
||||||
|
"8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1c" +
|
||||||
|
"db606e857233e0e61bc6649ffff001d01e3629900010000004860eb18bf1b1620e37e9490fc8a" +
|
||||||
|
"427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36" +
|
||||||
|
"ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610001000000bddd99ccfda39da1b108ce1" +
|
||||||
|
"a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387" +
|
||||||
|
"af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d00010000004944469562ae1c2c74" +
|
||||||
|
"d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec" +
|
||||||
|
"5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a9000100000085144a84488e" +
|
||||||
|
"a88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023" +
|
||||||
|
"370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e4770001000000fc33f5" +
|
||||||
|
"96f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4" +
|
||||||
|
"ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c9700"));
|
||||||
|
|
||||||
|
HeadersMessage hm = (HeadersMessage) bs.deserialize(bais);
|
||||||
|
|
||||||
|
int nBlocks = hm.getBlockHeaders().size();
|
||||||
|
assertEquals(nBlocks, 6);
|
||||||
|
|
||||||
|
// index 0 block is the number 1 block in the block chain
|
||||||
|
// http://blockexplorer.com/b/1
|
||||||
|
Block zeroBlock = hm.getBlockHeaders().get(0);
|
||||||
|
String zeroBlockHash = zeroBlock.getHashAsString();
|
||||||
|
|
||||||
|
assertEquals("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048",
|
||||||
|
zeroBlockHash);
|
||||||
|
assertEquals(zeroBlock.getNonce(), 2573394689L);
|
||||||
|
|
||||||
|
|
||||||
|
Block thirdBlock = hm.getBlockHeaders().get(3);
|
||||||
|
String thirdBlockHash = thirdBlock.getHashAsString();
|
||||||
|
|
||||||
|
// index 3 block is the number 4 block in the block chain
|
||||||
|
// http://blockexplorer.com/b/4
|
||||||
|
assertEquals("000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485",
|
||||||
|
thirdBlockHash);
|
||||||
|
assertEquals(thirdBlock.getNonce(), 2850094635L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user