Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
node/minisketchwrapper.cpp
node/peerman_args.cpp
node/psbt.cpp
node/quantum.cpp
node/timeoffsets.cpp
node/transaction.cpp
node/txdownloadman_impl.cpp
Expand Down
1 change: 1 addition & 0 deletions src/common/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ const std::vector<std::string> TEST_OPTIONS_DOC{
"addrman (use deterministic addrman)",
"reindex_after_failure_noninteractive_yes (When asked for a reindex after failure interactively, simulate as-if answered with 'yes')",
"bip94 (enforce BIP94 consensus rules)",
"fakenums (use a NUMS point with a known discrete log for the quantum tripwire, so a theft and proof can be simulated)",
};

bool HasTestOption(const ArgsManager& args, const std::string& test_option)
Expand Down
25 changes: 25 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <node/peerman_args.h>
#include <node/quantum.h>
#include <policy/feerate.h>
#include <policy/fees/block_policy_estimator.h>
#include <policy/fees/block_policy_estimator_args.h>
Expand Down Expand Up @@ -1461,6 +1462,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)

LogInfo("Using at most %i automatic connections (%i file descriptors available)", nMaxConnections, available_fds);

// The quantum tripwire normally verifies proofs against the real BIP-341
// NUMS point (unknown discrete log). On regtest, -test=fakenums switches to a
// NUMS point whose discrete log is known, so a theft -- and a verifying proof
// -- can be simulated end to end.
if (HasTestOption(args, "fakenums")) {
node::GetQuantumProofStore().UseFakeNumsPoint();
LogInfo("Quantum tripwire using fake NUMS point (known discrete log) for testing");
}
// Persist the tripwire's activation point here, so it survives a restart.
node::GetQuantumProofStore().SetActivationFilePath(args.GetDataDirNet() / "quantum_tripwire.dat");

