diff --git a/src/common.h b/src/common.h
index 497f7de..8d61ad9 100644
--- a/src/common.h
+++ b/src/common.h
@@ -251,6 +251,13 @@ struct ChainMain
 	hash id;
 };
 
+enum class NetworkType {
+	Invalid,
+	Mainnet,
+	Testnet,
+	Stagenet,
+};
+
 } // namespace p2pool
 
 #include "util.h"
diff --git a/src/json_parsers.h b/src/json_parsers.h
index ad5243b..1274fe9 100644
--- a/src/json_parsers.h
+++ b/src/json_parsers.h
@@ -54,6 +54,7 @@ JSON_VALUE_PARSER(String, const char*)
 JSON_VALUE_PARSER(String, std::string)
 JSON_VALUE_PARSER(Uint, uint8_t)
 JSON_VALUE_PARSER(Uint64, uint64_t)
+JSON_VALUE_PARSER(Bool, bool)
 
 #undef JSON_VALUE_PARSER
 
diff --git a/src/log.h b/src/log.h
index 4cc194d..60c9e87 100644
--- a/src/log.h
+++ b/src/log.h
@@ -359,6 +359,19 @@ template<> struct log::Stream::Entry<XMRAmount>
 	}
 };
 
+template<> struct log::Stream::Entry<NetworkType>
+{
+	static NOINLINE void put(const NetworkType& value, Stream* wrapper)
+	{
+		switch (value) {
+		case NetworkType::Invalid:  *wrapper << "invalid";  break;
+		case NetworkType::Mainnet:  *wrapper << "mainnet";  break;
+		case NetworkType::Testnet:  *wrapper << "testnet";  break;
+		case NetworkType::Stagenet: *wrapper << "stagenet"; break;
+		}
+	}
+};
+
 namespace {
 	template<log::Severity severity> void apply_severity(log::Stream&);
 
diff --git a/src/p2pool.cpp b/src/p2pool.cpp
index 74fe141..796890d 100644
--- a/src/p2pool.cpp
+++ b/src/p2pool.cpp
@@ -51,12 +51,12 @@ p2pool::p2pool(int argc, char* argv[])
 		panic();
 	}
 
-	const Wallet::Type type = m_params->m_wallet.type();
+	const NetworkType type = m_params->m_wallet.type();
 
