diff --git a/Dockerfile b/Dockerfile index 343b61b..1fd53e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,10 +20,10 @@ RUN useradd --create-home --uid 1000 minichain \ && chown -R minichain:minichain /app USER minichain -EXPOSE 9000 +EXPOSE 9000 8545 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD python -c "import socket; socket.create_connection(('127.0.0.1', 8545), timeout=3)" ENTRYPOINT ["python", "main.py"] -CMD ["--host", "0.0.0.0", "--port", "9000"] +CMD ["--host", "0.0.0.0", "--rpc-host", "0.0.0.0", "--port", "9000"] diff --git a/main.py b/main.py index b37fce4..7c07251 100644 --- a/main.py +++ b/main.py @@ -605,7 +605,7 @@ def print_prompt_info(current_pk): # Main entry point # ────────────────────────────────────────────── -async def run_node(port: int, host: str, connect_to: str | None, fund: int, datadir: str | None): +async def run_node(port: int, host: str, connect_to: str | None, fund: int, datadir: str | None, rpc_host: str = "127.0.0.1"): """Boot the node, optionally connect to a peer, then enter the CLI.""" sk, pk = load_or_create_wallet(datadir) @@ -656,7 +656,7 @@ async def on_peer_connected(writer): # Start RPC server on a port correlated to the node port (e.g. 8545 if P2P is 9000) rpc_port = 8545 + (port - 9000) - await rpc_server.start(host="127.0.0.1", port=rpc_port) + await rpc_server.start(host=rpc_host, port=rpc_port) # Fund this node's wallet so it can transact in the demo if fund > 0: @@ -683,13 +683,19 @@ async def on_peer_connected(writer): await network.stop() -def main(): +def build_arg_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="MiniChain Node — Testnet Demo") parser.add_argument("--host", type=str, default="127.0.0.1", help="Host/IP to bind the P2P server (default: 127.0.0.1)") + parser.add_argument("--rpc-host", type=str, default="127.0.0.1", help="Host/IP to bind the RPC server (default: 127.0.0.1)") parser.add_argument("--port", type=int, default=9000, help="TCP port to listen on (default: 9000)") parser.add_argument("--connect", type=str, default=None, help="Peer address to connect to (multiaddr)") parser.add_argument("--fund", type=int, default=100, help="Initial coins to fund this wallet (default: 100)") parser.add_argument("--datadir", type=str, default=".minichain", help="Directory to save/load blockchain state (enables persistence)") + return parser + + +def main(): + parser = build_arg_parser() args = parser.parse_args() logging.basicConfig( @@ -699,7 +705,7 @@ def main(): ) try: - asyncio.run(run_node(args.port, args.host, args.connect, args.fund, args.datadir)) + asyncio.run(run_node(args.port, args.host, args.connect, args.fund, args.datadir, args.rpc_host)) except KeyboardInterrupt: print("\nNode shut down.") diff --git a/tests/test_cli_args.py b/tests/test_cli_args.py new file mode 100644 index 0000000..07e42fb --- /dev/null +++ b/tests/test_cli_args.py @@ -0,0 +1,27 @@ +import unittest + +import main + + +class TestCliArgs(unittest.TestCase): + def test_rpc_host_defaults_to_loopback(self): + """Bare-metal behavior is unchanged unless --rpc-host is passed.""" + args = main.build_arg_parser().parse_args([]) + self.assertEqual(args.rpc_host, "127.0.0.1") + self.assertEqual(args.host, "127.0.0.1") + + def test_rpc_host_can_be_overridden(self): + """--rpc-host lets the RPC server bind independently of --host.""" + args = main.build_arg_parser().parse_args(["--rpc-host", "0.0.0.0"]) + self.assertEqual(args.rpc_host, "0.0.0.0") + + def test_host_and_rpc_host_are_independent(self): + args = main.build_arg_parser().parse_args( + ["--host", "0.0.0.0", "--rpc-host", "127.0.0.1"] + ) + self.assertEqual(args.host, "0.0.0.0") + self.assertEqual(args.rpc_host, "127.0.0.1") + + +if __name__ == "__main__": + unittest.main()