package org.ripple.power.txns.btc; import java.io.EOFException; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Arrays; import org.ripple.power.config.LSystem; /** * <p>The 'version' message is exchanged when two nodes connect. It identifies * the services provided by the nodes and the latest block each has seen. A node * responds with a 'verack' message if it accepts the connection, otherwise the * node will close the connection.</p> * * <p>Version Message:</p> * <pre> * Size Field Description * ==== ===== =========== * 4 bytes Version Protocol version * 8 bytes Services Supported services (bit field) * 8 bytes Timestamp Time in seconds since the epoch * 26 bytes RemoteAddress Remote node address * 26 bytes LocalAddress Local node address * 8 bytes Nonce Random value to identify sending node * VarString UserAgent Identification string * 4 bytes BlockHeight Last block received by sending node * 1 byte TxRelay TRUE if remote peer should relay transactions * </pre> * * <p>Network Address:</p> * <pre> * Size Field Description * ==== ===== =========== * 8 bytes Services Supported services (same as 'version' message) * 16 bytes NetworkAddress IPv6 address (IPv4 address encoded as IPv6 address) * 2 bytes Port Port (network byte order) * </pre> */ public class VersionMessage { /** Node identifier for this peer execution */ public static final long NODE_ID = Double.doubleToRawLongBits(Double.valueOf(Math.random())); /** * Builds a 'version' message * * @param peer The remote peer * @param localAddress Local listen address or null if not accepting inbound connections * @param chainHeight Current chain height * @return Message to send to remote peer */ public static Message buildVersionMessage(Peer peer, PeerAddress localAddress, int chainHeight) { // // Set the protocol version, supported services and current time // SerializedBuffer msgBuffer = new SerializedBuffer(); msgBuffer.putInt(NetParams.PROTOCOL_VERSION) .putLong(NetParams.SUPPORTED_SERVICES) .putLong(System.currentTimeMillis()/1000); // // Set the destination address // PeerAddress peerAddress = peer.getAddress(); byte[] dstAddress = peerAddress.getAddress().getAddress(); msgBuffer.skip(8); if (dstAddress.length == 16) { msgBuffer.putBytes(dstAddress); } else { msgBuffer.putBytes(PeerAddress.IPV6_PREFIX); msgBuffer.putBytes(dstAddress); } msgBuffer.putUnsignedByte(peerAddress.getPort()>>>8) .putUnsignedByte(peerAddress.getPort()); // // Set the source address // msgBuffer.putLong(NetParams.SUPPORTED_SERVICES); if (localAddress != null) { byte[] srcAddress = localAddress.getAddress().getAddress(); if (srcAddress.length == 16) { msgBuffer.putBytes(srcAddress); } else { msgBuffer.putBytes(PeerAddress.IPV6_PREFIX) .putBytes(srcAddress); } msgBuffer.putUnsignedByte(localAddress.getPort()>>>8) .putUnsignedByte(localAddress.getPort()); } else { msgBuffer.skip(16+2); } // // Set the random node identifier // msgBuffer.putLong(NODE_ID); // // Set the agent name // String agentName = String.format("/%s/%s/",LSystem.applicationName, LSystem.applicationVersion); msgBuffer.putString(agentName); // // Set the chain height and transaction relay flag // msgBuffer.putInt(chainHeight); msgBuffer.putBoolean((NetParams.SUPPORTED_SERVICES&NetParams.NODE_NETWORK)!=0); // // Build the message // ByteBuffer buffer = MessageHeader.buildMessage("version", msgBuffer); return new Message(buffer, peer, MessageHeader.MessageCommand.VERSION); } /** * Processes a 'version' message * * @param msg Message * @param inBuffer Input buffer * @param msgListener Message listener * @throws EOFException End-of-data processing message data * @throws VerificationException Message verification failed */ public static void processVersionMessage(Message msg, SerializedBuffer inBuffer, MessageListener msgListener) throws EOFException, VerificationException { Peer peer = msg.getPeer(); // // Validate the protocol level // int version = inBuffer.getInt(); if (version < NetParams.MIN_PROTOCOL_VERSION) throw new VerificationException(String.format("Protocol version %d is not supported", version), RejectMessage.REJECT_OBSOLETE); peer.setVersion(version); // // Get the peer services // peer.setServices(inBuffer.getLong()); peer.getAddress().setServices(peer.getServices()); // // Get our address as seen by the peer // inBuffer.skip(8+8); byte[] addrBytes = inBuffer.getBytes(16); InetAddress addr; try { boolean ipv4 = true; for (int j=0; j<12; j++) { if (addrBytes[j] != PeerAddress.IPV6_PREFIX[j]) { ipv4 = false; break; } } if (ipv4) addr = InetAddress.getByAddress(Arrays.copyOfRange(addrBytes, 12, 16)); else addr = InetAddress.getByAddress(addrBytes); } catch (UnknownHostException exc) { throw new VerificationException("Destination address is not valid: "+exc.getMessage()); } PeerAddress localAddress = new PeerAddress(addr, (inBuffer.getUnsignedByte()<<8)|inBuffer.getUnsignedByte()); // // Get the user agent // inBuffer.skip(26+8); peer.setUserAgent(inBuffer.getString()); // // Get the chain height and transaction relay flag (the transaction relay flag is // not included in earlier protocol versions and defaults to TRUE). Always relay // block notifications. // peer.setHeight(inBuffer.getInt()); peer.setTxRelay(inBuffer.available()==0 || inBuffer.getByte()!=0); peer.setBlockRelay(true); // // Notify the message listener // msgListener.processVersion(msg, localAddress); } }