-	if (type == Wallet::Type::Testnet) {
+	if (type == NetworkType::Testnet) {
 		LOGWARN(1, "Mining to a testnet wallet address");
 	}
-	else if (type == Wallet::Type::Stagenet) {
+	else if (type == NetworkType::Stagenet) {
 		LOGWARN(1, "Mining to a stagenet wallet address");
 	}
 
@@ -86,7 +86,7 @@ p2pool::p2pool(int argc, char* argv[])
 
 	MinerData d;
 
-	m_sideChain = new SideChain(this);
+	m_sideChain = new SideChain(this, type);
 	m_hasher = new RandomX_Hasher(this);
 	m_blockTemplate = new BlockTemplate(this);
 	m_mempool = new Mempool();
@@ -530,6 +530,63 @@ void p2pool::stratum_on_block()
 #endif
 }
 
+void p2pool::get_info()
+{
+	JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_info\"}",
+		[this](const char* data, size_t size)
+		{
+			parse_get_info_rpc(data, size);
+		});
+}
+
+void p2pool::parse_get_info_rpc(const char* data, size_t size)
+{
+	rapidjson::Document doc;
+	doc.Parse(data, size);
+
+	if (!doc.IsObject() || !doc.HasMember("result")) {
+		LOGWARN(1, "get_info RPC response is invalid (\"result\" not found), trying again in 1 second");
+		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+		get_info();
+		return;
+	}
+
+	const auto& result = doc["result"];
+
+	struct {
+		bool busy_syncing, mainnet, testnet, stagenet;
+	} info;
+
+	if (!PARSE(result, info, busy_syncing) || !PARSE(result, info, mainnet) || !PARSE(result, info, testnet) || !PARSE(result, info, stagenet)) {
+		LOGWARN(1, "get_info RPC response is invalid, trying again in 1 second");
+		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+		get_info();
+		return;
+	}
+
+	if (info.busy_syncing) {
+		LOGINFO(1, "monerod is busy syncing, trying again in 1 second");
+		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+		get_info();
+		return;
+	}
+
+	NetworkType monero_network = NetworkType::Invalid;
+
+	if (info.mainnet)  monero_network = NetworkType::Mainnet;
+	if (info.testnet)  monero_network = NetworkType::Testnet;
+	if (info.stagenet) monero_network = NetworkType::Stagenet;
+
+	const NetworkType sidechain_network = m_sideChain->network_type();
+
+	if (monero_network != sidechain_network) {
+		LOGERR(1, "monerod is on " << monero_network << ", but you're mining to a " << sidechain_network << " sidechain");
+		panic();
+	}
+
+	get_miner_data();
+}
+
 void p2pool::get_miner_data()
 {
 	JSONRPCRequest::call(m_params->m_host, m_params->m_rpcPort, "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"get_miner_data\"}",
@@ -761,7 +818,7 @@ int p2pool::run()
 
 	{
 		ZMQReader z(m_params->m_host, m_params->m_rpcPort, m_params->m_zmqPort, this);
-		get_miner_data();
+		get_info();
 		const int rc = uv_run(uv_default_loop_checked(), UV_RUN_DEFAULT);
 		LOGINFO(1, "uv_run exited, result = " << rc);
 	}
diff --git a/src/p2pool.h b/src/p2pool.h
index a4ab765..71dc4bc 100644
--- a/src/p2pool.h
+++ b/src/p2pool.h
@@ -102,6 +102,9 @@ private:
 
 	void stratum_on_block();
 
+	void get_info();
+	void parse_get_info_rpc(const char* data, size_t size);
+
 	void get_miner_data();
 	void parse_get_miner_data_rpc(const char* data, size_t size);
 
diff --git a/src/side_chain.cpp b/src/side_chain.cpp
index e2ac573..46ec91f 100644
--- a/src/side_chain.cpp
+++ b/src/side_chain.cpp
@@ -51,8 +51,9 @@ static_assert(1 <= UNCLE_BLOCK_DEPTH && UNCLE_BLOCK_DEPTH <= 10, "Invalid UNCLE_
 
 namespace p2pool {
 
-SideChain::SideChain(p2pool* pool)
+SideChain::SideChain(p2pool* pool, NetworkType type)
 	: m_pool(pool)
+	, m_networkType(type)
 	, m_chainTip(nullptr)
 	, m_poolName("default")
 	, m_targetBlockTime(1)
@@ -61,6 +62,8 @@ SideChain::SideChain(p2pool* pool)
 	, m_unclePenalty(20)
 	, m_curDifficulty(m_minDifficulty)
 {
+	LOGINFO(1, log::LightCyan() << "network type  = " << m_networkType);
+
 	if (!load_config(m_pool->params().m_config)) {
 		panic();
 	}
@@ -90,12 +93,13 @@ SideChain::SideChain(p2pool* pool)
 		char consensus_str[log::Stream::BUF_SIZE + 1];
 		log::Stream s(consensus_str);
 
-		s	<< m_poolName			<< '\0'
-			<< m_poolPassword		<< '\0'
-			<< m_targetBlockTime	<< '\0'
-			<< m_minDifficulty		<< '\0'
-			<< m_chainWindowSize	<< '\0'
-			<< m_unclePenalty		<< '\0';
+		s << m_networkType     << '\0'
+		  << m_poolName        << '\0'
+		  << m_poolPassword    << '\0'
+		  << m_targetBlockTime << '\0'
+		  << m_minDifficulty   << '\0'
+		  << m_chainWindowSize << '\0'
+		  << m_unclePenalty    << '\0';
 
 		randomx_init_cache(cache, consensus_str, s.m_pos);
 	}
diff --git a/src/side_chain.h b/src/side_chain.h
index d27d5ea..5453a41 100644
--- a/src/side_chain.h
+++ b/src/side_chain.h
@@ -41,7 +41,7 @@ struct MinerShare
 class SideChain
 {
 public:
-	explicit SideChain(p2pool* pool);
+	SideChain(p2pool* pool, NetworkType type);
 	~SideChain();
 
 	void fill_sidechain_data(PoolBlock& block, Wallet* w, const hash& txkeySec, std::vector<MinerShare>& shares);
@@ -62,11 +62,13 @@ public:
 	// Consensus ID can therefore be used as a password to create private P2Pools
 	const std::vector<uint8_t>& consensus_id() const { return m_consensusId; }
 	uint64_t chain_window_size() const { return m_chainWindowSize; }
+	NetworkType network_type() const { return m_networkType; }
 
 	static bool split_reward(uint64_t reward, const std::vector<MinerShare>& shares, std::vector<uint64_t>& rewards);
 
 private:
 	p2pool* m_pool;
+	NetworkType m_networkType;
 
 private:
 	bool get_shares(PoolBlock* tip, std::vector<MinerShare>& shares) const;
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 3998a90..41b61d3 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -123,7 +123,7 @@ Wallet& Wallet::operator=(const Wallet& w)
 
 bool Wallet::decode(const char* address)
 {
-	m_type = Type::Invalid;
+	m_type = NetworkType::Invalid;
 
 	if (!address || (strlen(address) != ADDRESS_LENGTH)) {
 		return false;
@@ -167,11 +167,11 @@ bool Wallet::decode(const char* address)
 
 	m_prefix = data[0];
 
-	if (m_prefix == valid_prefixes[0]) m_type = Type::Mainnet;
-	if (m_prefix == valid_prefixes[1]) m_type = Type::Testnet;
-	if (m_prefix == valid_prefixes[2]) m_type = Type::Stagenet;
+	if (m_prefix == valid_prefixes[0]) m_type = NetworkType::Mainnet;
+	if (m_prefix == valid_prefixes[1]) m_type = NetworkType::Testnet;
+	if (m_prefix == valid_prefixes[2]) m_type = NetworkType::Stagenet;
 
-	if (m_type == Type::Invalid) {
+	if (m_type == NetworkType::Invalid) {
 		return false;
 	}
 
@@ -183,7 +183,7 @@ bool Wallet::decode(const char* address)
 	keccak(data, sizeof(data) - sizeof(m_checksum), md);
 
 	if (memcmp(&m_checksum, md, sizeof(m_checksum)) != 0) {
-		m_type = Type::Invalid;
+		m_type = NetworkType::Invalid;
 	}
 
 	return valid();
@@ -198,7 +198,7 @@ void Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key)
 	m_viewPublicKey = view_pub_key;
 	m_checksum = 0;
 
-	m_type = Type::Mainnet;
+	m_type = NetworkType::Mainnet;
 
 	m_txkeySec = {};
 	m_outputIndex = std::numeric_limits<size_t>::max();
diff --git a/src/wallet.h b/src/wallet.h
index 9e9c1ad..18ad3af 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -24,21 +24,14 @@ namespace p2pool {
 class Wallet
 {
 public:
-	enum class Type {
-		Invalid,
-		Mainnet,
-		Testnet,
-		Stagenet,
-	};
-
 	explicit Wallet(const char* address);
 	~Wallet();
 
 	Wallet(const Wallet& w);
 	Wallet& operator=(const Wallet& w);
 
-	FORCEINLINE bool valid() const { return m_type != Type::Invalid; }
-	FORCEINLINE Type type() const { return m_type; }
+	FORCEINLINE bool valid() const { return m_type != NetworkType::Invalid; }
+	FORCEINLINE NetworkType type() const { return m_type; }
 
 	bool decode(const char* address);
 	void assign(const hash& spend_pub_key, const hash& view_pub_key);
@@ -56,7 +49,7 @@ private:
 	hash m_spendPublicKey;
 	hash m_viewPublicKey;
 	uint32_t m_checksum;
-	Type m_type;
+	NetworkType m_type;
 
 	mutable uv_mutex_t m_lock;
 	hash m_txkeySec;