diff --git a/.gitignore b/.gitignore index 169478a..e952b34 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ # Build folder build/ +build_write_enabled/ bin/ out/ _codeql_build_dir/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e03645..081bed8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -396,6 +396,19 @@ elseif(ZeroMQ_LIBRARY) target_compile_definitions(ailee_adapters PRIVATE AILEE_HAS_ZMQ=1) endif() +# ============================================================================ +# Bitcoin Write Safety Gate +# ============================================================================ + +option(AILEE_BITCOIN_WRITE_ENABLED "Enable Bitcoin write operations (sendrawtransaction, sign, etc.)" OFF) +if(NOT AILEE_BITCOIN_WRITE_ENABLED) + target_compile_definitions(ailee_node PRIVATE AILEE_BITCOIN_WRITE_DISABLED=1) + target_compile_definitions(ailee_adapters PRIVATE AILEE_BITCOIN_WRITE_DISABLED=1) + message(STATUS "Bitcoin write operations: DISABLED (shadow/read-only mode)") +else() + message(STATUS "Bitcoin write operations: ENABLED") +endif() + # ============================================================================ # Unit Tests (Optional) # ============================================================================ diff --git a/include/Global_Seven.h b/include/Global_Seven.h index 5ada733..ca89113 100644 --- a/include/Global_Seven.h +++ b/include/Global_Seven.h @@ -240,7 +240,7 @@ struct AdapterConfig { std::string network; // "mainnet", "testnet", "devnet" std::unordered_map extra; // per‑chain params bool enableTelemetry{true}; - bool readOnly{false}; // listen‑only + bool readOnly{true}; // listen‑only (default: shadow/read-only mode) FeePolicy feePolicy{}; SlippagePolicy slippagePolicy{}; double minOracleConfidence{0.7}; diff --git a/src/l1/BitcoinAdapter.cpp b/src/l1/BitcoinAdapter.cpp index 8c37ba9..9200143 100644 --- a/src/l1/BitcoinAdapter.cpp +++ b/src/l1/BitcoinAdapter.cpp @@ -305,6 +305,13 @@ class BitcoinTxBuilder { const std::vector& outputs, const std::unordered_map& opts ) { +#if defined(AILEE_BITCOIN_WRITE_DISABLED) + (void)rpc; (void)outputs; (void)opts; + throw std::runtime_error( + "Bitcoin write operations disabled at compile time " + "(AILEE_BITCOIN_WRITE_DISABLED). " + "Rebuild with -DAILEE_BITCOIN_WRITE_ENABLED=ON to enable."); +#else // Get UTXOs Json::Value utxosJson = rpc.call("listunspent", Json::Value(Json::arrayValue)); @@ -365,6 +372,7 @@ class BitcoinTxBuilder { } return signedTx["hex"].asString(); +#endif } }; @@ -515,6 +523,20 @@ class BTCInternal { bool broadcastRaw(const std::string& rawHex, std::string& outTxId, ErrorCallback onError) { +#if defined(AILEE_BITCOIN_WRITE_DISABLED) + (void)rawHex; (void)outTxId; + if (onError) { + onError(AdapterError{ + Severity::Error, + "Bitcoin write operations disabled at compile time " + "(AILEE_BITCOIN_WRITE_DISABLED). " + "Rebuild with -DAILEE_BITCOIN_WRITE_ENABLED=ON to enable.", + "Broadcast", + -12 + }); + } + return false; +#else try { // Check if already broadcast (idempotency) std::lock_guard lock(broadcastMutex_); @@ -550,6 +572,7 @@ class BTCInternal { } return false; } +#endif } std::optional fetchTx(const std::string& txid, ErrorCallback onError) { diff --git a/tests/AdapterRegistryTests.cpp b/tests/AdapterRegistryTests.cpp index 51adce0..aa9134a 100644 --- a/tests/AdapterRegistryTests.cpp +++ b/tests/AdapterRegistryTests.cpp @@ -99,3 +99,21 @@ TEST(AdapterRegistryTest, GetBlockHeightReturnsValue) { ASSERT_TRUE(h.has_value()); EXPECT_EQ(h.value(), 42ULL); } + +TEST(AdapterConfigTest, ReadOnlyDefaultIsTrue) { + AdapterConfig cfg; + EXPECT_TRUE(cfg.readOnly); +} + +#if defined(AILEE_BITCOIN_WRITE_DISABLED) +TEST(BitcoinShadowModeTest, WriteDisabledAtCompileTime) { + // When AILEE_BITCOIN_WRITE_DISABLED is set, the macro is active. + // This test documents that the compile-time gate is in effect. + EXPECT_TRUE(true); +} +#else +TEST(BitcoinShadowModeTest, WriteEnabledAtCompileTime) { + // AILEE_BITCOIN_WRITE_ENABLED=ON was set; write ops are compiled in. + EXPECT_TRUE(true); +} +#endif