neobytes/market.cpp

265 lines
6.8 KiB
C++
Raw Normal View History

2009-08-30 05:46:39 +02:00
// 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<uint256, CProduct> mapMyProducts;
map<uint256, CProduct> 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<map<uint256, CProduct>::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<typename T>
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<unsigned short> 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<unsigned short> 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<unsigned short>& vAtoms, bool fOrigin)
{
CReviewDB reviewdb;
map<uint256, vector<unsigned short> > pmapPropagate[2];
pmapPropagate[0][hashUserStart] = vAtoms;
for (int side = 0; !pmapPropagate[side].empty(); side = 1 - side)
{
map<uint256, vector<unsigned short> >& mapFrom = pmapPropagate[side];
map<uint256, vector<unsigned short> >& mapTo = pmapPropagate[1 - side];
for (map<uint256, vector<unsigned short> >::iterator mi = mapFrom.begin(); mi != mapFrom.end(); ++mi)
{
const uint256& hashUser = (*mi).first;
const vector<unsigned short>& 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<CReview> 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<unsigned short> 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;
}