// Copyright (c) 2009 Satoshi Nakamoto // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" // // Global state variables // //// later figure out how these are persisted map mapMyProducts; map mapProducts; CCriticalSection cs_mapProducts; bool AdvertInsert(const CProduct& product) { uint256 hash = product.GetHash(); bool fNew = false; bool fUpdated = false; CRITICAL_BLOCK(cs_mapProducts) { // Insert or find existing product pair::iterator, bool> item = mapProducts.insert(make_pair(hash, product)); CProduct* pproduct = &(*(item.first)).second; fNew = item.second; // Update if newer if (product.nSequence > pproduct->nSequence) { *pproduct = product; fUpdated = true; } } //if (fNew) // NotifyProductAdded(hash); //else if (fUpdated) // NotifyProductUpdated(hash); return (fNew || fUpdated); } void AdvertErase(const CProduct& product) { uint256 hash = product.GetHash(); CRITICAL_BLOCK(cs_mapProducts) mapProducts.erase(hash); //NotifyProductDeleted(hash); } template unsigned int Union(T& v1, T& v2) { // v1 = v1 union v2 // v1 and v2 must be sorted // returns the number of elements added to v1 ///// need to check that this is equivalent, then delete this comment //vector vUnion(v1.size() + v2.size()); //vUnion.erase(set_union(v1.begin(), v1.end(), // v2.begin(), v2.end(), // vUnion.begin()), // vUnion.end()); T vUnion; vUnion.reserve(v1.size() + v2.size()); set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(vUnion)); unsigned int nAdded = vUnion.size() - v1.size(); if (nAdded > 0) v1 = vUnion; return nAdded; } void CUser::AddAtom(unsigned short nAtom, bool fOrigin) { // Ignore duplicates if (binary_search(vAtomsIn.begin(), vAtomsIn.end(), nAtom) || find(vAtomsNew.begin(), vAtomsNew.end(), nAtom) != vAtomsNew.end()) return; //// instead of zero atom, should change to free atom that propagates, //// limited to lower than a certain value like 5 so conflicts quickly // The zero atom never propagates, // new atoms always propagate through the user that created them if (nAtom == 0 || fOrigin) { vector vTmp(1, nAtom); Union(vAtomsIn, vTmp); if (fOrigin) vAtomsOut.push_back(nAtom); return; } vAtomsNew.push_back(nAtom); if (vAtomsNew.size() >= nFlowthroughRate || vAtomsOut.empty()) { // Select atom to flow through to vAtomsOut vAtomsOut.push_back(vAtomsNew[GetRand(vAtomsNew.size())]); // Merge vAtomsNew into vAtomsIn sort(vAtomsNew.begin(), vAtomsNew.end()); Union(vAtomsIn, vAtomsNew); vAtomsNew.clear(); } } bool AddAtomsAndPropagate(uint256 hashUserStart, const vector& vAtoms, bool fOrigin) { CReviewDB reviewdb; map > pmapPropagate[2]; pmapPropagate[0][hashUserStart] = vAtoms; for (int side = 0; !pmapPropagate[side].empty(); side = 1 - side) { map >& mapFrom = pmapPropagate[side]; map >& mapTo = pmapPropagate[1 - side]; for (map >::iterator mi = mapFrom.begin(); mi != mapFrom.end(); ++mi) { const uint256& hashUser = (*mi).first; const vector& vReceived = (*mi).second; ///// this would be a lot easier on the database if it put the new atom at the beginning of the list, ///// so the change would be right next to the vector size. // Read user CUser user; reviewdb.ReadUser(hashUser, user); unsigned int nIn = user.vAtomsIn.size(); unsigned int nNew = user.vAtomsNew.size(); unsigned int nOut = user.vAtomsOut.size(); // Add atoms received foreach(unsigned short nAtom, vReceived) user.AddAtom(nAtom, fOrigin); fOrigin = false; // Don't bother writing to disk if no changes if (user.vAtomsIn.size() == nIn && user.vAtomsNew.size() == nNew) continue; // Propagate if (user.vAtomsOut.size() > nOut) foreach(const uint256& hash, user.vLinksOut) mapTo[hash].insert(mapTo[hash].end(), user.vAtomsOut.begin() + nOut, user.vAtomsOut.end()); // Write back if (!reviewdb.WriteUser(hashUser, user)) return false; } mapFrom.clear(); } return true; } bool CReview::AcceptReview() { // Timestamp nTime = GetTime(); // Check signature if (!CKey::Verify(vchPubKeyFrom, GetSigHash(), vchSig)) return false; CReviewDB reviewdb; // Add review text to recipient vector vReviews; reviewdb.ReadReviews(hashTo, vReviews); vReviews.push_back(*this); if (!reviewdb.WriteReviews(hashTo, vReviews)) return false; // Add link from sender CUser user; uint256 hashFrom = Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end()); reviewdb.ReadUser(hashFrom, user); user.vLinksOut.push_back(hashTo); if (!reviewdb.WriteUser(hashFrom, user)) return false; reviewdb.Close(); // Propagate atoms to recipient vector vZeroAtom(1, 0); if (!AddAtomsAndPropagate(hashTo, user.vAtomsOut.size() ? user.vAtomsOut : vZeroAtom, false)) return false; return true; } bool CProduct::CheckSignature() { return (CKey::Verify(vchPubKeyFrom, GetSigHash(), vchSig)); } bool CProduct::CheckProduct() { if (!CheckSignature()) return false; // Make sure it's a summary product if (!mapDetails.empty() || !vOrderForm.empty()) return false; // Look up seller's atom count CReviewDB reviewdb("r"); CUser user; reviewdb.ReadUser(GetUserHash(), user); nAtoms = user.GetAtomCount(); reviewdb.Close(); ////// delme, this is now done by AdvertInsert //// Store to memory //CRITICAL_BLOCK(cs_mapProducts) // mapProducts[GetHash()] = *this; return true; }