/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ethereumJ library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.net.rlpx; import org.ethereum.crypto.ECKey; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; import org.ethereum.util.Utils; import org.spongycastle.util.encoders.Hex; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import static org.ethereum.crypto.HashUtil.sha3; import static org.ethereum.util.ByteUtil.byteArrayToInt; import static org.ethereum.util.ByteUtil.bytesToIp; import static org.ethereum.util.ByteUtil.hostToBytes; public class Node implements Serializable { private static final long serialVersionUID = -4267600517925770636L; byte[] id; String host; int port; // discovery endpoint doesn't have real nodeId for example private boolean isFakeNodeId = false; /** * - create Node instance from enode if passed, * - otherwise fallback to random nodeId, if supplied with only "address:port" * NOTE: validation is absent as method is not heavily used */ public static Node instanceOf(String addressOrEnode) { try { URI uri = new URI(addressOrEnode); if (uri.getScheme().equals("enode")) { return new Node(addressOrEnode); } } catch (URISyntaxException e) { // continue } final ECKey generatedNodeKey = ECKey.fromPrivate(sha3(addressOrEnode.getBytes())); final String generatedNodeId = Hex.toHexString(generatedNodeKey.getNodeId()); final Node node = new Node("enode://" + generatedNodeId + "@" + addressOrEnode); node.isFakeNodeId = true; return node; } public Node(String enodeURL) { try { URI uri = new URI(enodeURL); if (!uri.getScheme().equals("enode")) { throw new RuntimeException("expecting URL in the format enode://PUBKEY@HOST:PORT"); } this.id = Hex.decode(uri.getUserInfo()); this.host = uri.getHost(); this.port = uri.getPort(); } catch (URISyntaxException e) { throw new RuntimeException("expecting URL in the format enode://PUBKEY@HOST:PORT", e); } } public Node(byte[] id, String host, int port) { this.id = id; this.host = host; this.port = port; } public Node(byte[] rlp) { RLPList nodeRLP = RLP.decode2(rlp); nodeRLP = (RLPList) nodeRLP.get(0); byte[] hostB = nodeRLP.get(0).getRLPData(); byte[] portB = nodeRLP.get(1).getRLPData(); byte[] idB; if (nodeRLP.size() > 3) { idB = nodeRLP.get(3).getRLPData(); } else { idB = nodeRLP.get(2).getRLPData(); } int port = byteArrayToInt(portB); this.host = bytesToIp(hostB); this.port = port; this.id = idB; } /** * @return true if this node is endpoint for discovery loaded from config */ public boolean isDiscoveryNode() { return isFakeNodeId; } public byte[] getId() { return id; } public String getHexId() { return Hex.toHexString(id); } public String getHexIdShort() { return Utils.getNodeIdShort(getHexId()); } public void setId(byte[] id) { this.id = id; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public void setDiscoveryNode(boolean isDiscoveryNode) { isFakeNodeId = isDiscoveryNode; } /** * Full RLP * [host, udpPort, tcpPort, nodeId] * @return RLP-encoded node data */ public byte[] getRLP() { byte[] rlphost = RLP.encodeElement(hostToBytes(host)); byte[] rlpTCPPort = RLP.encodeInt(port); byte[] rlpUDPPort = RLP.encodeInt(port); byte[] rlpId = RLP.encodeElement(id); return RLP.encodeList(rlphost, rlpUDPPort, rlpTCPPort, rlpId); } /** * RLP without nodeId * [host, udpPort, tcpPort] * @return RLP-encoded node data */ public byte[] getBriefRLP() { byte[] rlphost = RLP.encodeElement(hostToBytes(host)); byte[] rlpTCPPort = RLP.encodeInt(port); byte[] rlpUDPPort = RLP.encodeInt(port); return RLP.encodeList(rlphost, rlpUDPPort, rlpTCPPort); } @Override public String toString() { return "Node{" + " host='" + host + '\'' + ", port=" + port + ", id=" + Hex.toHexString(id) + '}'; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public boolean equals(Object o) { if (o == null) { return false; } if (o == this) { return true; } if (o instanceof Node) { return Arrays.equals(((Node) o).getId(), this.getId()); } return false; } }