// Warn about relative -datadir path.
if (args.IsArgSet("-datadir") && !args.GetPathArg("-datadir").is_absolute()) {
LogWarning("Relative datadir option '%s' specified, which will be interpreted relative to the "
Expand Down Expand Up @@ -1684,6 +1696,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
validation_signals.RegisterValidationInterface(fee_estimator);
}

// Watch connected blocks for a published quantum-tripwire proof, so the
// "quantum" deployment can activate instantly. Unregistered by
// UnregisterAllValidationInterfaces() at shutdown.
validation_signals.RegisterValidationInterface(&node::GetQuantumProofStore());

for (const std::string& socket_addr : args.GetArgs("-bind")) {
std::string host_out;
uint16_t port_out{0};
Expand Down Expand Up @@ -1913,6 +1930,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
ChainstateManager& chainman = *Assert(node.chainman);
auto& kernel_notifications{*Assert(node.notifications)};

// Restore the quantum tripwire's activation from its persisted file (if any).
// This must run after the headers are loaded from disk (done above, so the
// activation block can be looked up) but before the network is started and
// new headers are accepted (which could reorg the chain mid-check), to avoid
// a race. Blocks connected later, e.g. during reindex/IBD, are handled
// incrementally by the registered validation interface.
node::GetQuantumProofStore().LoadActivationFromFile(chainman);

assert(!node.peerman);
node.peerman = PeerManager::make(*node.connman, *node.addrman,
node.banman.get(), chainman,
Expand Down
32 changes: 32 additions & 0 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <node/blockstorage.h>
#include <node/connection_types.h>
#include <node/protocol_version.h>
#include <node/quantum.h>
#include <node/timeoffsets.h>
#include <node/txdownloadman.h>
#include <node/txorphanage.h>
Expand Down Expand Up @@ -539,6 +540,7 @@ class PeerManagerImpl final : public PeerManager
std::vector<PrivateBroadcast::TxBroadcastInfo> GetPrivateBroadcastInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
std::vector<CTransactionRef> AbortPrivateBroadcast(const uint256& id) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void RelayQuantumProof(std::span<const unsigned char> proof) override { FloodQuantumProof(proof, /*exclude_peer=*/std::nullopt); }
void InitiateTxBroadcastToAll(const Txid& txid, const Wtxid& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void InitiateTxBroadcastPrivate(const CTransactionRef& tx) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SetBestBlock(int height, std::chrono::seconds time) override
Expand Down Expand Up @@ -720,6 +722,16 @@ class PeerManagerImpl final : public PeerManager

/** Send a message to a peer */
void PushMessage(CNode& node, CSerializedNetMsg&& msg) const { m_connman.PushMessage(&node, std::move(msg)); }
//! Send a quantum-tripwire proof to every connected peer except `exclude_peer`.
void FloodQuantumProof(std::span<const unsigned char> proof, std::optional<NodeId> exclude_peer)
{
const std::vector<unsigned char> data(proof.begin(), proof.end());
m_connman.ForEachNode([&](CNode* pnode) {
if (!exclude_peer || pnode->GetId() != *exclude_peer) {
MakeAndPushMessage(*pnode, NetMsgType::QPROOF, data);
}
});
}
template <typename... Args>
void MakeAndPushMessage(CNode& node, std::string msg_type, Args&&... args) const
{
Expand Down Expand Up @@ -5101,6 +5113,26 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
return;
}

if (msg_type == NetMsgType::QPROOF) {
// Relayed "aRsm" ECDL-break (quantum tripwire) proof.
std::vector<unsigned char> proof;
vRecv >> proof;
if (proof.size() != node::QUANTUM_PROOF_SIZE) {
LogDebug(BCLog::NET, "qproof of invalid size %u from peer=%d\n", proof.size(), pfrom.GetId());
return;
}
// Verify and store. Add() returns false for an invalid (fake) or already
// known proof, in which case we neither store nor relay it: over p2p we
// only ever accept and forward the first valid proof.
if (!node::GetQuantumProofStore().Add(proof)) {
LogDebug(BCLog::NET, "ignored invalid or duplicate qproof from peer=%d\n", pfrom.GetId());
return;
}
LogDebug(BCLog::NET, "stored and relaying new qproof from peer=%d\n", pfrom.GetId());
FloodQuantumProof(proof, /*exclude_peer=*/pfrom.GetId());
return;
}

// Ignore unknown message types for extensibility
LogDebug(BCLog::NET, "Unknown message type \"%s\" from peer=%d", SanitizeString(msg_type), pfrom.GetId());
return;
Expand Down
4 changes: 4 additions & 0 deletions src/net_processing.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <vector>

Expand Down Expand Up @@ -153,6 +154,9 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
/** Send ping message to all peers */
virtual void SendPings() = 0;

/** Relay a verified "aRsm" ECDL-break (quantum tripwire) proof to all peers. */
virtual void RelayQuantumProof(std::span<const unsigned char> proof) = 0;

/** Set the height of the best block and its time (seconds since epoch). */
virtual void SetBestBlock(int height, std::chrono::seconds time) = 0;

Expand Down
29 changes: 29 additions & 0 deletions src/node/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <node/kernel_notifications.h>
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <node/quantum.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
Expand Down Expand Up @@ -183,6 +184,34 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
coinbaseTx.vout[0].nValue = block_reward;
coinbase_tx.block_reward_remaining = block_reward;

// Quantum tripwire: until the "quantum" deployment activates, publish the
// single stored "aRsm" ECDL-break proof as a required OP_RETURN output in the
// coinbase. The proof is QUANTUM_PROOF_SIZE bytes, carried in a standard
// null-data output with zero value (so block_reward_remaining is unaffected),
// and added as a required_output that IPC mining clients must include,
// alongside the witness commitment appended below.
//
// The proof cannot fit in the 80-byte block header, so the coinbase is the
// only place to carry it. Using a coinbase OP_RETURN as a signalling channel
// mirrors a draft for coinbase-based version-bit signalling:
// https://gnusha.org/pi/bitcoindev/D52CEDCC-C7EF-429E-802F-F28DC9241FF0@sprovoost.nl/
// In that thread Antoine Poinsot argued against using the coinbase for
// signalling -- chiefly that, unlike version bits in the header, it forces
// nodes to read full blocks to learn the activation state. We sidestep that
// by persisting the activation point (block hash + proof) to a small file
// (see node::QuantumProofStore), so determining activation after the one-off
// signal needs no block re-reads: a header lookup suffices, even on pruned
// nodes. We publish the proof in exactly one block: once that block connects,
// the deployment activates from the next block and we stop adding it
// (IsActiveAtHeight() below).
if (auto& quantum{GetQuantumProofStore()}; !quantum.IsActiveAtHeight(nHeight)) {
if (const auto proof{quantum.GetProof()}) {
CTxOut proof_out{0, CScript() << OP_RETURN << *proof};
coinbaseTx.vout.push_back(proof_out);
coinbase_tx.required_outputs.push_back(proof_out);
}
}

// Start the coinbase scriptSig with the block height as required by BIP34.
// Mining clients are expected to append extra data to this prefix, so
// increasing its length would reduce the space they can use and may break
Expand Down
Loading
Loading