70e4f2292e
Tuesdays idea of adding some code into the SyncSPVAction didn't feel right. A second look made clear that bloom filter updates make much more sense to go hand in hand with sending a mempool message. Especially since they depend on each other on the server side. To-rehash: the wallet may decide at any time that a new bloom filter is needed. It then uses the superclass (code in p2plib) PrivacySegment, to build that filter. As part of that we get a lock object which, when going out of scope, makes the peers that are subscribed to the privacySegment send out the filter. This separation of concerns means that the subclass wallet in the app doens't know about peers or messages, only its superclass PrivacySegment does. What we did in this change is make the PrivacySegment class decide to combine a bloom update with a mempool call. Typicall only once per connection. This means I can remove hacks in the SyncSPVAction which forced the sending of the mempool message separately.
177 lines
6.2 KiB
C++
177 lines
6.2 KiB
C++
/*
|
|
* This file is part of the Flowee project
|
|
* Copyright (C) 2020-2023 Tom Zander <tom@flowee.org>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef PRIVACYSEGMENT_H
|
|
#define PRIVACYSEGMENT_H
|
|
|
|
#include "BlockHeader.h"
|
|
|
|
#include <utils/PartialMerkleTree.h>
|
|
#include <utils/bloom.h>
|
|
|
|
#include <deque>
|
|
#include <mutex>
|
|
|
|
class KeyId;
|
|
class Tx;
|
|
class Message;
|
|
class DataListenerInterface;
|
|
class PrivacySegmentListener;
|
|
|
|
/**
|
|
* A wallet can split its funds into different privacy segments.
|
|
* The effect is that backing resources will be allocated for each
|
|
* segment and details will be cordoned off.
|
|
*
|
|
* A bloom filter, for instance, is known to allow combining of addresses
|
|
* with higher probablity than we initially thought.
|
|
* The simple solution to this is to not use the same bloom filter for
|
|
* addresses that should be separated.
|
|
*
|
|
* The privacy segment is intended to be assigned to a certain set of
|
|
* addresses in the wallet and the P2PNet library makes sure that we never
|
|
* mix the segments when talking to the individual peers on the Bitcoin network.
|
|
*/
|
|
class PrivacySegment
|
|
{
|
|
public:
|
|
explicit PrivacySegment(uint16_t id, DataListenerInterface *parent);
|
|
|
|
/// The priority of a segmment in the wider system.
|
|
/// This decides the order in which peers are assigned to privacy segments.
|
|
enum Priority {
|
|
First, ///< Highest priority
|
|
Normal,
|
|
Last,
|
|
OnlyManual ///< Never auto-connect, only when specifically asked.
|
|
};
|
|
|
|
uint16_t segmentId() const;
|
|
|
|
struct FilterLock {
|
|
FilterLock(FilterLock && other);
|
|
~FilterLock();
|
|
void setFirst(bool isFirst) { first = isFirst; }
|
|
private:
|
|
friend class PrivacySegment;
|
|
FilterLock(PrivacySegment *parent);
|
|
PrivacySegment *parent;
|
|
bool first = false;
|
|
};
|
|
|
|
/* clears the bloom filter, to allow adding addresses and outputs to it again.
|
|
* This returns a FilterLock that will keep the mutex locked for the duration
|
|
* of its scope.
|
|
*
|
|
* The safe way to update the filter is something like this:
|
|
* @code
|
|
* { // lock scope
|
|
* auto lock = segment->clearFilter(),
|
|
* segment->addToFilter(something);
|
|
* }
|
|
*
|
|
* Additionally, as the FilterLock destructor is called it will push out an update to any listeners.
|
|
*/
|
|
FilterLock clearFilter();
|
|
|
|
void addToFilter(const uint256 &prevHash, int outIndex);
|
|
|
|
/**
|
|
* @brief addToFilter allows you to get updates for a specific address.
|
|
* @param address The address to add.
|
|
* @param blockHeight the blockHeight the address was created at, first one we look at to get updates for data.
|
|
*/
|
|
void addToFilter(const std::string &address, int blockHeight);
|
|
|
|
/**
|
|
* Add public-key-hash directly instead of an address.
|
|
*/
|
|
void addKeyToFilter(const KeyId &address, int blockHeight);
|
|
|
|
Streaming::ConstBuffer writeFilter(Streaming::BufferPool &pool) const;
|
|
|
|
/**
|
|
* Return the blockheight when this segment was born.
|
|
* This privacy segment gets details in a bloom filter which allows
|
|
* it to use merkleblocks and follow the blockchain. One main ingredient here
|
|
* is the first block height of this segment as we know that nothing can be found
|
|
* before that date.
|
|
*
|
|
* The returned integer is the blockheight this segment was created at,
|
|
* in the case that we don't have this information we'll return -1.
|
|
*
|
|
* Cases where we don't have this info are that the block headers are not synched
|
|
* yet and we don't actually know the height. There may also simply be no keys
|
|
* loaded or created at all yet, for instance when the loading is user initiated.
|
|
*/
|
|
int firstBlock() const;
|
|
|
|
/// set the block a peer just synchronized (received and verified)
|
|
void blockSynched(int height);
|
|
/**
|
|
* This returns the last block that was synched.
|
|
*
|
|
* We return the height of the last synchronized block for this segment.
|
|
* Special value is:
|
|
* * -2 for not initialized wallets. This may be an empty wallet, never seen a block, or it is encrypted.
|
|
*/
|
|
int lastBlockSynched() const;
|
|
/// a backup peer doing a second sync has reached this height
|
|
int backupSyncHeight() const;
|
|
|
|
/**
|
|
* @brief newTransactions announces a list of transactions pushed to us from a peer.
|
|
* @param header the block header these transactions appeared in.
|
|
* @param blockHeight the blockheight we know the header under.
|
|
* @param blockTransactions The actual transactions.
|
|
*/
|
|
void newTransactions(const BlockHeader &header, int blockHeight, const std::deque<Tx> &blockTransactions);
|
|
/// A single transaction that matches our filters, forwarded to us as it hits a mempool.
|
|
void newTransaction(const Tx &tx);
|
|
|
|
int filterChangedHeight() const;
|
|
|
|
const CBloomFilter &bloomFilter() const;
|
|
|
|
void addListener(PrivacySegmentListener *listener);
|
|
void removeListener(PrivacySegmentListener *listener);
|
|
|
|
/// The priority of a segmment in the wider system.
|
|
/// This decides the order in which peers are assigned to privacy segments.
|
|
Priority priority() const;
|
|
/// Sets the sync priority
|
|
void setPriority(Priority priority);
|
|
|
|
bool enabled() const;
|
|
void setEnabled(bool newEnabled);
|
|
|
|
private:
|
|
const uint16_t m_segmentId = 0;
|
|
mutable std::recursive_mutex m_lock;
|
|
std::deque<PrivacySegmentListener*> m_listeners;
|
|
int m_firstBlock = -1; ///< first block we need to investigate
|
|
CBloomFilter m_bloom;
|
|
DataListenerInterface *m_parent;
|
|
int m_merkleBlockHeight = -1;
|
|
int m_filterChangedHeight = 0;
|
|
int m_softMerkleBlockHeight = -1;
|
|
Priority m_priority = Normal;
|
|
bool m_enabled = true;
|
|
};
|
|
|
|
#endif
|