mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-14 19:25:51 +00: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.IOException;
|
||||||
import java.io.OutputStream;
|
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
|
* <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 {
|
public class GetBlocksMessage extends Message {
|
||||||
|
|
||||||
protected long version;
|
protected long version;
|
||||||
protected List<Sha256Hash> locator;
|
protected BlockLocator locator;
|
||||||
protected Sha256Hash stopHash;
|
protected Sha256Hash stopHash;
|
||||||
|
|
||||||
public GetBlocksMessage(NetworkParameters params, List<Sha256Hash> locator, Sha256Hash stopHash) {
|
public GetBlocksMessage(NetworkParameters params, BlockLocator locator, Sha256Hash stopHash) {
|
||||||
super(params);
|
super(params);
|
||||||
this.version = protocolVersion;
|
this.version = protocolVersion;
|
||||||
this.locator = locator;
|
this.locator = locator;
|
||||||
@ -53,14 +51,14 @@ public class GetBlocksMessage extends Message {
|
|||||||
if (startCount > 500)
|
if (startCount > 500)
|
||||||
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
||||||
length = cursor - offset + ((startCount + 1) * 32);
|
length = cursor - offset + ((startCount + 1) * 32);
|
||||||
locator = new ArrayList<>(startCount);
|
locator = new BlockLocator();
|
||||||
for (int i = 0; i < startCount; i++) {
|
for (int i = 0; i < startCount; i++) {
|
||||||
locator.add(readHash());
|
locator = locator.add(readHash());
|
||||||
}
|
}
|
||||||
stopHash = readHash();
|
stopHash = readHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Sha256Hash> getLocator() {
|
public BlockLocator getLocator() {
|
||||||
return locator;
|
return locator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +68,7 @@ public class GetBlocksMessage extends Message {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "getblocks: " + Utils.SPACE_JOINER.join(locator);
|
return "getblocks: " + locator.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,7 +79,7 @@ public class GetBlocksMessage extends Message {
|
|||||||
// identifiers that spans the entire chain with exponentially increasing gaps between
|
// identifiers that spans the entire chain with exponentially increasing gaps between
|
||||||
// them, until we end up at the genesis block. See CBlockLocator::Set()
|
// them, until we end up at the genesis block. See CBlockLocator::Set()
|
||||||
stream.write(new VarInt(locator.size()).encode());
|
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.
|
// Have to reverse as wire format is little endian.
|
||||||
stream.write(hash.getReversedBytes());
|
stream.write(hash.getReversedBytes());
|
||||||
}
|
}
|
||||||
@ -95,13 +93,12 @@ public class GetBlocksMessage extends Message {
|
|||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
GetBlocksMessage other = (GetBlocksMessage) o;
|
GetBlocksMessage other = (GetBlocksMessage) o;
|
||||||
return version == other.version && stopHash.equals(other.stopHash) &&
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hashCode = (int)version ^ "getblocks".hashCode() ^ stopHash.hashCode();
|
int hashCode = (int) version ^ "getblocks".hashCode() ^ stopHash.hashCode();
|
||||||
for (Sha256Hash aLocator : locator) hashCode ^= aLocator.hashCode(); // ignores locator ordering
|
return hashCode ^= locator.hashCode();
|
||||||
return hashCode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.bitcoinj.core;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>The "getheaders" command is structurally identical to "getblocks", but has different meaning. On receiving this
|
* <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
|
* 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>
|
* <p>Instances of this class are not safe for use by multiple threads.</p>
|
||||||
*/
|
*/
|
||||||
public class GetHeadersMessage extends GetBlocksMessage {
|
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);
|
super(params, locator, stopHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +35,7 @@ public class GetHeadersMessage extends GetBlocksMessage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
GetHeadersMessage other = (GetHeadersMessage) o;
|
GetHeadersMessage other = (GetHeadersMessage) o;
|
||||||
return version == other.version && stopHash.equals(other.stopHash) &&
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hashCode = (int)version ^ "getheaders".hashCode() ^ stopHash.hashCode();
|
int hashCode = (int) version ^ "getheaders".hashCode() ^ stopHash.hashCode();
|
||||||
for (Sha256Hash aLocator : locator) hashCode ^= aLocator.hashCode(); // ignores locator ordering
|
return hashCode ^= locator.hashCode();
|
||||||
return 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
|
// 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.
|
// sends us the data we requested in a "headers" message.
|
||||||
|
|
||||||
// TODO: Block locators should be abstracted out rather than special cased here.
|
BlockLocator blockLocator = new BlockLocator();
|
||||||
List<Sha256Hash> blockLocator = new ArrayList<>(51);
|
|
||||||
// For now we don't do the exponential thinning as suggested here:
|
// For now we don't do the exponential thinning as suggested here:
|
||||||
//
|
//
|
||||||
// https://en.bitcoin.it/wiki/Protocol_specification#getblocks
|
// https://en.bitcoin.it/wiki/Protocol_specification#getblocks
|
||||||
@ -1462,7 +1461,7 @@ public class Peer extends PeerSocketHandler {
|
|||||||
this, toHash, chainHead.getHeader().getHashAsString());
|
this, toHash, chainHead.getHeader().getHashAsString());
|
||||||
StoredBlock cursor = chainHead;
|
StoredBlock cursor = chainHead;
|
||||||
for (int i = 100; cursor != null && i > 0; i--) {
|
for (int i = 100; cursor != null && i > 0; i--) {
|
||||||
blockLocator.add(cursor.getHeader().getHash());
|
blockLocator = blockLocator.add(cursor.getHeader().getHash());
|
||||||
try {
|
try {
|
||||||
cursor = cursor.getPrev(store);
|
cursor = cursor.getPrev(store);
|
||||||
} catch (BlockStoreException e) {
|
} 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.
|
// Only add the locator if we didn't already do so. If the chain is < 50 blocks we already reached it.
|
||||||
if (cursor != null)
|
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
|
// 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.
|
// block being solved during chain download.
|
||||||
|
@ -162,7 +162,7 @@ public class BitcoindComparisonTool {
|
|||||||
}
|
}
|
||||||
LinkedList<Block> sendHeaders = new LinkedList<>();
|
LinkedList<Block> sendHeaders = new LinkedList<>();
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Sha256Hash hash : ((GetHeadersMessage) m).getLocator()) {
|
for (Sha256Hash hash : ((GetHeadersMessage) m).getLocator().getHashes()) {
|
||||||
for (Block b : headers) {
|
for (Block b : headers) {
|
||||||
if (found) {
|
if (found) {
|
||||||
sendHeaders.addLast(b);
|
sendHeaders.addLast(b);
|
||||||
@ -206,8 +206,8 @@ public class BitcoindComparisonTool {
|
|||||||
|
|
||||||
connectedFuture.get();
|
connectedFuture.get();
|
||||||
|
|
||||||
ArrayList<Sha256Hash> locator = new ArrayList<>(1);
|
BlockLocator locator = new BlockLocator();
|
||||||
locator.add(PARAMS.getGenesisBlock().getHash());
|
locator = locator.add(PARAMS.getGenesisBlock().getHash());
|
||||||
Sha256Hash hashTo = Sha256Hash.wrap("0000000000000000000000000000000000000000000000000000000000000000");
|
Sha256Hash hashTo = Sha256Hash.wrap("0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
|
||||||
int rulesSinceFirstFail = 0;
|
int rulesSinceFirstFail = 0;
|
||||||
@ -295,8 +295,8 @@ public class BitcoindComparisonTool {
|
|||||||
if (block.throwsException)
|
if (block.throwsException)
|
||||||
blocksRequested.remove(nextBlock.getHash());
|
blocksRequested.remove(nextBlock.getHash());
|
||||||
//bitcoind.sendMessage(nextBlock);
|
//bitcoind.sendMessage(nextBlock);
|
||||||
locator.clear();
|
locator = new BlockLocator();
|
||||||
locator.add(bitcoindChainHead);
|
locator = locator.add(bitcoindChainHead);
|
||||||
bitcoind.sendMessage(new GetHeadersMessage(PARAMS, locator, hashTo));
|
bitcoind.sendMessage(new GetHeadersMessage(PARAMS, locator, hashTo));
|
||||||
bitcoind.ping().get();
|
bitcoind.ping().get();
|
||||||
if (!chain.getChainHead().getHeader().getHash().equals(bitcoindChainHead)) {
|
if (!chain.getChainHead().getHeader().getHash().equals(bitcoindChainHead)) {
|
||||||
|
@ -177,7 +177,7 @@ public class PeerTest extends TestWithNetworkConnections {
|
|||||||
inbound(writeTarget, b5);
|
inbound(writeTarget, b5);
|
||||||
getblocks = (GetBlocksMessage)outbound(writeTarget);
|
getblocks = (GetBlocksMessage)outbound(writeTarget);
|
||||||
assertEquals(b5.getHash(), getblocks.getStopHash());
|
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
|
// 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
|
// 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
|
// 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);
|
inbound(writeTarget, inv);
|
||||||
|
|
||||||
GetBlocksMessage getblocks = (GetBlocksMessage)outbound(writeTarget);
|
GetBlocksMessage getblocks = (GetBlocksMessage)outbound(writeTarget);
|
||||||
List<Sha256Hash> expectedLocator = new ArrayList<>();
|
BlockLocator expectedLocator = new BlockLocator();
|
||||||
expectedLocator.add(b1.getHash());
|
expectedLocator = expectedLocator.add(b1.getHash());
|
||||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||||
|
|
||||||
assertEquals(getblocks.getLocator(), expectedLocator);
|
assertEquals(getblocks.getLocator(), expectedLocator);
|
||||||
assertEquals(getblocks.getStopHash(), b3.getHash());
|
assertEquals(getblocks.getStopHash(), b3.getHash());
|
||||||
@ -396,10 +396,10 @@ public class PeerTest extends TestWithNetworkConnections {
|
|||||||
});
|
});
|
||||||
peer.startBlockChainDownload();
|
peer.startBlockChainDownload();
|
||||||
|
|
||||||
List<Sha256Hash> expectedLocator = new ArrayList<>();
|
BlockLocator expectedLocator = new BlockLocator();
|
||||||
expectedLocator.add(b2.getHash());
|
expectedLocator = expectedLocator.add(b2.getHash());
|
||||||
expectedLocator.add(b1.getHash());
|
expectedLocator = expectedLocator.add(b1.getHash());
|
||||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||||
|
|
||||||
GetBlocksMessage message = (GetBlocksMessage) outbound(writeTarget);
|
GetBlocksMessage message = (GetBlocksMessage) outbound(writeTarget);
|
||||||
assertEquals(message.getLocator(), expectedLocator);
|
assertEquals(message.getLocator(), expectedLocator);
|
||||||
@ -478,19 +478,19 @@ public class PeerTest extends TestWithNetworkConnections {
|
|||||||
peer.setDownloadParameters(Utils.currentTimeSeconds() - (600*2) + 1, false);
|
peer.setDownloadParameters(Utils.currentTimeSeconds() - (600*2) + 1, false);
|
||||||
peer.startBlockChainDownload();
|
peer.startBlockChainDownload();
|
||||||
GetHeadersMessage getheaders = (GetHeadersMessage) outbound(writeTarget);
|
GetHeadersMessage getheaders = (GetHeadersMessage) outbound(writeTarget);
|
||||||
List<Sha256Hash> expectedLocator = new ArrayList<>();
|
BlockLocator expectedLocator = new BlockLocator();
|
||||||
expectedLocator.add(b1.getHash());
|
expectedLocator = expectedLocator.add(b1.getHash());
|
||||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||||
assertEquals(getheaders.getLocator(), expectedLocator);
|
assertEquals(getheaders.getLocator(), expectedLocator);
|
||||||
assertEquals(getheaders.getStopHash(), Sha256Hash.ZERO_HASH);
|
assertEquals(getheaders.getStopHash(), Sha256Hash.ZERO_HASH);
|
||||||
// Now send all the headers.
|
// Now send all the headers.
|
||||||
HeadersMessage headers = new HeadersMessage(UNITTEST, b2.cloneAsHeader(),
|
HeadersMessage headers = new HeadersMessage(UNITTEST, b2.cloneAsHeader(),
|
||||||
b3.cloneAsHeader(), b4.cloneAsHeader());
|
b3.cloneAsHeader(), b4.cloneAsHeader());
|
||||||
// We expect to be asked for b3 and b4 again, but this time, with a body.
|
// We expect to be asked for b3 and b4 again, but this time, with a body.
|
||||||
expectedLocator.clear();
|
expectedLocator = new BlockLocator();
|
||||||
expectedLocator.add(b2.getHash());
|
expectedLocator = expectedLocator.add(b2.getHash());
|
||||||
expectedLocator.add(b1.getHash());
|
expectedLocator = expectedLocator.add(b1.getHash());
|
||||||
expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
expectedLocator = expectedLocator.add(UNITTEST.getGenesisBlock().getHash());
|
||||||
inbound(writeTarget, headers);
|
inbound(writeTarget, headers);
|
||||||
GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget);
|
GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget);
|
||||||
assertEquals(expectedLocator, getblocks.getLocator());
|
assertEquals(expectedLocator, getblocks.getLocator());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user