netwatch.h raw
1 // Copyright (c) 2017-2021 The Limenka developers
2 // Copyright (c) 2011-2013 David Krauss (std::align substitute from c-plus)
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #ifndef LIMENKA_QT_NETWATCH_H
7 #define LIMENKA_QT_NETWATCH_H
8
9 #include <qt/limenkaunits.h>
10
11 #include <primitives/transaction.h>
12 #include <sync.h>
13 #include <validationinterface.h>
14
15 #include <QAbstractTableModel>
16 #include <QWidget>
17
18 QT_BEGIN_NAMESPACE
19 class QLineEdit;
20 class QTableView;
21 QT_END_NAMESPACE
22
23 class CBlock;
24 class CBlockIndex;
25 class ClientModel;
26 class LogEntry;
27 class NetworkStyle;
28 class PlatformStyle;
29
30 static constexpr int LONGEST_BASE58_ADDRESS{35};
31 static constexpr int LONGEST_BECH32_ADDRESS{62}; // NOTE: Up to 74 in theory, but 62 in practice
32
33 class LogEntry {
34 private:
35 /**
36 * m_data is a uint32_t (meta_t) with the top two bits used for:
37 * 1: CBlockIndex*
38 * 2: CTransactionRef
39 * 3: weak_ptr<const CTransaction>
40 * The subsequent 30 bits are the timestamp, assumed to be most recent to the current time.
41 * Following this, and any padding necessary for alignment, the object itself is stored
42 */
43 uint8_t *m_data{nullptr};
44
45 typedef uint32_t meta_t;
46 typedef std::weak_ptr<const CTransaction> CTransactionWeakref;
47 static constexpr uint32_t rel_ts_mask{0x3fffffff};
48 static constexpr uint64_t rel_ts_mask64{rel_ts_mask};
49
50 template<typename T> static size_t data_sizeof(size_t& offset) {
51 void *p = (void *)intptr_t(sizeof(meta_t));
52 size_t dummy_bufsize = sizeof(T) * 2;
53 p = align(std::alignment_of<T>::value, sizeof(T), p, dummy_bufsize);
54 assert(p);
55 offset = size_t(p);
56 return offset + sizeof(T);
57 }
58
59 // std::align (missing in at least GCC 4.9) substitute from c-plus
60 static inline void *align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t &space) {
61 auto pn = reinterpret_cast<std::uintptr_t>(ptr);
62 auto aligned = (pn + alignment - 1) & -alignment;
63 auto new_space = space - (aligned - pn);
64 if (new_space < size) {
65 return nullptr;
66 }
67 space = new_space;
68 return ptr = reinterpret_cast<void *>(aligned);
69 }
70
71 void init(const LogEntry&);
72 void init(int32_t relTimestamp, const CBlockIndex&);
73 void init(int32_t relTimestamp, const CTransactionWeakref&, bool weak);
74 void clear();
75
76 public:
77 enum class Type {
78 Block,
79 Transaction,
80 };
81 static QString LogEntryTypeAbbreviation(Type);
82
83 LogEntry(const LogEntry&);
84 LogEntry() = default;
85 explicit LogEntry(int32_t relTimestamp, const CBlockIndex&);
86 explicit LogEntry(int32_t relTimestamp, const CTransactionWeakref&, bool weak = true);
87 explicit LogEntry(int32_t relTimestamp, const CTransactionRef&, bool weak = false);
88 ~LogEntry();
89
90 LogEntry& operator=(const LogEntry& other);
91
92 explicit operator bool() const;
93 int32_t getRelTimestamp() const;
94 uint64_t getTimestamp(uint64_t now) const;
95 Type getType() const;
96
97 template <typename T> T* get() const {
98 void *p = m_data + sizeof(meta_t);
99 size_t dummy_bufsize = sizeof(T) * 2;
100 p = align(std::alignment_of<T>::value, sizeof(T), p, dummy_bufsize);
101 assert(p);
102 return (T*)p;
103 }
104
105 const CBlockIndex& getBlockIndex() const;
106 CTransactionRef getTransactionRef() const;
107 bool isWeak() const;
108 bool expired() const;
109 void makeWeak();
110 };
111
112 class NetWatchLogModel;
113
114 class NetWatchLogSearch {
115 public:
116 QString m_query;
117
118 bool m_check_type;
119 bool m_check_id;
120 bool m_check_addr;
121 bool m_check_value;
122
123 NetWatchLogSearch(const QString& query, LimenkaUnit display_unit);
124 bool match(const NetWatchLogModel& model, int row) const;
125 };
126
127 class NetWatchValidationInterface;
128
129 class NetWatchLogModel : public QAbstractTableModel
130 {
131 Q_OBJECT
132
133 private:
134 QWidget * const m_widget;
135 ClientModel *m_client_model{nullptr};
136
137 NetWatchValidationInterface *m_validation_interface{nullptr};
138
139 mutable RecursiveMutex cs;
140 std::vector<LogEntry> m_log GUARDED_BY(cs);
141 static constexpr size_t logsizelimit{0x400};
142 size_t m_logpos GUARDED_BY(cs) {0};
143 size_t m_logskip GUARDED_BY(cs) {0};
144
145 static constexpr size_t max_nonweak_txouts{0x200};
146 static constexpr size_t max_vout_per_tx{0x100};
147
148 NetWatchLogSearch *m_current_search GUARDED_BY(cs) {nullptr};
149
150 const LogEntry& getLogEntryRow(int row) const EXCLUSIVE_LOCKS_REQUIRED(cs);
151 LogEntry& getLogEntryRow(int row) EXCLUSIVE_LOCKS_REQUIRED(cs);
152 void log_append(const LogEntry&, size_t& rows_used) EXCLUSIVE_LOCKS_REQUIRED(cs);
153
154 public:
155 static constexpr int HeaderCount{5};
156 enum class Header {
157 Time,
158 Type,
159 Id,
160 Address,
161 Value,
162 };
163
164 explicit NetWatchLogModel(QWidget *parent);
165 ~NetWatchLogModel();
166
167 void setClientModel(ClientModel *model);
168 void OrphanedValidationInterface();
169
170 bool isLogRowContinuation(int row) const EXCLUSIVE_LOCKS_REQUIRED(cs);
171 const LogEntry& findLogEntry(int row, int& out_entry_row) const EXCLUSIVE_LOCKS_REQUIRED(cs);
172
173 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
174 int columnCount(const QModelIndex& parent) const override;
175 QVariant data(const CBlockIndex&, int txout_index, const Header) const;
176 QVariant data(const CTransactionRef&, int txout_index, const Header) const;
177 QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
178 QVariant headerData(int section, Qt::Orientation, int role = Qt::DisplayRole) const override;
179
180 void searchRows(const QString& query, QList<int>& results);
181 void searchDisable();
182
183 void LogAddEntry(const LogEntry& le, size_t vout_count);
184 void LogBlock(const CBlockIndex*, const std::shared_ptr<const CBlock>&);
185 void LogTransaction(const CTransactionRef&);
186
187 Q_SIGNALS:
188 void moreSearchResults(const QList<int>& rows);
189
190 public Q_SLOTS:
191 void updateDisplayUnit();
192 };
193
194 class NetWatchLogTestModel : public NetWatchLogModel
195 {
196 Q_OBJECT
197
198 public:
199 NetWatchLogTestModel() : NetWatchLogModel(nullptr) { }
200
201 int rowCount(const QModelIndex& parent) const override;
202 QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
203 };
204
205 class GuiNetWatch: public QWidget
206 {
207 Q_OBJECT
208
209 private:
210 bool m_adjust_scroll;
211
212 public:
213 GuiNetWatch(const PlatformStyle *, const NetworkStyle *, QWidget * parent = nullptr);
214
215 void setClientModel(ClientModel *model);
216
217 NetWatchLogModel *log_model;
218 bool m_dont_cancel_search{false};
219
220 QLineEdit *m_search_editor;
221 QTableView *m_log_view;
222
223 public Q_SLOTS:
224 void rowsRemoved(const QModelIndex& parent, int start, int end);
225 void aboutToInsert();
226 void maybeScrollToBottom();
227 void doSearch(const QString& query);
228 void moreSearchResults(const QList<int>& rows);
229 void maybeCancelSearch();
230 };
231
232 #endif // LIMENKA_QT_NETWATCH_H
233