diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 34349e09..e6a0e76b 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,6 +1,7 @@ ## master * Skip merging PeerInfo entries, really not needed +* Adding support for host_uri in configs ## 1.5.1 diff --git a/client/BUILD b/client/BUILD index 836b0b03..63a6efe1 100644 --- a/client/BUILD +++ b/client/BUILD @@ -21,6 +21,8 @@ java_library( "@bitcoinj//jar", "@slf4j_nop//jar", "@slf4j_api//jar", + "@io_grpc_grpc_java//netty", + "@io_netty_netty_handler//:io_netty_netty_handler", ], ) diff --git a/client/src/SnowBlossomClient.java b/client/src/SnowBlossomClient.java index 21b1bf8e..7887f093 100644 --- a/client/src/SnowBlossomClient.java +++ b/client/src/SnowBlossomClient.java @@ -404,22 +404,18 @@ public SnowBlossomClient(Config config, String import_seed) throws Exception { this.config = config; logger.info(String.format("Starting SnowBlossomClient version %s", Globals.VERSION)); - config.require("node_host"); - - String host = config.get("node_host"); params = NetworkParams.loadFromConfig(config); - int port = config.getIntWithDefault("node_port", params.getDefaultPort()); + + ManagedChannel channel = StubUtil.openChannel(config, params); exec = TaskMaster.getBasicExecutor(64,"client_lookup"); - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build(); asyncStub = UserServiceGrpc.newStub(channel); blockingStub = UserServiceGrpc.newBlockingStub(channel); get_utxo_util = new GetUTXOUtil(blockingStub, params); - if (config.isSet("wallet_path")) { wallet_path = new File(config.get("wallet_path")); diff --git a/client/src/StubUtil.java b/client/src/StubUtil.java new file mode 100644 index 00000000..962ab6b6 --- /dev/null +++ b/client/src/StubUtil.java @@ -0,0 +1,129 @@ +package snowblossom.client; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import java.net.URI; +import java.io.ByteArrayInputStream; +import java.util.Properties; + +import io.netty.handler.ssl.SslContext; +import io.grpc.netty.GrpcSslContexts; +import snowblossom.lib.tls.SnowTrustManagerFactorySpi; +import io.grpc.netty.NettyChannelBuilder; + + +import duckutil.Config; +import snowblossom.lib.NetworkParams; +import snowblossom.lib.AddressSpecHash; +import snowblossom.lib.Globals; +import snowblossom.proto.UserServiceGrpc; +import snowblossom.proto.UserServiceGrpc.UserServiceBlockingStub; +import snowblossom.proto.UserServiceGrpc.UserServiceStub; + +public class StubUtil +{ + public static ManagedChannel openChannel(Config config, NetworkParams params) + throws Exception + { + if (config.isSet("node_uri")) + { + if (config.isSet("node_host")) + { + throw new Exception("node_host and node_uri are mutually exclusive. Pick one."); + } + + if (config.isSet("node_port")) + { + throw new Exception("node_port is only for node_host, not node_url"); + } + return openChannel(config.get("node_uri"), params); + } + if (config.isSet("node_host")) + { + String uri = "grpc://" + config.get("node_host"); + if (config.isSet("node_port")) + { + uri += ":" + config.get("node_port"); + } + return openChannel(uri, params); + } + + throw new Exception("Must have either 'node_uri' or 'node_host'"); + } + + public static ManagedChannel openChannel(String uri, NetworkParams params) + throws Exception + { + URI u = new URI(uri); + + String host = u.getHost(); + int port = u.getPort(); + String scheme = u.getScheme(); + if (scheme == null) scheme="grpc"; + + if (port == -1) + { + if (scheme.equals("grpc")) + { + port = params.getDefaultPort(); + } + else if (scheme.equals("grpc+tls")) + { + port = params.getDefaultTlsPort(); + } + else + { + throw new Exception("Unknown scheme: " + scheme); + } + } + + if (scheme.equals("grpc")) + { + if (port == -1) port = params.getDefaultPort(); + return ManagedChannelBuilder + .forAddress(host, port) + .usePlaintext(true) + .build(); + } + else if (scheme.equals("grpc+tls")) + { + if (port == -1) port = params.getDefaultTlsPort(); + AddressSpecHash expected_key = null; + + String query=u.getQuery(); + Properties query_props = new Properties(); + if (query!=null) + { + query_props.load(new ByteArrayInputStream( query.replace('&','\n').getBytes() )); + + if (query_props.getProperty("key") != null) + { + expected_key = new AddressSpecHash(query_props.getProperty("key"), Globals.NODE_ADDRESS_STRING); + } + } + + SslContext ssl_ctx = GrpcSslContexts.forClient() + .trustManager(SnowTrustManagerFactorySpi.getFactory(expected_key, params)) + .build(); + return NettyChannelBuilder + .forAddress(host, port) + .useTransportSecurity() + .sslContext(ssl_ctx) + .build(); + } + else + { + throw new Exception("Unknown scheme: " + scheme); + } + } + + public static UserServiceBlockingStub getBlockingStub(ManagedChannel channel) + { + return UserServiceGrpc.newBlockingStub(channel); + } + + public static UserServiceStub getAsyncStub(ManagedChannel channel) + { + return UserServiceGrpc.newStub(channel); + } + +} diff --git a/lib/src/AddressSpecHash.java b/lib/src/AddressSpecHash.java index b1c1284d..56a0394f 100644 --- a/lib/src/AddressSpecHash.java +++ b/lib/src/AddressSpecHash.java @@ -25,7 +25,13 @@ public AddressSpecHash(byte[] b) public AddressSpecHash(String address, NetworkParams params) throws ValidationException { - AddressSpecHash o = AddressUtil.getHashForAddress( params.getAddressPrefix(), address); + this(address, params.getAddressPrefix()); + } + + public AddressSpecHash(String address, String prefix) + throws ValidationException + { + AddressSpecHash o = AddressUtil.getHashForAddress( prefix, address); bytes = o.getBytes(); if (bytes.size() != Globals.ADDRESS_SPEC_HASH_LEN) @@ -34,6 +40,7 @@ public AddressSpecHash(String address, NetworkParams params) } } + public AddressSpecHash(String str) { Assert.assertEquals(Globals.ADDRESS_SPEC_HASH_LEN*2, str.length()); diff --git a/lib/src/NetworkParams.java b/lib/src/NetworkParams.java index f5590414..549a4fd0 100644 --- a/lib/src/NetworkParams.java +++ b/lib/src/NetworkParams.java @@ -50,6 +50,7 @@ public BigInteger getMaxTarget() public abstract List getSeedNodes(); public int getDefaultPort() { return 2338; } + public int getDefaultTlsPort() { return 2348; } public int getActivationHeightTxOutRequirements() { return Integer.MAX_VALUE; } public int getActivationHeightTxOutExtras() { return Integer.MAX_VALUE; } diff --git a/lib/src/NetworkParamsRegtest.java b/lib/src/NetworkParamsRegtest.java index 87d63fc2..e032ba1e 100644 --- a/lib/src/NetworkParamsRegtest.java +++ b/lib/src/NetworkParamsRegtest.java @@ -57,8 +57,11 @@ public List getSeedNodes() { return ImmutableList.of("seed-regtest.snowblossom.org"); } + @Override public int getDefaultPort() { return 2340; } + @Override + public int getDefaultTlsPort() { return 2350; } @Override public int getActivationHeightTxOutRequirements() { return 1000; } diff --git a/lib/src/NetworkParamsTestnet.java b/lib/src/NetworkParamsTestnet.java index 14ca29c8..36213b43 100644 --- a/lib/src/NetworkParamsTestnet.java +++ b/lib/src/NetworkParamsTestnet.java @@ -58,6 +58,9 @@ public List getSeedNodes() @Override public int getDefaultPort() { return 2339; } + @Override + public int getDefaultTlsPort() { return 2349; } + @Override public ByteString getBlockZeroRemark() { return ByteString.copyFrom(new String("testnet2-20180516").getBytes()); } diff --git a/miner/src/MrPlow.java b/miner/src/MrPlow.java index e510de4e..115b8bd8 100644 --- a/miner/src/MrPlow.java +++ b/miner/src/MrPlow.java @@ -15,6 +15,7 @@ import snowblossom.lib.db.DB; import snowblossom.lib.db.lobstack.LobstackDB; import snowblossom.lib.db.rocksdb.JRocksDB; +import snowblossom.client.StubUtil; import io.grpc.Server; import io.grpc.ServerBuilder; @@ -87,7 +88,6 @@ public MrPlow(Config config) throws Exception this.config = config; logger.info(String.format("Starting MrPlow version %s", Globals.VERSION)); - config.require("node_host"); config.require("pool_address"); config.require("pool_fee"); config.require("db_type"); @@ -236,9 +236,7 @@ private void subscribe() throws Exception channel = null; } - String host = config.get("node_host"); - int port = config.getIntWithDefault("node_port", params.getDefaultPort()); - channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build(); + channel = StubUtil.openChannel(config, params); asyncStub = UserServiceGrpc.newStub(channel); blockingStub = UserServiceGrpc.newBlockingStub(channel); diff --git a/miner/src/SnowBlossomMiner.java b/miner/src/SnowBlossomMiner.java index 1ccc781e..09542db8 100644 --- a/miner/src/SnowBlossomMiner.java +++ b/miner/src/SnowBlossomMiner.java @@ -15,6 +15,7 @@ import snowblossom.proto.UserServiceGrpc.UserServiceStub; import snowblossom.lib.trie.HashUtils; import snowblossom.client.WalletUtil; +import snowblossom.client.StubUtil; import java.io.File; @@ -82,7 +83,6 @@ public SnowBlossomMiner(Config config) throws Exception logger.info(String.format("Starting SnowBlossomMiner version %s", Globals.VERSION)); config.require("snow_path"); - config.require("node_host"); params = NetworkParams.loadFromConfig(config); @@ -125,9 +125,7 @@ private void subscribe() throws Exception channel = null; } - String host = config.get("node_host"); - int port = config.getIntWithDefault("node_port", params.getDefaultPort()); - channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build(); + channel = StubUtil.openChannel(config, params); asyncStub = UserServiceGrpc.newStub(channel); blockingStub = UserServiceGrpc.newBlockingStub(channel); diff --git a/shackleton/src/RichList.java b/shackleton/src/RichList.java index 7723e3da..0dacbad5 100644 --- a/shackleton/src/RichList.java +++ b/shackleton/src/RichList.java @@ -17,6 +17,7 @@ import java.util.Map; import com.google.common.collect.TreeMultimap; import snowblossom.client.GetUTXOUtil; +import snowblossom.client.StubUtil; import java.text.DecimalFormat; import com.google.protobuf.ByteString; @@ -56,13 +57,10 @@ public RichList(Config config) { this.config = config; - config.require("node_host"); params = NetworkParams.loadFromConfig(config); - String host = config.get("node_host"); - int port = config.getIntWithDefault("node_port", params.getDefaultPort()); - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build(); + ManagedChannel channel = StubUtil.openChannel(config, params); asyncStub = UserServiceGrpc.newStub(channel); stub = UserServiceGrpc.newBlockingStub(channel); diff --git a/shackleton/src/Shackleton.java b/shackleton/src/Shackleton.java index d6ef2d21..b3858322 100644 --- a/shackleton/src/Shackleton.java +++ b/shackleton/src/Shackleton.java @@ -15,6 +15,7 @@ import java.util.logging.Logger; import snowblossom.client.GetUTXOUtil; +import snowblossom.client.StubUtil; /** Yes a penguin taught me french back in antacrtica */ @@ -55,13 +56,10 @@ public Shackleton(Config config) this.config = config; web_server = new WebServer(config, this); - config.require("node_host"); params = NetworkParams.loadFromConfig(config); - String host = config.get("node_host"); - int port = config.getIntWithDefault("node_port", params.getDefaultPort()); - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build(); + ManagedChannel channel = StubUtil.openChannel(config, params); asyncStub = UserServiceGrpc.newStub(channel); blockingStub = UserServiceGrpc.newBlockingStub(channel); diff --git a/systemtests/test/SpoonTest.java b/systemtests/test/SpoonTest.java index fb57e8ed..66fb9107 100644 --- a/systemtests/test/SpoonTest.java +++ b/systemtests/test/SpoonTest.java @@ -328,11 +328,9 @@ private PoolMiner startPoolMiner(int port, AddressSpecHash mine_to, File snow_pa private SnowBlossomClient startClient(int port) throws Exception { Map config_map = new TreeMap<>(); - config_map.put("node_host", "localhost"); - config_map.put("node_port", "" + port); + config_map.put("node_uri", "grpc://localhost:" + port); config_map.put("network", "spoon"); return new SnowBlossomClient(new ConfigMem(config_map)); - } }