You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Wonerujo/app/src/main/java/com/m2049r/xmrwallet/data/Node.java

307 lines
9.7 KiB

/*
* Copyright (c) 2018 m2049r
*
* 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.m2049r.xmrwallet.data;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import lombok.Getter;
import lombok.Setter;
import timber.log.Timber;
public class Node {
static public final String MAINNET = "mainnet";
static public final String STAGENET = "stagenet";
static public final String TESTNET = "testnet";
@Getter
private String name = null;
@Getter
final private NetworkType networkType;
InetAddress hostAddress;
@Getter
private String host;
@Getter
@Setter
int rpcPort = 0;
private int levinPort = 0;
@Getter
@Setter
private String username = "";
@Getter
@Setter
private String password = "";
@Getter
@Setter
private boolean favourite = false;
@Getter
@Setter
private boolean selected = false;
@Override
public int hashCode() {
return hostAddress.hashCode();
}
// Nodes are equal if they are the same host address:port & are on the same network
@Override
public boolean equals(Object other) {
if (!(other instanceof Node)) return false;
final Node anotherNode = (Node) other;
return (hostAddress.equals(anotherNode.hostAddress)
&& (rpcPort == anotherNode.rpcPort)
&& (networkType == anotherNode.networkType));
}
static public Node fromString(String nodeString) {
try {
return new Node(nodeString);
} catch (IllegalArgumentException ex) {
Timber.w(ex);
return null;
}
}
Node(String nodeString) {
if ((nodeString == null) || nodeString.isEmpty())
throw new IllegalArgumentException("daemon is empty");
String daemonAddress;
String a[] = nodeString.split("@");
if (a.length == 1) { // no credentials
daemonAddress = a[0];
username = "";
password = "";
} else if (a.length == 2) { // credentials
String userPassword[] = a[0].split(":");
if (userPassword.length != 2)
throw new IllegalArgumentException("User:Password invalid");
username = userPassword[0];
if (!username.isEmpty()) {
password = userPassword[1];
} else {
password = "";
}
daemonAddress = a[1];
} else {
throw new IllegalArgumentException("Too many @");
}
String daParts[] = daemonAddress.split("/");
if ((daParts.length > 3) || (daParts.length < 1))
throw new IllegalArgumentException("Too many '/' or too few");
daemonAddress = daParts[0];
String da[] = daemonAddress.split(":");
if ((da.length > 2) || (da.length < 1))
throw new IllegalArgumentException("Too many ':' or too few");
String host = da[0];
if (daParts.length == 1) {
networkType = NetworkType.NetworkType_Mainnet;
} else {
switch (daParts[1]) {
case MAINNET:
networkType = NetworkType.NetworkType_Mainnet;
break;
case STAGENET:
networkType = NetworkType.NetworkType_Stagenet;
break;
case TESTNET:
networkType = NetworkType.NetworkType_Testnet;
break;
default:
throw new IllegalArgumentException("invalid net: " + daParts[1]);
}
}
if (networkType != WalletManager.getInstance().getNetworkType())
throw new IllegalArgumentException("wrong net: " + networkType);
String name = host;
if (daParts.length == 3) {
try {
name = URLDecoder.decode(daParts[2], "UTF-8");
} catch (UnsupportedEncodingException ex) {
Timber.w(ex); // if we can't encode it, we don't use it
}
}
this.name = name;
int port;
if (da.length == 2) {
try {
port = Integer.parseInt(da[1]);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Port not numeric");
}
} else {
port = getDefaultRpcPort();
}
try {
setHost(host);
} catch (UnknownHostException ex) {
throw new IllegalArgumentException("cannot resolve host " + host);
}
this.rpcPort = port;
this.levinPort = getDefaultLevinPort();
}
public String toNodeString() {
return toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (!username.isEmpty() && !password.isEmpty()) {
sb.append(username).append(":").append(password).append("@");
}
sb.append(host).append(":").append(rpcPort);
sb.append("/");
switch (networkType) {
case NetworkType_Mainnet:
sb.append(MAINNET);
break;
case NetworkType_Stagenet:
sb.append(STAGENET);
break;
case NetworkType_Testnet:
sb.append(TESTNET);
break;
}
if (name != null)
try {
sb.append("/").append(URLEncoder.encode(name, "UTF-8"));
} catch (UnsupportedEncodingException ex) {
Timber.w(ex); // if we can't encode it, we don't store it
}
return sb.toString();
}
public Node() {
this.networkType = WalletManager.getInstance().getNetworkType();
}
// constructor used for created nodes from retrieved peer lists
public Node(InetSocketAddress socketAddress) {
this();
this.hostAddress = socketAddress.getAddress();
this.host = socketAddress.getHostString();
this.rpcPort = 0; // unknown
this.levinPort = socketAddress.getPort();
this.username = "";
this.password = "";
}
public String getAddress() {
return getHostAddress() + ":" + rpcPort;
}
public String getHostAddress() {
return hostAddress.getHostAddress();
}
public void setHost(String host) throws UnknownHostException {
if ((host == null) || (host.isEmpty()))
throw new UnknownHostException("loopback not supported (yet?)");
this.host = host;
this.hostAddress = InetAddress.getByName(host);
}
public void setName() {
if (name == null)
this.name = hostAddress.getHostName();
}
public void setName(String name) {
if ((name == null) || (name.isEmpty()))
this.name = hostAddress.getHostName();
else
this.name = name;
}
public void toggleFavourite() {
favourite = !favourite;
}
public Node(Node anotherNode) {
networkType = anotherNode.networkType;
overwriteWith(anotherNode);
}
public void overwriteWith(Node anotherNode) {
if (networkType != anotherNode.networkType)
throw new IllegalStateException("network types do not match");
name = anotherNode.name;
hostAddress = anotherNode.hostAddress;
host = anotherNode.host;
rpcPort = anotherNode.rpcPort;
levinPort = anotherNode.levinPort;
username = anotherNode.username;
password = anotherNode.password;
favourite = anotherNode.favourite;
}
static private int DEFAULT_LEVIN_PORT = 0;
// every node knows its network, but they are all the same
static public int getDefaultLevinPort() {
if (DEFAULT_LEVIN_PORT > 0) return DEFAULT_LEVIN_PORT;
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
DEFAULT_LEVIN_PORT = 34567;
break;
case NetworkType_Testnet:
DEFAULT_LEVIN_PORT = 11180;
break;
case NetworkType_Stagenet:
DEFAULT_LEVIN_PORT = 38080;
break;
default:
throw new IllegalStateException("unsupported net " + WalletManager.getInstance().getNetworkType());
}
return DEFAULT_LEVIN_PORT;
}
static private int DEFAULT_RPC_PORT = 0;
// every node knows its network, but they are all the same
static public int getDefaultRpcPort() {
if (DEFAULT_RPC_PORT > 0) return DEFAULT_RPC_PORT;
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
DEFAULT_RPC_PORT = 34568;
break;
case NetworkType_Testnet:
DEFAULT_RPC_PORT = 11181;
break;
case NetworkType_Stagenet:
DEFAULT_RPC_PORT = 38081;
break;
default:
throw new IllegalStateException("unsupported net " + WalletManager.getInstance().getNetworkType());
}
return DEFAULT_RPC_PORT;
}
}