mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-18 21:32:35 +01:00
Add BlockLocator class for representing block locators as used in GetBlocksMessage and GetHeadersMessage.
This commit is contained in:
parent
2ec193f847
commit
4a316089fa
88
core/src/main/java/org/bitcoinj/core/BlockLocator.java
Normal file
88
core/src/main/java/org/bitcoinj/core/BlockLocator.java
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright by the original author or authors.
|
||||
*
|
||||
* 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 org.bitcoinj.core;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents Block Locator in GetBlocks and GetHeaders messages
|
||||
**/
|
||||
public final class BlockLocator {
|
||||
private final ImmutableList<Sha256Hash> hashes;
|
||||
|
||||
public BlockLocator() {
|
||||
hashes = ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Block locator with defined list of hashes.
|
||||
*/
|
||||
public BlockLocator(ImmutableList<Sha256Hash> hashes) {
|
||||
this.hashes = hashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Sha256Hash} to a newly created block locator.
|
||||
*/
|
||||
public BlockLocator add(Sha256Hash hash) {
|
||||
return new BlockLocator(new ImmutableList.Builder<Sha256Hash>().addAll(this.hashes).add(hash).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of hashes in this block locator.
|
||||
*/
|
||||
public int size() {
|
||||
return hashes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns List of Block locator hashes.
|
||||
*/
|
||||
public List<Sha256Hash> getHashes() {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hash by index from this block locator.
|
||||
*/
|
||||
public Sha256Hash get(int i) {
|
||||
return hashes.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Block locator with " + size() + " blocks\n " + Utils.SPACE_JOINER.join(hashes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 0;
|
||||
for (Sha256Hash i : hashes) {
|
||||
hashCode ^= i.hashCode();
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
return ((BlockLocator) o).getHashes().equals(hashes);
|
||||
}
|
||||
}
|
@ -19,8 +19,6 @@ package org.bitcoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Represents the "getblocks" P2P network message, which requests the hashes of the parts of the block chain we're
|
||||
@ -31,10 +29,10 @@ import java.util.List;
|
||||
public class GetBlocksMessage extends Message {
|
||||
|
||||
protected long version;
|
||||
protected List<Sha256Hash> locator;
|
||||
protected BlockLocator locator;
|
||||
protected Sha256Hash stopHash;
|
||||
|
||||
public GetBlocksMessage(NetworkParameters params, List<Sha256Hash> locator, Sha256Hash stopHash) {
|
||||
public GetBlocksMessage(NetworkParameters params, BlockLocator locator, Sha256Hash stopHash) {
|
||||
super(params);
|
||||
this.version = protocolVersion;
|
||||
this.locator = locator;
|
||||
@ -53,14 +51,14 @@ public class GetBlocksMessage extends Message {
|
||||
if (startCount > 500)
|
||||
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
||||
length = cursor - offset + ((startCount + 1) * 32);
|
||||
locator = new ArrayList<>(startCount);
|
||||
locator = new BlockLocator();
|
||||
for (int i = 0; i < startCount; i++) {
|
||||
locator.add(readHash());
|
||||
locator = locator.add(readHash());
|
||||
}
|
||||
stopHash = readHash();
|
||||
}
|
||||
|
||||
public List<Sha256Hash> getLocator() {
|
||||
public BlockLocator getLocator() {
|
||||
return locator;
|
||||
}
|
||||
|
||||
@ -70,7 +68,7 @@ public class GetBlocksMessage extends Message {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "getblocks: " + Utils.SPACE_JOINER.join(locator);
|
||||
return "getblocks: " + locator.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -81,7 +79,7 @@ public class GetBlocksMessage extends Message {
|
||||
// identifiers that spans the entire chain with exponentially increasing gaps between
|
||||
// them, until we end up at the genesis block. See CBlockLocator::Set()
|
||||
stream.write(new VarInt(locator.size()).encode());
|
||||
for (Sha256Hash hash : locator) {
|
||||
for (Sha256Hash hash : locator.getHashes()) {
|
||||
// Have to reverse as wire format is little endian.
|
||||
stream.write(hash.getReversedBytes());
|
||||
}
|
||||
@ -95,13 +93,12 @@ public class GetBlocksMessage extends Message {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GetBlocksMessage other = (GetBlocksMessage) o;
|
||||
return version == other.version && stopHash.equals(other.stopHash) &&
|
||||
locator.size() == other.locator.size() && locator.containsAll(other.locator); // ignores locator ordering
|
||||
locator.size() == other.locator.size() && locator.equals(other.locator); // ignores locator ordering
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = (int)version ^ "getblocks".hashCode() ^ stopHash.hashCode();
|
||||
for (Sha256Hash aLocator : locator) hashCode ^= aLocator.hashCode(); // ignores locator ordering
|
||||
return hashCode;
|
||||
int hashCode = (int) version ^ "getblocks".hashCode() ^ stopHash.hashCode();
|
||||
return hashCode ^= locator.hashCode();
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>The "getheaders" command is structurally identical to "getblocks", but has different meaning. On receiving this
|
||||
* message a Bitcoin node returns matching blocks up to the limit, but without the bodies. It is useful as an
|
||||
@ -27,7 +25,7 @@ import java.util.List;
|
||||
* <p>Instances of this class are not safe for use by multiple threads.</p>
|
||||
*/
|
||||
public class GetHeadersMessage extends GetBlocksMessage {
|
||||
public GetHeadersMessage(NetworkParameters params, List<Sha256Hash> locator, Sha256Hash stopHash) {
|
||||
public GetHeadersMessage(NetworkParameters params, BlockLocator locator, Sha256Hash stopHash) {
|
||||
super(params, locator, stopHash);
|
||||
}
|
||||
|
||||
@ -37,7 +35,7 @@ public class GetHeadersMessage extends GetBlocksMessage {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "getheaders: " + Utils.SPACE_JOINER.join(locator);
|
||||
return "getheaders: " + locator.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,13 +48,12 @@ public class GetHeadersMessage extends GetBlocksMessage {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GetHeadersMessage other = (GetHeadersMessage) o;
|
||||
return version == other.version && stopHash.equals(other.stopHash) &&
|
||||
locator.size() == other.locator.size() && locator.containsAll(other.locator); // ignores locator ordering
|
||||
locator.size() == other.locator.size() && locator.equals(other.locator); // ignores locator ordering
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = (int)version ^ "getheaders".hashCode() ^ stopHash.hashCode();
|
||||
for (Sha256Hash aLocator : locator) hashCode ^= aLocator.hashCode(); // ignores locator ordering
|
||||
return hashCode;
|
||||
int hashCode = (int) version ^ "getheaders".hashCode() ^ stopHash.hashCode();
|
||||
return hashCode ^= locator.hashCode();
|
||||
}
|
||||
}
|
||||
|
@ -1437,8 +1437,7 @@ public class Peer extends PeerSocketHandler {
|
||||
// headers and then request the blocks from that point onwards. "getheaders" does not send us an inv, it just
|
||||
// sends us the data we requested in a "headers" message.
|
||||
|
||||
// TODO: Block locators should be abstracted out rather than special cased here.
|
||||
List<Sha256Hash> blockLocator = new ArrayList<>(51);
|
||||
BlockLocator blockLocator = new BlockLocator();
|
||||
// For now we don't do the exponential thinning as suggested here:
|
||||
//
|
||||
// https://en.bitcoin.it/wiki/Protocol_specification#getblocks
|
||||
@ -1462,7 +1461,7 @@ public class Peer extends PeerSocketHandler {
|
||||
this, toHash, chainHead.getHeader().getHashAsString());
|
||||
StoredBlock cursor = chainHead;
|
||||
for (int i = 100; cursor != null && i > 0; i--) {
|
||||
blockLocator.add(cursor.getHeader().getHash());
|
||||
blockLocator = blockLocator.add(cursor.getHeader().getHash());
|
||||
try {
|
||||
cursor = cursor.getPrev(store);
|
||||
} catch (BlockStoreException e) {
|
||||
@ -1472,7 +1471,7 @@ public class Peer extends PeerSocketHandler {
|
||||
}
|
||||
// Only add the locator if we didn't already do so. If the chain is < 50 blocks we already reached it.
|
||||
if (cursor != null)
|
||||
blockLocator.add(params.getGenesisBlock().getHash());
|
||||
blockLocator = blockLocator.add(params.getGenesisBlock().getHash());
|
||||
|
||||
// Record that we requested this range of blocks so we can filter out duplicate requests in the event of a
|
||||
// block being solved during chain download.
|
||||
|
@ -162,7 +162,7 @@ public class BitcoindComparisonTool {
|
||||
}
|
||||
LinkedList<Block> sendHeaders = new LinkedList<>();
|
||||
boolean found = false;
|
||||
for (Sha256Hash hash : ((GetHeadersMessage) m).getLocator()) {
|
||||
for (Sha256Hash hash : ((GetHeadersMessage) m).getLocator().getHashes()) {
|
||||
for (Block b : headers) {
|
||||
if (found) {
|
||||
sendHeaders.addLast(b);
|
||||
@ -206,8 +206,8 @@ public class BitcoindComparisonTool {
|
||||
|
||||
connectedFuture.get();
|
||||
|
||||
ArrayList<Sha256Hash> locator = new ArrayList<>(1);
|
||||
locator.add(PARAMS.getGenesisBlock().getHash());
|
||||
BlockLocator locator = new BlockLocator();
|
||||
locator = locator.add(PARAMS.getGenesisBlock().getHash());
|
||||
Sha256Hash hashTo = Sha256Hash.wrap("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
int rulesSinceFirstFail = 0;
|
||||
@ -295,8 +295,8 @@ public class BitcoindComparisonTool {
|
||||
if (block.throwsException)
|
||||
blocksRequested.remove(nextBlock.getHash());
|
||||
//bitcoind.sendMessage(nextBlock);
|
||||
locator.clear();
|
||||
locator.add(bitcoindChainHead);
|
||||
locator = new BlockLocator();
|
||||
locator = locator.add(bitcoindChainHead);
|
||||
bitcoind.sendMessage(new GetHeadersMessage(PARAMS, locator, hashTo));
|
||||
bitcoind.ping().get();
|
||||
if (!chain.getChainHead().getHeader().getHash().equals(bitcoindChainHead)) {
|
||||
|
@ -177,7 +177,7 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
inbound(writeTarget, b5);
|
||||
getblocks = (GetBlocksMessage)outbound(writeTarget);
|
||||
assertEquals(b5.getHash(), getblocks.getStopHash());
|
||||
assertEquals(b3.getHash(), getblocks.getLocator().get(0));
|
||||
assertEquals(b3.getHash(), getblocks.getLocator().getHashes().get(0));
|
||||
// At this point another block is solved and broadcast. The inv triggers a getdata but we do NOT send another
|
||||
// getblocks afterwards, because that would result in us receiving the same set of blocks twice which is a
|
||||
// timewaste. The getblocks message that would have been generated is set to be the same as the previous
|
||||
@ -225,9 +225,9 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
inbound(writeTarget, inv);
|
||||
|
||||
GetBlocksMessage getblocks = (GetBlocksMessage)outbound(writeTarget);
|
||||
List<Sha256Hash> expectedLocator = new ArrayList<>();
|
||||
expectedLocator.add(b1.getHash());
|
||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
BlockLocator expectedLocator = new BlockLocator();
|
||||
expectedLocator = expectedLocator.add(b1.getHash());
|
||||
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
|
||||
assertEquals(getblocks.getLocator(), expectedLocator);
|
||||
assertEquals(getblocks.getStopHash(), b3.getHash());
|
||||
@ -396,10 +396,10 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
});
|
||||
peer.startBlockChainDownload();
|
||||
|
||||
List<Sha256Hash> expectedLocator = new ArrayList<>();
|
||||
expectedLocator.add(b2.getHash());
|
||||
expectedLocator.add(b1.getHash());
|
||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
BlockLocator expectedLocator = new BlockLocator();
|
||||
expectedLocator = expectedLocator.add(b2.getHash());
|
||||
expectedLocator = expectedLocator.add(b1.getHash());
|
||||
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
|
||||
GetBlocksMessage message = (GetBlocksMessage) outbound(writeTarget);
|
||||
assertEquals(message.getLocator(), expectedLocator);
|
||||
@ -478,19 +478,19 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
peer.setDownloadParameters(Utils.currentTimeSeconds() - (600*2) + 1, false);
|
||||
peer.startBlockChainDownload();
|
||||
GetHeadersMessage getheaders = (GetHeadersMessage) outbound(writeTarget);
|
||||
List<Sha256Hash> expectedLocator = new ArrayList<>();
|
||||
expectedLocator.add(b1.getHash());
|
||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
BlockLocator expectedLocator = new BlockLocator();
|
||||
expectedLocator = expectedLocator.add(b1.getHash());
|
||||
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
assertEquals(getheaders.getLocator(), expectedLocator);
|
||||
assertEquals(getheaders.getStopHash(), Sha256Hash.ZERO_HASH);
|
||||
// Now send all the headers.
|
||||
HeadersMessage headers = new HeadersMessage(UNITTEST, b2.cloneAsHeader(),
|
||||
b3.cloneAsHeader(), b4.cloneAsHeader());
|
||||
// We expect to be asked for b3 and b4 again, but this time, with a body.
|
||||
expectedLocator.clear();
|
||||
expectedLocator.add(b2.getHash());
|
||||
expectedLocator.add(b1.getHash());
|
||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
expectedLocator = new BlockLocator();
|
||||
expectedLocator = expectedLocator.add(b2.getHash());
|
||||
expectedLocator = expectedLocator.add(b1.getHash());
|
||||
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||
inbound(writeTarget, headers);
|
||||
GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget);
|
||||
assertEquals(expectedLocator, getblocks.getLocator());
|
||||
|
Loading…
Reference in New Issue
Block a user