/* * This file is part of the Flowee project * Copyright (C) 2020-2024 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "PrivacySegment.h" #include "Peer.h" #include "DataListenerInterface.h" #include #include #include #include #include #include #include #include PrivacySegment::PrivacySegment(uint16_t id, DataListenerInterface *parent) : m_segmentId(id), m_bloom(10000, 0.01, GetRandInt(INT_MAX), BLOOM_UPDATE_ALL), m_parent(parent) { assert(m_segmentId > 0); // zero is not allowed, that is the 'unset' value elsewhere } uint16_t PrivacySegment::segmentId() const { return m_segmentId; } PrivacySegment::FilterLock PrivacySegment::clearFilter() { std::unique_lock mutexLock(m_lock); m_filterChangedHeight = m_merkleBlockHeight; FilterLock lock(this); m_bloom.clear(); if (m_filterShouldFollowWithMemoolCall) lock.addMempoolRequest(); return lock; } void PrivacySegment::addToFilter(const uint256 &prevHash, int outIndex) { assert(outIndex >= 0); std::unique_lock lock(m_lock); std::vector data; data.resize(36); memcpy(data.data(), prevHash.begin(), 32); WriteLE32(data.data() + 32, outIndex); m_bloom.insert(data); } void PrivacySegment::addToFilter(const std::string &address, int blockHeight) { std::unique_lock lock(m_lock); CashAddress::Content c = CashAddress::decodeCashAddrContent(address, "bitcoincash"); if (c.hash.empty()) { CBase58Data old; // legacy address encoding if (old.SetString(address)) { c.hash = old.data(); if (!old.isMainnetPkh() && !old.isMainnetSh()) { logCritical() << "PrivacySegment::addToFilter: Address could not be parsed"; return; } } } m_bloom.insert(c.hash); if (blockHeight > 0) { if (m_firstBlock == -1) m_firstBlock = blockHeight; else m_firstBlock = std::min(m_firstBlock, blockHeight); } m_filterChangedHeight = m_merkleBlockHeight; } void PrivacySegment::addKeyToFilter(const KeyId &address, int blockHeight) { std::unique_lock lock(m_lock); m_bloom.insert(std::vector(address.begin(), address.end())); if (blockHeight > 0) { if (m_firstBlock == -1) m_firstBlock = blockHeight; else m_firstBlock = std::min(m_firstBlock, blockHeight); } m_filterChangedHeight = m_merkleBlockHeight; } Streaming::ConstBuffer PrivacySegment::writeFilter(const std::shared_ptr &pool) const { std::unique_lock lock(m_lock); pool->reserve(m_bloom.GetSerializeSize(0, 0)); Streaming::P2PBuilder builder(pool); m_bloom.store(builder); return builder.buffer(); } int PrivacySegment::firstBlock() const { std::unique_lock lock(m_lock); return m_firstBlock; } void PrivacySegment::blockSynched(int height) { bool forward = false; { std::unique_lock lock(m_lock); if (height <= m_merkleBlockHeight) { m_softMerkleBlockHeight = height; } else { m_merkleBlockHeight = height; forward = true; } } // avoid deadlocks in wallet / privacysegment interaction and do this outside of our locks. if (forward) { assert(m_parent); m_parent->setLastSynchedBlockHeight(height); } else if (m_parent) { m_parent->updateBackupBlockHeight(); } } int PrivacySegment::lastBlockSynched() const { std::unique_lock lock(m_lock); if (m_merkleBlockHeight == -1) return m_firstBlock - 1; return m_merkleBlockHeight; } int PrivacySegment::backupSyncHeight() const { std::unique_lock lock(m_lock); if (m_softMerkleBlockHeight == -1) return m_firstBlock - 1; return m_softMerkleBlockHeight; } void PrivacySegment::newTransactions(const BlockHeader &header, int blockHeight, const std::deque &blockTransactions) { /* * Notice that the transactions match hit our filter, that doesn't mean it actually matched the * address or output that the wallet owns. * The wallet should thus test this and make sure that our filter is updated continuesly * with new outputs and replaced with a new filter when many outputs are already spent (which * we then want to push to peers to avoid them sending us some false-positives). */ m_parent->newTransactions(header.createHash(), blockHeight, blockTransactions); } void PrivacySegment::newTransaction(const Tx &tx) { m_parent->newTransaction(tx); } int PrivacySegment::filterChangedHeight() const { std::unique_lock lock(m_lock); return m_filterChangedHeight; } const CBloomFilter &PrivacySegment::bloomFilter() const { std::unique_lock lock(m_lock); return m_bloom; } void PrivacySegment::addPeer(const std::shared_ptr &peer) { assert(peer.get()); std::unique_lock lock(m_lock); for (auto iter = m_peers.begin(); iter != m_peers.end(); ++iter) { if (iter->lock() == peer) return; } m_peers.push_back(peer); } void PrivacySegment::removePeer(const std::shared_ptr &peer) { std::unique_lock lock(m_lock); for (auto iter = m_peers.begin(); iter != m_peers.end(); ++iter) { if (iter->lock() == peer) { m_peers.erase(iter); return; } } } PrivacySegment::Priority PrivacySegment::priority() const { return m_priority; } void PrivacySegment::setPriority(Priority priority) { m_priority = priority; } bool PrivacySegment::enabled() const { return m_enabled; } void PrivacySegment::setEnabled(bool newEnabled) { m_enabled = newEnabled; } void PrivacySegment::rebuildFilter(IsAtTip tip) { m_filterShouldFollowWithMemoolCall = tip == FilterAtTip; m_parent->rebuildFilter(); m_filterShouldFollowWithMemoolCall = false; } // /////////////////////////////////////////////////////////////////// PrivacySegment::FilterLock::FilterLock(PrivacySegment *parent) : parent(parent) { parent->m_lock.lock(); } PrivacySegment::FilterLock::FilterLock(PrivacySegment::FilterLock && other) : parent(other.parent) { } PrivacySegment::FilterLock::~FilterLock() { parent->m_lock.unlock(); for (const auto &wp : parent->m_peers) { auto peer = wp.lock(); if (peer.get()) peer->filterUpdated(shouldAddMempoolRequest); } } void PrivacySegment::FilterLock::addMempoolRequest() { shouldAddMempoolRequest = true; }