upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 18:17:06 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 22:48:27 +0000
commit11766bec0ea465da263f78a6f473ca792da59280 (patch)
treef07c7eecabdf6a0b5f2299447dcf957472e7ef58
parent88d30ade28d6836e64d0157da57896bdb320e8a2 (diff)
net: stop serving the 2009 marketplace to anonymous peers
Four inherited handlers took attacker-controlled input and wrote to disk with nothing authorising it. None of them had a caller left anywhere in the tree, so all four were reachable only from the outside. checkorder called GenerateNewKey() -- AddKey(), a wallet.dat write -- for any peer that asked, and handed back the pubkey. Its comment claims the cache keeps one key per ip, and on a 2009 network of routable addresses it did. Not here: inbound peers arrive through a rendezvous tunnel and are tagged by BtfMarkerAddr() over three random bytes, so addr.ip is fresh every connection and the cache never hits. Reconnect in a loop and the node mints a key per connection, none of them in any backup taken beforehand, which for a pre-BIP32 wallet is how coins go missing. The map holding them was static and never trimmed. submitorder AddToWallet()'d a CWalletTx from any peer that sent one. It had to be a valid transaction; it did not have to be ours. review checked a signature the sender made with its own key, which proves possession and authorises nothing, then appended to reviews.dat by reading the whole vector, pushing one entry and writing it all back -- quadratic disk on an unbounded map<string,string> off the socket -- then walked a link graph the same peer supplied and relayed the message to every other node. reply dispatched into CRequestTracker. Nothing ever called PushRequest, so the table was permanently empty and the handler was a parser and nothing else. Removed with the subsystem behind them: market.cpp, CProduct, CReview, CUser, CReviewDB, the advert publish templates and the subscribe channel machinery, none of which had an inbound handler either. MSG_REVIEW, MSG_PRODUCT and MSG_TABLE stay in the inv enum, unserved: CInv puts type on the wire as an integer and MSG_TX and MSG_BLOCK are shared with every other node. Unknown commands are already ignored for extensibility, so peers still running these lose nothing but an answer they never asked for. Verified by a clean build of bitflash-node. (cherry picked from commit ad9c393866c007a115f32d70a9017ce91066a7d1)
-rw-r--r--src/Makefile4
-rw-r--r--src/db.cpp16
-rw-r--r--src/db.h23
-rw-r--r--src/headers_core.h1
-rw-r--r--src/main.cpp115
-rw-r--r--src/makefile.mingw4
-rw-r--r--src/market.cpp264
-rw-r--r--src/market.h182
-rw-r--r--src/net.cpp109
-rw-r--r--src/net.h138
10 files changed, 69 insertions, 787 deletions
diff --git a/src/Makefile b/src/Makefile
index 3577dd6..171afe0 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -48,14 +48,14 @@ LIBS = -L$(SECP_LIB) -lsecp256k1 \
48# binary. Nothing crashed here, and nothing would say so if it had. 48# binary. Nothing crashed here, and nothing would say so if it had.
49HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \ 49HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \
50 key.h bignum.h script.h db.h base58.h net.h nostr.h \ 50 key.h bignum.h script.h db.h base58.h net.h nostr.h \
51 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h \ 51 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h \
52 selftest.h walletcmd.h compat.h sockcount.h font_roboto.h 52 selftest.h walletcmd.h compat.h sockcount.h font_roboto.h
53 53
54IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \ 54IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \
55 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o 55 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o
56 56
57NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \ 57NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \
58 obj/market.o obj/sha.o obj/nostr.o obj/randomx_pow.o \ 58 obj/sha.o obj/nostr.o obj/randomx_pow.o \
59 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \ 59 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \
60 obj/selftest.o obj/bip32.o obj/walletcmd.o 60 obj/selftest.o obj/bip32.o obj/walletcmd.o
61 61
diff --git a/src/db.cpp b/src/db.cpp
index c8642e9..6f9461b 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -598,20 +598,12 @@ bool CTxDB::LoadBlockIndex()
598 598
599 599
600// 600//
601// CReviewDB 601// CReviewDB is gone with the "review" handler. Its write path was
602// read-whole-vector / append-one / write-whole-vector, driven straight from an
603// unauthenticated peer, which is quadratic disk on an unbounded record.
604// Existing reviews.dat files are simply left alone and never opened.
602// 605//
603 606
604bool CReviewDB::ReadReviews(uint256 hash, vector<CReview>& vReviews)
605{
606 vReviews.size(); // msvc workaround, just need to do anything with vReviews
607 return Read(make_pair(string("reviews"), hash), vReviews);
608}
609
610bool CReviewDB::WriteReviews(uint256 hash, const vector<CReview>& vReviews)
611{
612 return Write(make_pair(string("reviews"), hash), vReviews);
613}
614
615 607
616 608
617 609
diff --git a/src/db.h b/src/db.h
index 4fb5cfd..37a252f 100644
--- a/src/db.h
+++ b/src/db.h
@@ -8,8 +8,6 @@ class CTxIndex;
8class CDiskBlockIndex; 8class CDiskBlockIndex;
9class CDiskTxPos; 9class CDiskTxPos;
10class COutPoint; 10class COutPoint;
11class CUser;
12class CReview;
13class CAddress; 11class CAddress;
14class CWalletTx; 12class CWalletTx;
15 13
@@ -314,27 +312,6 @@ public:
314 312
315 313
316 314
317class CReviewDB : public CDB
318{
319public:
320 CReviewDB(const char* pszMode="r+", bool fTxn=false) : CDB("reviews.dat", pszMode, fTxn) { }
321private:
322 CReviewDB(const CReviewDB&);
323 void operator=(const CReviewDB&);
324public:
325 bool ReadUser(uint256 hash, CUser& user)
326 {
327 return Read(make_pair(string("user"), hash), user);
328 }
329
330 bool WriteUser(uint256 hash, const CUser& user)
331 {
332 return Write(make_pair(string("user"), hash), user);
333 }
334
335 bool ReadReviews(uint256 hash, vector<CReview>& vReviews);
336 bool WriteReviews(uint256 hash, const vector<CReview>& vReviews);
337};
338 315
339 316
340 317
diff --git a/src/headers_core.h b/src/headers_core.h
index 6c0c602..6a60b52 100644
--- a/src/headers_core.h
+++ b/src/headers_core.h
@@ -66,7 +66,6 @@ inline typename std::common_type<T1,T2>::type max(const T1& a, const T2& b)
66#include "nostr.h" 66#include "nostr.h"
67#include "randomx_pow.h" 67#include "randomx_pow.h"
68#include "main.h" 68#include "main.h"
69#include "market.h"
70 69
71// GUI callback -- implemented in gui.cpp (or as no-op in headless builds) 70// GUI callback -- implemented in gui.cpp (or as no-op in headless builds)
72void MainFrameRepaint(); 71void MainFrameRepaint();
diff --git a/src/main.cpp b/src/main.cpp
index 62068d2..efff96c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2782,9 +2782,10 @@ bool AlreadyHave(CTxDB& txdb, const CInv& inv)
2782 { 2782 {
2783 case MSG_TX: return mapTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); 2783 case MSG_TX: return mapTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash);
2784 case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); 2784 case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash);
2785 case MSG_REVIEW: return true;
2786 case MSG_PRODUCT: return mapProducts.count(inv.hash);
2787 } 2785 }
2786 // MSG_REVIEW / MSG_PRODUCT / MSG_TABLE are the 2009 marketplace inventory
2787 // types. Nothing announces or serves them any more (see ProcessMessage),
2788 // so they fall through to the default and are never requested.
2788 // Don't know what it is, just say we already got one 2789 // Don't know what it is, just say we already got one
2789 return true; 2790 return true;
2790} 2791}
@@ -2942,7 +2943,6 @@ bool ProcessMessages(CNode* pfrom)
2942 2943
2943bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 2944bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
2944{ 2945{
2945 static map<unsigned int, vector<unsigned char> > mapReuseKey;
2946 if (LogAcceptsCategory("net")) { printf("received: %-12s (%d bytes) ", strCommand.c_str(), (int)vRecv.size()); for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++) printf("%02x ", vRecv[i] & 0xff); printf("\n"); } 2946 if (LogAcceptsCategory("net")) { printf("received: %-12s (%d bytes) ", strCommand.c_str(), (int)vRecv.size()); for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++) printf("%02x ", vRecv[i] & 0xff); printf("\n"); }
2947 if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0) 2947 if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0)
2948 { 2948 {
@@ -3181,22 +3181,18 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
3181 } 3181 }
3182 3182
3183 3183
3184 else if (strCommand == "review") 3184 // "review" used to be handled here. It took a CReview off the wire from an
3185 { 3185 // unauthenticated peer, checked a signature the sender had made with a key
3186 CDataStream vMsg(vRecv); 3186 // of its own choosing -- which proves possession of that key and authorises
3187 CReview review; 3187 // nothing -- and then appended the message to reviews.dat by reading the
3188 vRecv >> review; 3188 // whole vector for the target hash, pushing one entry, and writing all of
3189 3189 // it back. That is quadratic disk for a stream an attacker controls, on a
3190 CInv inv(MSG_REVIEW, review.GetHash()); 3190 // record whose mapValue is an unbounded map<string,string> read straight
3191 pfrom->AddInventoryKnown(inv); 3191 // from the socket, followed by AddAtomsAndPropagate walking a link graph
3192 3192 // the same peer supplied, and finally RelayMessage forwarding it to every
3193 if (review.AcceptReview()) 3193 // other node. Unbounded remote disk growth with network amplification and
3194 { 3194 // no authorisation anywhere in the path. The marketplace it belonged to has
3195 // Relay the original message as-is in case it's a higher version than we know how to parse 3195 // not existed since 2009; the handler is gone rather than bounded.
3196 RelayMessage(inv, vMsg);
3197 mapAlreadyAskedFor.erase(inv);
3198 }
3199 }
3200 3196
3201 3197
3202 else if (strCommand == "block") 3198 else if (strCommand == "block")
@@ -3229,65 +3225,28 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
3229 } 3225 }
3230 3226
3231 3227
3232 else if (strCommand == "checkorder") 3228 // "checkorder", "submitorder" and "reply" -- the 2009 buy-from-a-merchant
3233 { 3229 // exchange -- used to be handled here. All three are gone.
3234 uint256 hashReply; 3230 //
3235 CWalletTx order; 3231 // "checkorder" called GenerateNewKey() for any peer that asked, which is
3236 vRecv >> hashReply >> order; 3232 // AddKey() and a wallet.dat write, and handed the pubkey back. The comment
3237 3233 // said "keep giving the same key to the same ip until they use it", and on
3238 /// we have a chance to check the order here 3234 // a 2009 network of routable IPs that held. It does not hold here: every
3239 3235 // inbound peer arrives through a rendezvous tunnel and is tagged with
3240 // Keep giving the same key to the same ip until they use it 3236 // BtfMarkerAddr() over three random bytes (net.cpp), so addr.ip is freshly
3241 if (!mapReuseKey.count(pfrom->addr.ip)) 3237 // random per connection and the cache never hits. Reconnect in a loop and
3242 mapReuseKey[pfrom->addr.ip] = GenerateNewKey(); 3238 // the node mints a key per connection -- unbounded wallet growth, unbounded
3243 3239 // growth of the static map that held them, and every one of those keys
3244 // Send back approval of order and pubkey to use 3240 // missing from every backup taken beforehand, which for this pre-BIP32
3245 CScript scriptPubKey; 3241 // wallet is how coins are lost.
3246 scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; 3242 //
3247 pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); 3243 // "submitorder" took a CWalletTx from an unauthenticated peer and, if it
3248 } 3244 // was merely a valid transaction, AddToWallet()'d it. Valid is not the same
3249 3245 // as ours: any peer could write chosen transactions into wallet.dat.
3250 3246 //
3251 else if (strCommand == "submitorder") 3247 // "reply" dispatched to a CRequestTracker callback. Nothing in this tree
3252 { 3248 // has ever called PushRequest, so mapRequests was permanently empty and the
3253 uint256 hashReply; 3249 // handler could only ever be a parser for hostile input.
3254 CWalletTx wtxNew;
3255 vRecv >> hashReply >> wtxNew;
3256
3257 // Broadcast
3258 if (!wtxNew.AcceptWalletTransaction())
3259 {
3260 pfrom->PushMessage("reply", hashReply, (int)1);
3261 return error("submitorder AcceptWalletTransaction() failed, returning error 1");
3262 }
3263 wtxNew.fTimeReceivedIsTxTime = true;
3264 AddToWallet(wtxNew);
3265 wtxNew.RelayWalletTransaction();
3266 mapReuseKey.erase(pfrom->addr.ip);
3267
3268 // Send back confirmation
3269 pfrom->PushMessage("reply", hashReply, (int)0);
3270 }
3271
3272
3273 else if (strCommand == "reply")
3274 {
3275 uint256 hashReply;
3276 vRecv >> hashReply;
3277
3278 CRequestTracker tracker;
3279 CRITICAL_BLOCK(pfrom->cs_mapRequests)
3280 {
3281 map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply);
3282 if (mi != pfrom->mapRequests.end())
3283 {
3284 tracker = (*mi).second;
3285 pfrom->mapRequests.erase(mi);
3286 }
3287 }
3288 if (!tracker.IsNull())
3289 tracker.fn(tracker.param1, vRecv);
3290 }
3291 3250
3292 3251
3293 else if (strCommand == "ping") 3252 else if (strCommand == "ping")
diff --git a/src/makefile.mingw b/src/makefile.mingw
index e076c49..72f8bbf 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -40,7 +40,7 @@ IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \
40 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o 40 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o
41 41
42NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \ 42NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \
43 obj/market.o obj/sha.o obj/nostr.o obj/randomx_pow.o \ 43 obj/sha.o obj/nostr.o obj/randomx_pow.o \
44 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \ 44 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \
45 obj/selftest.o obj/bip32.o obj/walletcmd.o 45 obj/selftest.o obj/bip32.o obj/walletcmd.o
46 46
@@ -55,7 +55,7 @@ GUI_OBJS = obj/gui.o obj/main_gui.o obj/bitflash_res.o
55# binary. Nothing crashed here, and nothing would say so if it had. 55# binary. Nothing crashed here, and nothing would say so if it had.
56HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \ 56HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \
57 key.h bignum.h script.h db.h base58.h net.h nostr.h \ 57 key.h bignum.h script.h db.h base58.h net.h nostr.h \
58 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h \ 58 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h \
59 selftest.h walletcmd.h sockcount.h compat.h font_roboto.h 59 selftest.h walletcmd.h sockcount.h compat.h font_roboto.h
60 60
61all: bitflash.exe 61all: bitflash.exe
diff --git a/src/market.cpp b/src/market.cpp
deleted file mode 100644
index e5d34f4..0000000
--- a/src/market.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
1// Copyright (c) 2009 Satoshi Nakamoto
2// Distributed under the MIT/X11 software license, see the accompanying
3// file license.txt or http://www.opensource.org/licenses/mit-license.php.
4
5#include "headers.h"
6
7
8
9
10
11
12
13
14
15
16//
17// Global state variables
18//
19
20//// later figure out how these are persisted
21map<uint256, CProduct> mapMyProducts;
22
23
24
25
26map<uint256, CProduct> mapProducts;
27CCriticalSection cs_mapProducts;
28
29bool AdvertInsert(const CProduct& product)
30{
31 uint256 hash = product.GetHash();
32 bool fNew = false;
33 bool fUpdated = false;
34
35 CRITICAL_BLOCK(cs_mapProducts)
36 {
37 // Insert or find existing product
38 pair<map<uint256, CProduct>::iterator, bool> item = mapProducts.insert(make_pair(hash, product));
39 CProduct* pproduct = &(*(item.first)).second;
40 fNew = item.second;
41
42 // Update if newer
43 if (product.nSequence > pproduct->nSequence)
44 {
45 *pproduct = product;
46 fUpdated = true;
47 }
48 }
49
50 //if (fNew)
51 // NotifyProductAdded(hash);
52 //else if (fUpdated)
53 // NotifyProductUpdated(hash);
54
55 return (fNew || fUpdated);
56}
57
58void AdvertErase(const CProduct& product)
59{
60 uint256 hash = product.GetHash();
61 CRITICAL_BLOCK(cs_mapProducts)
62 mapProducts.erase(hash);
63 //NotifyProductDeleted(hash);
64}
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84template<typename T>
85unsigned int Union(T& v1, T& v2)
86{
87 // v1 = v1 union v2
88 // v1 and v2 must be sorted
89 // returns the number of elements added to v1
90
91 ///// need to check that this is equivalent, then delete this comment
92 //vector<unsigned short> vUnion(v1.size() + v2.size());
93 //vUnion.erase(set_union(v1.begin(), v1.end(),
94 // v2.begin(), v2.end(),
95 // vUnion.begin()),
96 // vUnion.end());
97
98 T vUnion;
99 vUnion.reserve(v1.size() + v2.size());
100 set_union(v1.begin(), v1.end(),
101 v2.begin(), v2.end(),
102 back_inserter(vUnion));
103 unsigned int nAdded = vUnion.size() - v1.size();
104 if (nAdded > 0)
105 v1 = vUnion;
106 return nAdded;
107}
108
109void CUser::AddAtom(unsigned short nAtom, bool fOrigin)
110{
111 // Ignore duplicates
112 if (binary_search(vAtomsIn.begin(), vAtomsIn.end(), nAtom) ||
113 find(vAtomsNew.begin(), vAtomsNew.end(), nAtom) != vAtomsNew.end())
114 return;
115
116 //// instead of zero atom, should change to free atom that propagates,
117 //// limited to lower than a certain value like 5 so conflicts quickly
118 // The zero atom never propagates,
119 // new atoms always propagate through the user that created them
120 if (nAtom == 0 || fOrigin)
121 {
122 vector<unsigned short> vTmp(1, nAtom);
123 Union(vAtomsIn, vTmp);
124 if (fOrigin)
125 vAtomsOut.push_back(nAtom);
126 return;
127 }
128
129 vAtomsNew.push_back(nAtom);
130
131 if (vAtomsNew.size() >= nFlowthroughRate || vAtomsOut.empty())
132 {
133 // Select atom to flow through to vAtomsOut
134 vAtomsOut.push_back(vAtomsNew[GetRand(vAtomsNew.size())]);
135
136 // Merge vAtomsNew into vAtomsIn
137 sort(vAtomsNew.begin(), vAtomsNew.end());
138 Union(vAtomsIn, vAtomsNew);
139 vAtomsNew.clear();
140 }
141}
142
143bool AddAtomsAndPropagate(uint256 hashUserStart, const vector<unsigned short>& vAtoms, bool fOrigin)
144{
145 CReviewDB reviewdb;
146 map<uint256, vector<unsigned short> > pmapPropagate[2];
147 pmapPropagate[0][hashUserStart] = vAtoms;
148
149 for (int side = 0; !pmapPropagate[side].empty(); side = 1 - side)
150 {
151 map<uint256, vector<unsigned short> >& mapFrom = pmapPropagate[side];
152 map<uint256, vector<unsigned short> >& mapTo = pmapPropagate[1 - side];
153
154 for (map<uint256, vector<unsigned short> >::iterator mi = mapFrom.begin(); mi != mapFrom.end(); ++mi)
155 {
156 const uint256& hashUser = (*mi).first;
157 const vector<unsigned short>& vReceived = (*mi).second;
158
159 ///// this would be a lot easier on the database if it put the new atom at the beginning of the list,
160 ///// so the change would be right next to the vector size.
161
162 // Read user
163 CUser user;
164 reviewdb.ReadUser(hashUser, user);
165 unsigned int nIn = user.vAtomsIn.size();
166 unsigned int nNew = user.vAtomsNew.size();
167 unsigned int nOut = user.vAtomsOut.size();
168
169 // Add atoms received
170 foreach(unsigned short nAtom, vReceived)
171 user.AddAtom(nAtom, fOrigin);
172 fOrigin = false;
173
174 // Don't bother writing to disk if no changes
175 if (user.vAtomsIn.size() == nIn && user.vAtomsNew.size() == nNew)
176 continue;
177
178 // Propagate
179 if (user.vAtomsOut.size() > nOut)
180 foreach(const uint256& hash, user.vLinksOut)
181 mapTo[hash].insert(mapTo[hash].end(), user.vAtomsOut.begin() + nOut, user.vAtomsOut.end());
182
183 // Write back
184 if (!reviewdb.WriteUser(hashUser, user))
185 return false;
186 }
187 mapFrom.clear();
188 }
189 return true;
190}
191
192
193
194
195
196
197bool CReview::AcceptReview()
198{
199 // Timestamp
200 nTime = GetTime();
201
202 // Check signature
203 if (!CKey::Verify(vchPubKeyFrom, GetSigHash(), vchSig))
204 return false;
205
206 CReviewDB reviewdb;
207
208 // Add review text to recipient
209 vector<CReview> vReviews;
210 reviewdb.ReadReviews(hashTo, vReviews);
211 vReviews.push_back(*this);
212 if (!reviewdb.WriteReviews(hashTo, vReviews))
213 return false;
214
215 // Add link from sender
216 CUser user;
217 uint256 hashFrom = Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end());
218 reviewdb.ReadUser(hashFrom, user);
219 user.vLinksOut.push_back(hashTo);
220 if (!reviewdb.WriteUser(hashFrom, user))
221 return false;
222
223 reviewdb.Close();
224
225 // Propagate atoms to recipient
226 vector<unsigned short> vZeroAtom(1, 0);
227 if (!AddAtomsAndPropagate(hashTo, user.vAtomsOut.size() ? user.vAtomsOut : vZeroAtom, false))
228 return false;
229
230 return true;
231}
232
233
234
235
236
237bool CProduct::CheckSignature()
238{
239 return (CKey::Verify(vchPubKeyFrom, GetSigHash(), vchSig));
240}
241
242bool CProduct::CheckProduct()
243{
244 if (!CheckSignature())
245 return false;
246
247 // Make sure it's a summary product
248 if (!mapDetails.empty() || !vOrderForm.empty())
249 return false;
250
251 // Look up seller's atom count
252 CReviewDB reviewdb("r");
253 CUser user;
254 reviewdb.ReadUser(GetUserHash(), user);
255 nAtoms = user.GetAtomCount();
256 reviewdb.Close();
257
258 ////// delme, this is now done by AdvertInsert
259 //// Store to memory
260 //CRITICAL_BLOCK(cs_mapProducts)
261 // mapProducts[GetHash()] = *this;
262
263 return true;
264}
diff --git a/src/market.h b/src/market.h
deleted file mode 100644
index b5c8b3b..0000000
--- a/src/market.h
+++ /dev/null
@@ -1,182 +0,0 @@
1// Copyright (c) 2009 Satoshi Nakamoto
2// Distributed under the MIT/X11 software license, see the accompanying
3// file license.txt or http://www.opensource.org/licenses/mit-license.php.
4
5class CUser;
6class CReview;
7class CProduct;
8
9static const unsigned int nFlowthroughRate = 2;
10
11
12
13
14bool AdvertInsert(const CProduct& product);
15void AdvertErase(const CProduct& product);
16bool AddAtomsAndPropagate(uint256 hashUserStart, const vector<unsigned short>& vAtoms, bool fOrigin);
17
18
19
20
21
22
23
24
25class CUser
26{
27public:
28 vector<unsigned short> vAtomsIn;
29 vector<unsigned short> vAtomsNew;
30 vector<unsigned short> vAtomsOut;
31 vector<uint256> vLinksOut;
32
33 CUser()
34 {
35 }
36
37 IMPLEMENT_SERIALIZE
38 (
39 if (!(nType & SER_GETHASH))
40 READWRITE(nVersion);
41 READWRITE(vAtomsIn);
42 READWRITE(vAtomsNew);
43 READWRITE(vAtomsOut);
44 READWRITE(vLinksOut);
45 )
46
47 void SetNull()
48 {
49 vAtomsIn.clear();
50 vAtomsNew.clear();
51 vAtomsOut.clear();
52 vLinksOut.clear();
53 }
54
55 uint256 GetHash() const { return SerializeHash(*this); }
56
57
58 int GetAtomCount() const
59 {
60 return (vAtomsIn.size() + vAtomsNew.size());
61 }
62
63 void AddAtom(unsigned short nAtom, bool fOrigin);
64};
65
66
67
68
69
70
71
72class CReview
73{
74public:
75 int nVersion;
76 uint256 hashTo;
77 map<string, string> mapValue;
78 vector<unsigned char> vchPubKeyFrom;
79 vector<unsigned char> vchSig;
80
81 // memory only
82 unsigned int nTime;
83 int nAtoms;
84
85
86 CReview()
87 {
88 nVersion = 1;
89 hashTo = 0;
90 nTime = 0;
91 nAtoms = 0;
92 }
93
94 IMPLEMENT_SERIALIZE
95 (
96 READWRITE(this->nVersion);
97 nVersion = this->nVersion;
98 if (!(nType & SER_DISK))
99 READWRITE(hashTo);
100 READWRITE(mapValue);
101 READWRITE(vchPubKeyFrom);
102 if (!(nType & SER_GETHASH))
103 READWRITE(vchSig);
104 )
105
106 uint256 GetHash() const { return SerializeHash(*this); }
107 uint256 GetSigHash() const { return SerializeHash(*this, SER_GETHASH|SER_SKIPSIG); }
108 uint256 GetUserHash() const { return Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end()); }
109
110
111 bool AcceptReview();
112};
113
114
115
116
117
118
119
120class CProduct
121{
122public:
123 int nVersion;
124 CAddress addr;
125 map<string, string> mapValue;
126 map<string, string> mapDetails;
127 vector<pair<string, string> > vOrderForm;
128 unsigned int nSequence;
129 vector<unsigned char> vchPubKeyFrom;
130 vector<unsigned char> vchSig;
131
132 // disk only
133 int nAtoms;
134
135 // memory only
136 set<unsigned int> setSources;
137
138 CProduct()
139 {
140 nVersion = 1;
141 nAtoms = 0;
142 nSequence = 0;
143 }
144
145 IMPLEMENT_SERIALIZE
146 (
147 READWRITE(this->nVersion);
148 nVersion = this->nVersion;
149 READWRITE(addr);
150 READWRITE(mapValue);
151 if (!(nType & SER_GETHASH))
152 {
153 READWRITE(mapDetails);
154 READWRITE(vOrderForm);
155 READWRITE(nSequence);
156 }
157 READWRITE(vchPubKeyFrom);
158 if (!(nType & SER_GETHASH))
159 READWRITE(vchSig);
160 if (nType & SER_DISK)
161 READWRITE(nAtoms);
162 )
163
164 uint256 GetHash() const { return SerializeHash(*this); }
165 uint256 GetSigHash() const { return SerializeHash(*this, SER_GETHASH|SER_SKIPSIG); }
166 uint256 GetUserHash() const { return Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end()); }
167
168
169 bool CheckSignature();
170 bool CheckProduct();
171};
172
173
174
175
176
177
178
179
180extern map<uint256, CProduct> mapProducts;
181extern CCriticalSection cs_mapProducts;
182extern map<uint256, CProduct> mapMyProducts;
diff --git a/src/net.cpp b/src/net.cpp
index 72a7fda..e08f6f7 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -444,28 +444,9 @@ bool GetMyExternalIP(unsigned int& ipRet)
444 444
445 445
446 446
447void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) 447// AbandonRequests() lived here, sweeping CNode::mapRequests for a callback a
448{ 448// closing dialog had registered. Nothing ever registered one and nothing ever
449 // If the dialog might get closed before the reply comes back, 449// called this, so it went with CRequestTracker.
450 // call this in the destructor so it doesn't get called after it's deleted.
451 CRITICAL_BLOCK(cs_vNodes)
452 {
453 foreach(CNode* pnode, vNodes)
454 {
455 CRITICAL_BLOCK(pnode->cs_mapRequests)
456 {
457 for (map<uint256, CRequestTracker>::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();)
458 {
459 CRequestTracker& tracker = (*mi).second;
460 if (tracker.fn == fn && tracker.param1 == param1)
461 pnode->mapRequests.erase(mi++);
462 else
463 mi++;
464 }
465 }
466 }
467 }
468}
469 450
470 451
471 452
@@ -473,25 +454,10 @@ void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1)
473 454
474 455
475 456
476// 457// The broadcast/subscribe channel system (AnySubscribed, CNode::IsSubscribed,
477// Subscription methods for the broadcast and subscription system. 458// Subscribe, CancelSubscribe) stood here. Its channels were MSG_TABLE and
478// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. 459// MSG_PRODUCT, there was never an inbound handler for "subscribe",
479// 460// "sub-cancel", "publish" or "pub-cancel", and the objects it carried are gone.
480// The subscription system uses a meet-in-the-middle strategy.
481// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers
482// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through.
483//
484
485bool AnySubscribed(unsigned int nChannel)
486{
487 if (pnodeLocalHost->IsSubscribed(nChannel))
488 return true;
489 CRITICAL_BLOCK(cs_vNodes)
490 foreach(CNode* pnode, vNodes)
491 if (pnode->IsSubscribed(nChannel))
492 return true;
493 return false;
494}
495 461
496void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) 462void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd)
497{ 463{
@@ -504,55 +470,6 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd)
504 PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); 470 PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd);
505} 471}
506 472
507bool CNode::IsSubscribed(unsigned int nChannel)
508{
509 if (nChannel >= vfSubscribe.size())
510 return false;
511 return vfSubscribe[nChannel];
512}
513
514void CNode::Subscribe(unsigned int nChannel, unsigned int nHops)
515{
516 if (nChannel >= vfSubscribe.size())
517 return;
518
519 if (!AnySubscribed(nChannel))
520 {
521 // Relay subscribe
522 CRITICAL_BLOCK(cs_vNodes)
523 foreach(CNode* pnode, vNodes)
524 if (pnode != this)
525 pnode->PushMessage("subscribe", nChannel, nHops);
526 }
527
528 vfSubscribe[nChannel] = true;
529}
530
531void CNode::CancelSubscribe(unsigned int nChannel)
532{
533 if (nChannel >= vfSubscribe.size())
534 return;
535
536 // Prevent from relaying cancel if wasn't subscribed
537 if (!vfSubscribe[nChannel])
538 return;
539 vfSubscribe[nChannel] = false;
540
541 if (!AnySubscribed(nChannel))
542 {
543 // Relay subscription cancel
544 CRITICAL_BLOCK(cs_vNodes)
545 foreach(CNode* pnode, vNodes)
546 if (pnode != this)
547 pnode->PushMessage("sub-cancel", nChannel);
548
549 // Clear memory, no longer subscribed
550 if (nChannel == MSG_PRODUCT)
551 CRITICAL_BLOCK(cs_mapProducts)
552 mapProducts.clear();
553 }
554}
555
556 473
557 474
558 475
@@ -1215,17 +1132,6 @@ void CNode::Disconnect()
1215 BtfCloseSocket(hSocket); 1132 BtfCloseSocket(hSocket);
1216 hSocket = INVALID_SOCKET; 1133 hSocket = INVALID_SOCKET;
1217 1134
1218 // All of a nodes broadcasts and subscriptions are automatically torn down
1219 // when it goes down, so a node has to stay up to keep its broadcast going.
1220
1221 CRITICAL_BLOCK(cs_mapProducts)
1222 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end();)
1223 AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second);
1224
1225 // Cancel subscriptions
1226 for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++)
1227 if (vfSubscribe[nChannel])
1228 CancelSubscribe(nChannel);
1229} 1135}
1230 1136
1231 1137
@@ -1350,7 +1256,6 @@ void ThreadSocketHandler2(void* parg)
1350 bool fDelete = false; 1256 bool fDelete = false;
1351 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1257 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1352 TRY_CRITICAL_BLOCK(pnode->cs_vRecv) 1258 TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
1353 TRY_CRITICAL_BLOCK(pnode->cs_mapRequests)
1354 TRY_CRITICAL_BLOCK(pnode->cs_inventory) 1259 TRY_CRITICAL_BLOCK(pnode->cs_inventory)
1355 fDelete = true; 1260 fDelete = true;
1356 if (fDelete) 1261 if (fDelete)
diff --git a/src/net.h b/src/net.h
index 0317676..8b0245a 100644
--- a/src/net.h
+++ b/src/net.h
@@ -5,7 +5,6 @@
5class CMessageHeader; 5class CMessageHeader;
6class CAddress; 6class CAddress;
7class CInv; 7class CInv;
8class CRequestTracker;
9class CNode; 8class CNode;
10// Defined in main.h, which is included after this file. Only ever used here as 9// Defined in main.h, which is included after this file. Only ever used here as
11// a pointer, so the forward declaration is enough. 10// a pointer, so the forward declaration is enough.
@@ -14,7 +13,6 @@ class CBlockIndex;
14 13
15 14
16static const unsigned short DEFAULT_PORT = htons(8433); 15static const unsigned short DEFAULT_PORT = htons(8433);
17static const unsigned int PUBLISH_HOPS = 5;
18enum 16enum
19{ 17{
20 NODE_NETWORK = (1 << 0), 18 NODE_NETWORK = (1 << 0),
@@ -214,8 +212,6 @@ void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMee
214void BtfPexCollect(std::vector<std::string>& vDescOut); 212void BtfPexCollect(std::vector<std::string>& vDescOut);
215// Verify descriptors a peer sent and remember the good ones. Returns how many. 213// Verify descriptors a peer sent and remember the good ones. Returns how many.
216int BtfPexAccept(const std::vector<std::string>& vDesc); 214int BtfPexAccept(const std::vector<std::string>& vDesc);
217void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);
218bool AnySubscribed(unsigned int nChannel);
219void ThreadBitcoinMiner(void* parg); 215void ThreadBitcoinMiner(void* parg);
220// Starts MinerThreadCount() miners; returns how many came up. 216// Starts MinerThreadCount() miners; returns how many came up.
221int StartMinerThreads(); 217int StartMinerThreads();
@@ -489,13 +485,19 @@ public:
489 485
490 486
491 487
488// MSG_REVIEW, MSG_PRODUCT and MSG_TABLE no longer have a handler, a producer
489// or a store -- the marketplace they belonged to is gone. They stay in the
490// enum, and their names stay in ppszTypeName below, because CInv serialises
491// `type` as a plain integer: dropping them would renumber nothing today but
492// would make it tempting to renumber later, and MSG_TX and MSG_BLOCK are wire
493// values shared with every other node. Reserved, never emitted, never served.
492enum 494enum
493{ 495{
494 MSG_TX = 1, 496 MSG_TX = 1,
495 MSG_BLOCK, 497 MSG_BLOCK,
496 MSG_REVIEW, 498 MSG_REVIEW, // reserved, dead
497 MSG_PRODUCT, 499 MSG_PRODUCT, // reserved, dead
498 MSG_TABLE, 500 MSG_TABLE, // reserved, dead
499}; 501};
500 502
501static const char* ppszTypeName[] = 503static const char* ppszTypeName[] =
@@ -582,23 +584,9 @@ public:
582 584
583 585
584 586
585class CRequestTracker 587// CRequestTracker and the PushRequest/"reply" round trip lived here. Nothing
586{ 588// in this tree ever called PushRequest, so the table was always empty and the
587public: 589// "reply" handler was pure inbound parser. Both removed.
588 void (*fn)(void*, CDataStream&);
589 void* param1;
590
591 explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)
592 {
593 fn = fnIn;
594 param1 = param1In;
595 }
596
597 bool IsNull()
598 {
599 return fn == NULL;
600 }
601};
602 590
603 591
604 592
@@ -644,9 +632,6 @@ protected:
644public: 632public:
645 int64 nReleaseTime; 633 int64 nReleaseTime;
646 int64 nDisconnectSince; 634 int64 nDisconnectSince;
647 map<uint256, CRequestTracker> mapRequests;
648 CCriticalSection cs_mapRequests;
649
650 // inventory based relay 635 // inventory based relay
651 set<CInv> setInventoryKnown; 636 set<CInv> setInventoryKnown;
652 set<CInv> setInventoryKnown2; 637 set<CInv> setInventoryKnown2;
@@ -654,9 +639,6 @@ public:
654 CCriticalSection cs_inventory; 639 CCriticalSection cs_inventory;
655 multimap<int64, CInv> mapAskFor; 640 multimap<int64, CInv> mapAskFor;
656 641
657 // publish and subscription
658 vector<char> vfSubscribe;
659
660 // Last "btfpeers" we accepted from this node, to rate-limit the exchange. 642 // Last "btfpeers" we accepted from this node, to rate-limit the exchange.
661 int64 nLastPexRecv; 643 int64 nLastPexRecv;
662 644
@@ -723,7 +705,6 @@ public:
723 strBtfAddr.clear(); 705 strBtfAddr.clear();
724 strBtfMeeting.clear(); 706 strBtfMeeting.clear();
725 nStartingHeight = -1; 707 nStartingHeight = -1;
726 vfSubscribe.assign(256, false);
727 708
728 // Push a version message 709 // Push a version message
729 /// when NTP implemented, change to just nTime = GetAdjustedTime() 710 /// when NTP implemented, change to just nTime = GetAdjustedTime()
@@ -1002,49 +983,6 @@ public:
1002 } 983 }
1003 984
1004 985
1005 void PushRequest(const char* pszCommand,
1006 void (*fn)(void*, CDataStream&), void* param1)
1007 {
1008 uint256 hashReply;
1009 RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
1010
1011 CRITICAL_BLOCK(cs_mapRequests)
1012 mapRequests[hashReply] = CRequestTracker(fn, param1);
1013
1014 PushMessage(pszCommand, hashReply);
1015 }
1016
1017 template<typename T1>
1018 void PushRequest(const char* pszCommand, const T1& a1,
1019 void (*fn)(void*, CDataStream&), void* param1)
1020 {
1021 uint256 hashReply;
1022 RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
1023
1024 CRITICAL_BLOCK(cs_mapRequests)
1025 mapRequests[hashReply] = CRequestTracker(fn, param1);
1026
1027 PushMessage(pszCommand, hashReply, a1);
1028 }
1029
1030 template<typename T1, typename T2>
1031 void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,
1032 void (*fn)(void*, CDataStream&), void* param1)
1033 {
1034 uint256 hashReply;
1035 RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
1036
1037 CRITICAL_BLOCK(cs_mapRequests)
1038 mapRequests[hashReply] = CRequestTracker(fn, param1);
1039
1040 PushMessage(pszCommand, hashReply, a1, a2);
1041 }
1042
1043
1044
1045 bool IsSubscribed(unsigned int nChannel);
1046 void Subscribe(unsigned int nChannel, unsigned int nHops=0);
1047 void CancelSubscribe(unsigned int nChannel);
1048 void Disconnect(); 986 void Disconnect();
1049}; 987};
1050 988
@@ -1101,50 +1039,8 @@ inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)
1101 1039
1102 1040
1103 1041
1104// 1042// The publish/subscribe advert templates that used to close this file
1105// Templates for the publish and subscription system. 1043// (AdvertStartPublish / AdvertStopPublish / AdvertRemoveSource) are gone along
1106// The object being published as T& obj needs to have: 1044// with the marketplace objects they carried. Nothing ever handled an inbound
1107// a set<unsigned int> setSources member 1045// "publish" or "pub-cancel", so the machinery only ever pushed; what it pushed
1108// specializations of AdvertInsert and AdvertErase 1046// was CProduct, which no longer exists.
1109// Currently implemented for CTable and CProduct.
1110//
1111
1112template<typename T>
1113void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)
1114{
1115 // Add to sources
1116 obj.setSources.insert(pfrom->addr.ip);
1117
1118 if (!AdvertInsert(obj))
1119 return;
1120
1121 // Relay
1122 CRITICAL_BLOCK(cs_vNodes)
1123 foreach(CNode* pnode, vNodes)
1124 if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))
1125 pnode->PushMessage("publish", nChannel, nHops, obj);
1126}
1127
1128template<typename T>
1129void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)
1130{
1131 uint256 hash = obj.GetHash();
1132
1133 CRITICAL_BLOCK(cs_vNodes)
1134 foreach(CNode* pnode, vNodes)
1135 if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel)))
1136 pnode->PushMessage("pub-cancel", nChannel, nHops, hash);
1137
1138 AdvertErase(obj);
1139}
1140
1141template<typename T>
1142void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj)
1143{
1144 // Remove a source
1145 obj.setSources.erase(pfrom->addr.ip);
1146
1147 // If no longer supported by any sources, cancel it
1148 if (obj.setSources.empty())
1149 AdvertStopPublish(pfrom, nChannel, nHops, obj);
1150}