/** * 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 java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; public class VersionMessage extends Message { private static final long serialVersionUID = 7313594258967483180L; /** * A services flag that denotes whether the peer has a copy of the block chain or not. */ public static final int NODE_NETWORK = 1; /** The version number of the protocol spoken. */ public int clientVersion; /** Flags defining what is supported. Right now {@link #NODE_NETWORK} is the only flag defined. */ public long localServices; /** What the other side believes the current time to be, in seconds. */ public long time; /** What the other side believes the address of this program is. Not used. */ public PeerAddress myAddr; /** What the other side believes their own address is. Not used. */ public PeerAddress theirAddr; /** * An additional string that today the official client sets to the empty string. We treat it as something like an * HTTP User-Agent header. */ public String subVer; /** How many blocks are in the chain, according to the other side. */ public long bestHeight; public VersionMessage(NetworkParameters params, byte[] msg) throws ProtocolException { super(params, msg, 0); } public VersionMessage(NetworkParameters params, int newBestHeight) { super(params); clientVersion = NetworkParameters.PROTOCOL_VERSION; localServices = 0; time = System.currentTimeMillis() / 1000; // Note that the official client doesn't do anything with these, and finding out your own external IP address // is kind of tricky anyway, so we just put nonsense here for now. try { myAddr = new PeerAddress(InetAddress.getLocalHost(), params.port, 0); theirAddr = new PeerAddress(InetAddress.getLocalHost(), params.port, 0); } catch (UnknownHostException e) { throw new RuntimeException(e); // Cannot happen. } subVer = "BitCoinJ 0.2"; bestHeight = newBestHeight; } @Override public void parse() throws ProtocolException { clientVersion = (int) readUint32(); localServices = readUint64().longValue(); time = readUint64().longValue(); myAddr = new PeerAddress(params, bytes, cursor, 0); cursor += myAddr.getMessageSize(); theirAddr = new PeerAddress(params, bytes, cursor, 0); cursor += theirAddr.getMessageSize(); // uint64 localHostNonce (random data) // We don't care about the localhost nonce. It's used to detect connecting back to yourself in cases where // there are NATs and proxies in the way. However we don't listen for inbound connections so it's irrelevant. readUint64(); // string subVer (currently "") subVer = readStr(); // int bestHeight (size of known block chain). bestHeight = readUint32(); } @Override public void bitcoinSerializeToStream(OutputStream buf) throws IOException { Utils.uint32ToByteStreamLE(clientVersion, buf); Utils.uint32ToByteStreamLE(localServices, buf); Utils.uint32ToByteStreamLE(localServices >> 32, buf); Utils.uint32ToByteStreamLE(time, buf); Utils.uint32ToByteStreamLE(time >> 32, buf); try { // My address. myAddr.bitcoinSerializeToStream(buf); // Their address. theirAddr.bitcoinSerializeToStream(buf); } catch (UnknownHostException e) { throw new RuntimeException(e); // Can't happen. } catch (IOException e) { throw new RuntimeException(e); // Can't happen. } // Next up is the "local host nonce", this is to detect the case of connecting // back to yourself. We don't care about this as we won't be accepting inbound // connections. Utils.uint32ToByteStreamLE(0, buf); Utils.uint32ToByteStreamLE(0, buf); // Now comes subVer. byte[] subVerBytes = subVer.getBytes("UTF-8"); buf.write(new VarInt(subVerBytes.length).encode()); buf.write(subVerBytes); // Size of known block chain. Utils.uint32ToByteStreamLE(bestHeight, buf); } /** * Returns true if the version message indicates the sender has a full copy of the block chain, * or if it's running in client mode (only has the headers). */ public boolean hasBlockChain() { return (localServices & NODE_NETWORK) == NODE_NETWORK; } }