This commit is contained in:
jed
2011-10-20 13:34:49 -07:00
parent 6715b966fc
commit 650ee74391
27 changed files with 726 additions and 314 deletions

View File

@@ -36,7 +36,7 @@ public:
LedgerMaster& getLedgerMaster(){ return(mLedgerMaster); } LedgerMaster& getLedgerMaster(){ return(mLedgerMaster); }
UniqueNodeList& getUNL(){ return(mUNL); } UniqueNodeList& getUNL(){ return(mUNL); }
ValidationCollection& getValidationCollection(){ return(mValidations); } ValidationCollection& getValidationCollection(){ return(mValidations); }
Wallet& getWallet(){ return(mWallet); }
void run(); void run();

View File

@@ -15,8 +15,8 @@ Config::Config()
RPC_PORT=5001; RPC_PORT=5001;
NUMBER_CONNECTIONS=30; NUMBER_CONNECTIONS=30;
// a new ledger every 5 min // a new ledger every 30 min
LEDGER_SECONDS=(60*5); LEDGER_SECONDS=(60*30);
// length of delay between start finalization and sending your first proposal // length of delay between start finalization and sending your first proposal
// This delay allows us to collect a few extra transactions from people who's clock is different than ours // This delay allows us to collect a few extra transactions from people who's clock is different than ours
@@ -26,11 +26,14 @@ Config::Config()
// How long to wait between proposal send and ledger close. // How long to wait between proposal send and ledger close.
// at which point you publish your validation // at which point you publish your validation
// You are only waiting to get extra transactions from your peers // You are only waiting to get extra transactions from your peers
LEDGER_FINALIZATION_SECONDS=30; LEDGER_FINALIZATION_SECONDS=(60*5);
RPC_USER="admin"; RPC_USER="admin";
RPC_PASSWORD="pass"; RPC_PASSWORD="pass";
HISTORY_DIR="history/"; HISTORY_DIR="history/";
TRANSACTION_FEE=1000;
ACCOUNT_FEE=1000;
} }
void Config::load() void Config::load()

View File

@@ -7,6 +7,8 @@ public:
std::string VERSION_STR; std::string VERSION_STR;
bool TEST_NET; bool TEST_NET;
int TRANSACTION_FEE;
int ACCOUNT_FEE;
int PEER_PORT; int PEER_PORT;
int RPC_PORT; int RPC_PORT;
int NUMBER_CONNECTIONS; int NUMBER_CONNECTIONS;

View File

@@ -27,7 +27,7 @@ void ConnectionPool::connectToNetwork(KnownNodeList& nodeList,boost::asio::io_se
} }
} }
/*
bool ConnectionPool::isMessageKnown(PackedMessage::pointer msg) bool ConnectionPool::isMessageKnown(PackedMessage::pointer msg)
{ {
for(unsigned int n=0; n<mBroadcastMessages.size(); n++) for(unsigned int n=0; n<mBroadcastMessages.size(); n++)
@@ -36,9 +36,10 @@ bool ConnectionPool::isMessageKnown(PackedMessage::pointer msg)
} }
return(false); return(false);
} }
*/
void ConnectionPool::relayMessage(Peer* fromPeer,PackedMessage::pointer msg,uint64 ledgerID) void ConnectionPool::relayMessage(Peer* fromPeer,PackedMessage::pointer msg)
{ {
BOOST_FOREACH(Peer::pointer peer, mPeers) BOOST_FOREACH(Peer::pointer peer, mPeers)
{ {

View File

@@ -3,19 +3,20 @@
#include "types.h" #include "types.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
class KnownNodeList; class KnownNodeList;
/* /*
This is the list of all the Peers we are currently connected to This is the list of all the Peers we are currently connected to
*/ */
class ConnectionPool class ConnectionPool
{ {
std::vector<Peer::pointer> mPeers; std::vector<Peer::pointer> mPeers;
std::vector<std::pair<PackedMessage::pointer,int> > mBroadcastMessages; //std::vector<std::pair<PackedMessage::pointer,int> > mBroadcastMessages;
public: public:
ConnectionPool(); ConnectionPool();
void connectToNetwork(KnownNodeList& nodeList,boost::asio::io_service& io_service); void connectToNetwork(KnownNodeList& nodeList,boost::asio::io_service& io_service);
void relayMessage(Peer* fromPeer,PackedMessage::pointer msg,uint64 ledgerIndex); void relayMessage(Peer* fromPeer,PackedMessage::pointer msg);
bool isMessageKnown(PackedMessage::pointer msg); //bool isMessageKnown(PackedMessage::pointer msg);
}; };

View File

@@ -12,7 +12,7 @@ using namespace boost;
using namespace std; using namespace std;
Ledger::Ledger(uint64 index) Ledger::Ledger(uint32 index)
{ {
mIndex=index; mIndex=index;
mValidSig=false; mValidSig=false;
@@ -22,19 +22,20 @@ Ledger::Ledger(uint64 index)
// TODO: we should probably make a shared pointer type for each of these PB types // TODO: we should probably make a shared pointer type for each of these PB types
newcoin::FullLedger* Ledger::createFullLedger() newcoin::FullLedger* Ledger::createFullLedger()
{ {
// TODO: do we need to hash and create mone map first? // TODO: do we need to hash and create accounts map first?
newcoin::FullLedger* ledger=new newcoin::FullLedger(); newcoin::FullLedger* ledger=new newcoin::FullLedger();
ledger->set_index(mIndex); ledger->set_index(mIndex);
ledger->set_hash(mHash); ledger->set_hash(mHash);
pair<string,uint64>& account=pair<string,uint64>(); pair<uint160, pair<uint64,uint32> >& account=pair<uint160, pair<uint64,uint32> >();
BOOST_FOREACH(account,mMoneyMap) BOOST_FOREACH(account,mAccounts)
{ {
newcoin::Account* saveAccount=ledger->add_accounts(); newcoin::Account* saveAccount=ledger->add_accounts();
saveAccount->set_address(account.first); saveAccount->set_address(account.first.begin(),account.first.GetSerializeSize());
saveAccount->set_amount(account.second); saveAccount->set_amount(account.second.first);
saveAccount->set_seqnum(account.second.second);
} }
mBundle.addTransactionsToPB(ledger); //mBundle.addTransactionsToPB(ledger);
return(ledger); return(ledger);
} }
@@ -42,8 +43,9 @@ newcoin::FullLedger* Ledger::createFullLedger()
void Ledger::setTo(newcoin::FullLedger& ledger) void Ledger::setTo(newcoin::FullLedger& ledger)
{ {
mIndex=ledger.index(); mIndex=ledger.index();
mBundle.clear(); mTransactions.clear();
mMoneyMap.clear(); mDiscardedTransactions.clear();
mAccounts.clear();
mValidSig=false; mValidSig=false;
mValidHash=false; mValidHash=false;
@@ -51,14 +53,14 @@ void Ledger::setTo(newcoin::FullLedger& ledger)
for(int n=0; n<numAccounts; n++) for(int n=0; n<numAccounts; n++)
{ {
const newcoin::Account& account=ledger.accounts(n); const newcoin::Account& account=ledger.accounts(n);
mMoneyMap[account.address()] = account.amount(); mAccounts[ NewcoinAddress::protobufToInternal(account.address()) ] = pair<uint64,uint32>(account.amount(),account.seqnum());
} }
int numTrans=ledger.transactions_size(); int numTrans=ledger.transactions_size();
for(int n=0; n<numTrans; n++) for(int n=0; n<numTrans; n++)
{ {
const newcoin::Transaction& trans=ledger.transactions(n); const newcoin::Transaction& trans=ledger.transactions(n);
mBundle.addTransaction(trans); mTransactions.push_back(TransactionPtr(new newcoin::Transaction(trans)));
} }
} }
@@ -94,6 +96,24 @@ void Ledger::save(string dir)
delete(ledger); delete(ledger);
} }
int64 Ledger::getAmountHeld(uint160& address)
{
if(mAccounts.count(address))
{
return(mAccounts[address].first);
}
return(0);
}
Ledger::Account* Ledger::getAccount(uint160& address)
{
if(mAccounts.count(address))
{
return(&(mAccounts[address]));
}
return(NULL);
}
string& Ledger::getHash() string& Ledger::getHash()
{ {
if(!mValidHash) hash(); if(!mValidHash) hash();
@@ -109,7 +129,7 @@ string& Ledger::getSignature()
void Ledger::publish() void Ledger::publish()
{ {
PackedMessage::pointer packet=Peer::createValidation(shared_from_this()); PackedMessage::pointer packet=Peer::createValidation(shared_from_this());
theApp->getConnectionPool().relayMessage(NULL,packet,mIndex); theApp->getConnectionPool().relayMessage(NULL,packet);
} }
void Ledger::finalize() void Ledger::finalize()
@@ -121,8 +141,10 @@ void Ledger::sign()
{ {
// TODO: // TODO:
} }
void Ledger::calcMoneyMap() void Ledger::calcMoneyMap()
{ {
/*
// start with map from the previous ledger // start with map from the previous ledger
// go through every transaction // go through every transaction
Ledger::pointer parent=theApp->getLedgerMaster().getLedger(mIndex-1); Ledger::pointer parent=theApp->getLedgerMaster().getLedger(mIndex-1);
@@ -133,7 +155,7 @@ void Ledger::calcMoneyMap()
mBundle.updateMap(mMoneyMap); mBundle.updateMap(mMoneyMap);
// TODO: strip the 0 ones // TODO: strip the 0 ones
} } */
} }
void Ledger::hash() void Ledger::hash()
@@ -143,62 +165,265 @@ void Ledger::hash()
// TODO: // TODO:
} }
/*
uint64 Ledger::getAmount(std::string address) uint64 Ledger::getAmount(std::string address)
{ {
return(mMoneyMap[address]); return(mAccounts[NewcoinAddress:: address].first);
} }*/
// returns true if the transaction was valid // returns true if the from account has enough for the transaction and seq num is correct
bool Ledger::addTransaction(newcoin::Transaction& trans) bool Ledger::addTransaction(TransactionPtr trans,bool checkDuplicate)
{ {
if(mBundle.hasTransaction(trans)) return(false); if(checkDuplicate && hasTransaction(trans)) return(false);
Ledger::pointer parent=theApp->getLedgerMaster().getLedger(mIndex-1); if(mParent)
if(parent)
{ // check the lineage of the from addresses { // check the lineage of the from addresses
vector<uint64> cacheInputLeftOverAmount; uint160 address=NewcoinAddress::protobufToInternal(trans->from());
int numInputs=trans.inputs_size(); if(mAccounts.count(address))
cacheInputLeftOverAmount.resize(numInputs);
for(int n=0; n<numInputs; n++)
{ {
const newcoin::TransInput& input=trans.inputs(n); pair<uint64,uint32> account=mAccounts[address];
if( (account.first<trans->amount()) &&
(trans->seqnum()==account.second) )
{
account.first -= trans->amount();
account.second++;
mAccounts[address]=account;
uint64 amountHeld=parent->getAmount(input.from());
// TODO: checkValid could invalidate mValidSig and mValidHash
amountHeld = mBundle.checkValid(input.from(),amountHeld,0,trans.seconds());
if(amountHeld<input.amount())
{
mBundle.addDiscardedTransaction(trans);
cout << "Not enough money" << endl;
return(false);
}
cacheInputLeftOverAmount[n]=amountHeld-input.amount();
}
for(int n=0; n<numInputs; n++) uint160 destAddress=NewcoinAddress::protobufToInternal(trans->dest());
{
const newcoin::TransInput& input=trans.inputs(n); Account destAccount=mAccounts[destAddress];
mBundle.checkValid(input.from(),cacheInputLeftOverAmount[n],trans.seconds(),theConfig.LEDGER_SECONDS); destAccount.first += trans->amount();
} mAccounts[destAddress]=destAccount;
mValidSig=false; mValidSig=false;
mValidHash=false; mValidHash=false;
mBundle.addTransaction(trans); mTransactions.push_back(trans);
if(mChild)
{
mChild->parentAddedTransaction(trans);
}
return(true);
}else
{
mDiscardedTransactions.push_back(trans);
return false;
}
}else
{
mDiscardedTransactions.push_back(trans);
return false;
}
}else }else
{ // we have no way to know so just accept it { // we have no way to know so just hold on to it but don't add to the accounts
mValidSig=false; mValidSig=false;
mValidHash=false; mValidHash=false;
mBundle.addTransaction(trans); mDiscardedTransactions.push_back(trans);
return(true); return(true);
} }
}
// Don't check the amounts. We will do this at the end.
void Ledger::addTransactionRecalculate(TransactionPtr trans)
{
uint160 fromAddress=NewcoinAddress::protobufToInternal(trans->from());
if(mAccounts.count(fromAddress))
{
Account fromAccount=mAccounts[fromAddress];
if(trans->seqnum()==fromAccount.second)
{
fromAccount.first -= trans->amount();
fromAccount.second++;
mAccounts[fromAddress]=fromAccount;
uint160 destAddress=NewcoinAddress::protobufToInternal(trans->dest());
Account destAccount=mAccounts[destAddress];
destAccount.first += trans->amount();
mAccounts[destAddress]=destAccount;
mTransactions.push_back(trans);
}else
{ // invalid seqnum
mDiscardedTransactions.push_back(trans);
}
}else
{
if(trans->seqnum()==0)
{
mAccounts[fromAddress]=Account(-((int64)trans->amount()),1);
uint160 destAddress=NewcoinAddress::protobufToInternal(trans->dest());
Account destAccount=mAccounts[destAddress];
destAccount.first += trans->amount();
mAccounts[destAddress]=destAccount;
mTransactions.push_back(trans);
}else
{
mDiscardedTransactions.push_back(trans);
}
}
}
// Must look for transactions to discard to make this account positive
// When we chuck transactions it might cause other accounts to need correcting
void Ledger::correctAccount(uint160& address)
{
list<uint160> effected;
// do this in reverse so we take of the higher seqnum first
for( list<TransactionPtr>::reverse_iterator iter=mTransactions.rbegin(); iter != mTransactions.rend(); )
{
TransactionPtr trans= *iter;
if(NewcoinAddress::protobufToInternal(trans->from()) == address)
{
Account fromAccount=mAccounts[address];
assert(fromAccount.second==trans->seqnum()+1);
if(fromAccount.first<0)
{
fromAccount.first += trans->amount();
fromAccount.second --;
mAccounts[address]=fromAccount;
uint160 destAddress=NewcoinAddress::protobufToInternal(trans->dest());
Account destAccount=mAccounts[destAddress];
destAccount.first -= trans->amount();
mAccounts[destAddress]=destAccount;
if(destAccount.first<0) effected.push_back(destAddress);
list<TransactionPtr>::iterator temp=mTransactions.erase( --iter.base() );
if(fromAccount.first>=0) break;
iter=list<TransactionPtr>::reverse_iterator(temp);
}else break;
}else iter--;
}
BOOST_FOREACH(uint160& address,effected)
{
correctAccount(address);
}
}
// start from your parent and go through every transaction
// calls this on its child if recursive is set
void Ledger::recalculate(bool recursive)
{
if(mParent)
{
mValidSig=false;
mValidHash=false;
mAccounts.clear();
mAccounts=mParent->getAccounts();
list<TransactionPtr> firstTransactions=mTransactions;
list<TransactionPtr> secondTransactions=mDiscardedTransactions;
mTransactions.clear();
mDiscardedTransactions.clear();
firstTransactions.sort(gTransactionSorter);
secondTransactions.sort(gTransactionSorter);
// don't check balances until the end
BOOST_FOREACH(TransactionPtr trans,firstTransactions)
{
addTransactionRecalculate(trans);
}
BOOST_FOREACH(TransactionPtr trans,secondTransactions)
{
addTransactionRecalculate(trans);
}
pair<uint160, Account >& fullAccount=pair<uint160, Account >();
BOOST_FOREACH(fullAccount,mAccounts)
{
if(fullAccount.second.first <0 )
{
correctAccount(fullAccount.first);
}
}
if(mChild && recursive) mChild->recalculate();
}else
{
cout << "Can't recalculate if there is no parent" << endl;
}
}
void Ledger::parentAddedTransaction(TransactionPtr cause)
{
// TODO: we can make this more efficient at some point. For now just redo everything
recalculate();
/*
// IMPORTANT: these changes can't change the sequence number. This means we only need to check the dest account
// If there was a seqnum change we have to re-do all the transactions again
// There was a change to the balances of the parent ledger
// This could cause:
// an account to now be negative so we have to discard one
// a discarded transaction to be pulled back in
// seqnum invalidation
uint160 fromAddress=NewcoinAddress::protobufToInternal(cause->from());
uint160 destAddress=NewcoinAddress::protobufToInternal(cause->dest());
Account* fromAccount=getAccount(fromAddress);
Account* destAccount=getAccount(destAddress);
if(fromAccount)
{
if(fromAccount->first<cause->amount())
{
fromAccount->first -= cause->amount();
fromAccount->second = cause->seqnum()+1;
mAccounts[fromAddress] = *fromAccount;
}else cout << "This shouldn't happen2" << endl;
}else
{
cout << "This shouldn't happen" << endl;
}
if(destAccount)
{
destAccount->first += cause->amount();
mAccounts[destAddress]= *destAccount;
}else
{
mAccounts[destAddress]=pair<uint64,uint32>(cause->amount(),cause->seqnum());
}
// look for discarded transactions
BOOST_FOREACH(TransactionPtr trans,)
*/
}
bool Ledger::hasTransaction(TransactionPtr needle)
{
BOOST_FOREACH(TransactionPtr trans,mTransactions)
{
if( Transaction::isEqual(needle,trans) ) return(true);
}
BOOST_FOREACH(TransactionPtr disTrans,mDiscardedTransactions)
{
if( Transaction::isEqual(needle,disTrans) ) return(true);
}
return(false); return(false);
} }
void Ledger::recheck(Ledger::pointer parent,newcoin::Transaction& cause)
{
// TODO:
}

View File

@@ -1,53 +1,80 @@
#ifndef __LEDGER__ #ifndef __LEDGER__
#define __LEDGER__ #define __LEDGER__
#include "TransactionBundle.h" #include "Transaction.h"
#include "types.h" #include "types.h"
#include "BitcoinUtil.h"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp> #include <boost/enable_shared_from_this.hpp>
#include <map> #include <map>
#include <list>
class Ledger : public boost::enable_shared_from_this<Ledger> class Ledger : public boost::enable_shared_from_this<Ledger>
{ {
public:
typedef boost::shared_ptr<Ledger> pointer;
typedef std::pair<int64,uint32> Account;
private:
bool mValidSig; bool mValidSig;
bool mValidHash; bool mValidHash;
bool mFaith; //TODO: if you will bother to validate this ledger or not. You have to accept the first ledger on Faith bool mFaith; //TODO: if you will bother to validate this ledger or not. You have to accept the first ledger on Faith
uint64 mIndex; uint32 mIndex;
std::string mHash; std::string mHash;
std::string mSignature; std::string mSignature;
std::map<std::string,uint64> mMoneyMap;
TransactionBundle mBundle;
std::map<uint160, Account > mAccounts;
std::list<TransactionPtr> mTransactions;
std::list<TransactionPtr> mDiscardedTransactions;
//TransactionBundle mBundle;
// these can be NULL
// we need to keep track in case there are changes in this ledger that effect either the parent or child.
Ledger::pointer mParent;
Ledger::pointer mChild;
void calcMoneyMap(); void calcMoneyMap();
void sign(); void sign();
void hash(); void hash();
void addTransactionRecalculate(TransactionPtr trans);
void correctAccount(uint160& address);
public: public:
typedef boost::shared_ptr<Ledger> pointer; typedef boost::shared_ptr<Ledger> pointer;
Ledger(uint64 index); Ledger(uint32 index);
void setTo(newcoin::FullLedger& ledger); void setTo(newcoin::FullLedger& ledger);
void save(std::string dir); void save(std::string dir);
bool load(std::string dir); bool load(std::string dir);
void recalculate(bool recursive=true);
void publish(); void publish();
void finalize(); void finalize();
uint64 getAmount(std::string address); bool hasTransaction(TransactionPtr trans);
void recheck(Ledger::pointer parent,newcoin::Transaction& cause); int64 getAmountHeld(uint160& address);
bool addTransaction(newcoin::Transaction& trans); void parentAddedTransaction(TransactionPtr cause);
bool addTransaction(TransactionPtr trans,bool checkDuplicate=true);
void addValidation(newcoin::Validation& valid); void addValidation(newcoin::Validation& valid);
void addIgnoredValidation(newcoin::Validation& valid); void addIgnoredValidation(newcoin::Validation& valid);
uint64 getIndex(){ return(mIndex); } uint32 getIndex(){ return(mIndex); }
std::string& getHash(); std::string& getHash();
std::string& getSignature(); std::string& getSignature();
unsigned int getNumTransactions(){ return(mBundle.size()); } unsigned int getNumTransactions(){ return(mTransactions.size()); }
std::map<std::string,uint64>& getMoneyMap(){ return(mMoneyMap); } std::map<uint160, std::pair<int64,uint32> >& getAccounts(){ return(mAccounts); }
Account* getAccount(uint160& address);
newcoin::FullLedger* createFullLedger(); newcoin::FullLedger* createFullLedger();
}; };
#endif #endif

View File

@@ -11,12 +11,12 @@ void LedgerHistory::load()
} }
bool LedgerHistory::loadLedger(uint64 index) bool LedgerHistory::loadLedger(uint32 index)
{ {
Ledger::pointer ledger(new Ledger(index)); Ledger::pointer ledger(new Ledger(index));
if(ledger->load(theConfig.HISTORY_DIR)) if(ledger->load(theConfig.HISTORY_DIR))
{ {
mLedgers[index]=ledger; mAcceptedLedgers[index]=ledger;
} }
return(false); return(false);
} }
@@ -24,16 +24,16 @@ bool LedgerHistory::loadLedger(uint64 index)
// this will see if the ledger is in memory // this will see if the ledger is in memory
// if not it will check disk and load it // if not it will check disk and load it
// if not it will return NULL // if not it will return NULL
Ledger::pointer LedgerHistory::getLedger(uint64 index) Ledger::pointer LedgerHistory::getLedger(uint32 index)
{ {
if(mLedgers.count(index)) if(mAcceptedLedgers.count(index))
return(mLedgers[index]); return(mAcceptedLedgers[index]);
if(loadLedger(index)) return(mLedgers[index]); if(loadLedger(index)) return(mAcceptedLedgers[index]);
return(Ledger::pointer()); return(Ledger::pointer());
} }
void LedgerHistory::addLedger(Ledger::pointer ledger) void LedgerHistory::addLedger(Ledger::pointer ledger)
{ {
mLedgers[ledger->getIndex()]=ledger; mAcceptedLedgers[ledger->getIndex()]=ledger;
ledger->save(theConfig.HISTORY_DIR); ledger->save(theConfig.HISTORY_DIR);
} }

View File

@@ -4,18 +4,28 @@
#include "Ledger.h" #include "Ledger.h"
/* /*
This is all the history that you know of. This is a collection of all the ledgers you know about.
One thread of them is the thread you validate.
But you keep others that you have heard about.
Why do you need to keep them all?
In case people ask.
To make sure that there isn't a conflict between the validated ledgers
*/ */
class LedgerHistory class LedgerHistory
{ {
std::map<uint64, Ledger::pointer> mLedgers; std::map<uint32, Ledger::pointer> mAcceptedLedgers;
bool loadLedger(uint64 index); std::map<uint256, Ledger::pointer> mAllLedgers;
bool loadLedger(uint32 index);
public: public:
void load(); void load();
void addLedger(Ledger::pointer ledger); void addLedger(Ledger::pointer ledger);
Ledger::pointer getLedger(uint64 index); Ledger::pointer getLedger(uint32 index);
Ledger::pointer getLedger(uint256 hash);
}; };
#endif #endif

View File

@@ -1,11 +1,13 @@
#include "LedgerMaster.h" #include "LedgerMaster.h"
#include "Application.h" #include "Application.h"
#include "NewcoinAddress.h"
#include <boost/foreach.hpp>
using namespace std; using namespace std;
LedgerMaster::LedgerMaster() LedgerMaster::LedgerMaster()
{ {
mAfterProposed=false; //mAfterProposed=false;
} }
void LedgerMaster::load() void LedgerMaster::load()
@@ -18,69 +20,106 @@ void LedgerMaster::save()
} }
uint64 LedgerMaster::getCurrentLedgerIndex() uint32 LedgerMaster::getCurrentLedgerIndex()
{ {
return(mCurrentLedger->getIndex()); return(mCurrentLedger->getIndex());
} }
int LedgerMaster::getCurrentLedgerSeconds() int64 LedgerMaster::getAmountHeld(uint160& addr)
{ {
// TODO: return(mCurrentLedger->getAmountHeld(addr));
return(1);
} }
uint64 LedgerMaster::getAmountHeld(std::string& addr) int64 LedgerMaster::getAmountHeld(std::string& addr)
{ {
return(mCurrentLedger->getAmount(addr)); return(mCurrentLedger->getAmountHeld(NewcoinAddress::humanToInternal(addr)));
} }
Ledger::pointer LedgerMaster::getLedger(uint64 index) Ledger::pointer LedgerMaster::getLedger(uint32 index)
{ {
return(mLedgerHistory.getLedger(index)); return(mLedgerHistory.getLedger(index));
} }
// TODO: make sure the signature is valid bool LedgerMaster::isTransactionOnFutureList(TransactionPtr needle)
// TODO: make sure the transactionID is valid {
// TODO: make sure no from address = dest address BOOST_FOREACH(TransactionPtr straw,mFutureTransactions)
// TODO: make sure no 0 amounts {
// TODO: make sure no duplicate from addresses if(Transaction::isEqual(straw,needle))
bool LedgerMaster::isValidTransactionSig(newcoin::Transaction& trans)
{ {
return(true); return(true);
} }
}
return(false);
}
// returns true if we should broadcast it
bool LedgerMaster::addTransaction(newcoin::Transaction& trans)
{
if(! isValidTransactionSig(trans)) return(false);
if(trans.ledgerindex()==mFinalizingLedger->getIndex()) // make sure the signature is valid
// make sure from address != dest address
// make sure not 0 amount unless null dest (this is just a way to make sure your seqnum is incremented)
// make sure the sequence number is good (but the ones with a bad seqnum we need to save still?)
bool LedgerMaster::isValidTransaction(TransactionPtr trans)
{ {
if(mFinalizingLedger->addTransaction(trans)) if(trans->from()==trans->dest()) return(false);
if(trans->amount()==0) return(false);
if(!Transaction::isSigValid(trans)) return(false);
Ledger::Account* account=mCurrentLedger->getAccount( NewcoinAddress::protobufToInternal(trans->from()) );
if(!account) return(false);
if(trans->seqnum() != (account->second+1) ) return(false); // TODO: do we need to save these?
return(true);
}
// returns true if we should relay it
bool LedgerMaster::addTransaction(TransactionPtr trans)
{
if(mFinalizingLedger && (trans->ledgerindex()==mFinalizingLedger->getIndex()))
{
if(mFinalizingLedger->hasTransaction(trans)) return(false);
if(!isValidTransaction(trans)) return(false);
if(mFinalizingLedger->addTransaction(trans,false))
{ {
// TODO: we shouldn't really sendProposal right here // TODO: we shouldn't really sendProposal right here
// TODO: since maybe we are adding a whole bunch at once. we should send at the end of the batch // TODO: since maybe we are adding a whole bunch at once. we should send at the end of the batch
if(mAfterProposed) sendProposal(); // TODO: do we ever really need to re-propose?
mCurrentLedger->recheck(mFinalizingLedger,trans); //if(mAfterProposed) sendProposal();
return(true); return(true);
} }else return(false);
}else if(trans.ledgerindex()==mCurrentLedger->getIndex()) }else if(trans->ledgerindex()==mCurrentLedger->getIndex())
{ {
return( mCurrentLedger->addTransaction(trans) ); if(mCurrentLedger->hasTransaction(trans)) return(false);
}else if(trans.ledgerindex()>mCurrentLedger->getIndex()) if(!isValidTransaction(trans)) return(false);
return( mCurrentLedger->addTransaction(trans,false) );
}else if(trans->ledgerindex()>mCurrentLedger->getIndex())
{ // in the future { // in the future
// TODO: should we broadcast this?
// TODO: if NO then we might be slowing down transmission because our clock is off if(isTransactionOnFutureList(trans)) return(false);
// TODO: if YES then we could be contributing to not following the protocol if(!isValidTransaction(trans)) return(false);
// TODO: Probably just broadcast once we get to that ledger. mFutureTransactions.push_back(trans); // broadcast once we get to that ledger.
mFutureTransactions.push_back(trans); return(false);
}else }else
{ // transaction is too old. ditch it { // transaction is old but we don't have it. Add it to the current ledger
cout << "Old Transaction" << endl; cout << "Old Transaction" << endl;
// distant past
// This is odd make sure the transaction is valid before proceeding since checking all the past is expensive
if(! isValidTransaction(trans)) return(false);
uint32 checkIndex=trans->ledgerindex();
while(checkIndex <= mCurrentLedger->getIndex())
{
Ledger::pointer ledger=mLedgerHistory.getLedger(checkIndex);
if(ledger)
{
if(ledger->hasTransaction(trans)) return(false);
}
checkIndex++;
} }
return(false); return( mCurrentLedger->addTransaction(trans,false) );
}
} }
@@ -94,9 +133,9 @@ void LedgerMaster::gotFullLedger(newcoin::FullLedger& ledger)
void LedgerMaster::sendProposal() void LedgerMaster::sendProposal()
{ {
mAfterProposed=true; //mAfterProposed=true;
PackedMessage::pointer packet=Peer::createLedgerProposal(mFinalizingLedger); PackedMessage::pointer packet=Peer::createLedgerProposal(mFinalizingLedger);
theApp->getConnectionPool().relayMessage(NULL,packet,mFinalizingLedger->getIndex()); theApp->getConnectionPool().relayMessage(NULL,packet);
} }
void LedgerMaster::nextLedger() void LedgerMaster::nextLedger()
@@ -105,7 +144,7 @@ void LedgerMaster::nextLedger()
// finalize current ledger // finalize current ledger
// start a new ledger // start a new ledger
mAfterProposed=false; //mAfterProposed=false;
Ledger::pointer closedLedger=mFinalizingLedger; Ledger::pointer closedLedger=mFinalizingLedger;
mFinalizingLedger=mCurrentLedger; mFinalizingLedger=mCurrentLedger;
mCurrentLedger=Ledger::pointer(new Ledger(mCurrentLedger->getIndex()+1)); mCurrentLedger=Ledger::pointer(new Ledger(mCurrentLedger->getIndex()+1));
@@ -114,8 +153,8 @@ void LedgerMaster::nextLedger()
closedLedger->publish(); closedLedger->publish();
mLedgerHistory.addLedger(closedLedger); mLedgerHistory.addLedger(closedLedger);
applyFutureProposals(); applyFutureProposals( mFinalizingLedger->getIndex() );
applyFutureTransactions(); applyFutureTransactions( mCurrentLedger->getIndex() );
} }
void LedgerMaster::addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger& otherLedger) void LedgerMaster::addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger& otherLedger)
@@ -123,11 +162,11 @@ void LedgerMaster::addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger&
mFutureProposals.push_front(pair<Peer::pointer,newcoin::ProposeLedger>(peer,otherLedger)); mFutureProposals.push_front(pair<Peer::pointer,newcoin::ProposeLedger>(peer,otherLedger));
} }
void LedgerMaster::applyFutureProposals() void LedgerMaster::applyFutureProposals(uint32 ledgerIndex)
{ {
for(list< pair<Peer::pointer,newcoin::ProposeLedger> >::iterator iter=mFutureProposals.begin(); iter !=mFutureProposals.end(); ) for(list< pair<Peer::pointer,newcoin::ProposeLedger> >::iterator iter=mFutureProposals.begin(); iter !=mFutureProposals.end(); )
{ {
if( (*iter).second.ledgerindex() == mFinalizingLedger->getIndex()) if( (*iter).second.ledgerindex() == ledgerIndex)
{ {
checkLedgerProposal((*iter).first,(*iter).second); checkLedgerProposal((*iter).first,(*iter).second);
mFutureProposals.erase(iter); mFutureProposals.erase(iter);
@@ -135,11 +174,11 @@ void LedgerMaster::applyFutureProposals()
} }
} }
void LedgerMaster::applyFutureTransactions() void LedgerMaster::applyFutureTransactions(uint32 ledgerIndex)
{ {
for(list<newcoin::Transaction>::iterator iter=mFutureTransactions.begin(); iter !=mFutureTransactions.end(); ) for(list<TransactionPtr>::iterator iter=mFutureTransactions.begin(); iter !=mFutureTransactions.end(); )
{ {
if( (*iter).ledgerindex() == mCurrentLedger->getIndex() ) if( (*iter)->ledgerindex() == ledgerIndex)
{ {
addTransaction(*iter); addTransaction(*iter);
mFutureTransactions.erase(iter); mFutureTransactions.erase(iter);

View File

@@ -5,6 +5,7 @@
#include "LedgerHistory.h" #include "LedgerHistory.h"
#include "Peer.h" #include "Peer.h"
#include "types.h" #include "types.h"
#include "Transaction.h"
/* /*
Handles: Handles:
@@ -12,6 +13,9 @@ Handles:
finalizing the ledger finalizing the ledger
validating the past ledger validating the past ledger
keeping the ledger history keeping the ledger history
There is one ledger we are working on.
*/ */
class LedgerMaster class LedgerMaster
{ {
@@ -19,28 +23,31 @@ class LedgerMaster
Ledger::pointer mFinalizingLedger; Ledger::pointer mFinalizingLedger;
LedgerHistory mLedgerHistory; LedgerHistory mLedgerHistory;
std::list<newcoin::Transaction> mFutureTransactions; std::list<TransactionPtr> mFutureTransactions;
std::list< std::pair<Peer::pointer,newcoin::ProposeLedger> > mFutureProposals; std::list< std::pair<Peer::pointer,newcoin::ProposeLedger> > mFutureProposals;
bool mAfterProposed; //bool mAfterProposed;
void addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger& packet); void addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger& packet);
void applyFutureProposals(); void applyFutureProposals(uint32 ledgerIndex);
void applyFutureTransactions(); void applyFutureTransactions(uint32 ledgerIndex);
bool isValidTransactionSig(newcoin::Transaction& trans); bool isValidTransaction(TransactionPtr trans);
bool isTransactionOnFutureList(TransactionPtr trans);
public: public:
LedgerMaster(); LedgerMaster();
void load(); void load();
void save(); void save();
uint64 getCurrentLedgerIndex(); uint32 getCurrentLedgerIndex();
int getCurrentLedgerSeconds(); //int getCurrentLedgerSeconds();
Ledger::pointer getLedger(uint64 index); Ledger::pointer getLedger(uint32 index);
uint64 getAmountHeld(std::string& addr); int64 getAmountHeld(std::string& addr);
int64 getAmountHeld(uint160& addr);
Ledger::Account* getAccount(uint160& addr){ return(mCurrentLedger->getAccount(addr)); }
bool addTransaction(newcoin::Transaction& trans); bool addTransaction(TransactionPtr trans);
void gotFullLedger(newcoin::FullLedger& ledger); void gotFullLedger(newcoin::FullLedger& ledger);
void nextLedger(); void nextLedger();

View File

@@ -21,6 +21,10 @@ public:
uint160 GetHash160(); uint160 GetHash160();
static uint160 protobufToInternal(const std::string& buf);
static uint160 humanToInternal(const std::string& buf);
}; };
#endif #endif

View File

@@ -145,7 +145,7 @@ void Peer::sendFullLedger(Ledger::pointer ledger)
} }
} }
void Peer::sendGetFullLedger(uint64 index) void Peer::sendGetFullLedger(uint32 index)
{ {
newcoin::GetFullLedger* gfl=new newcoin::GetFullLedger(); newcoin::GetFullLedger* gfl=new newcoin::GetFullLedger();
gfl->set_ledgerindex(index); gfl->set_ledgerindex(index);
@@ -207,8 +207,8 @@ void Peer::processReadBuffer()
break; break;
case newcoin::TRANSACTION: case newcoin::TRANSACTION:
{ {
newcoin::Transaction trans; TransactionPtr trans(new newcoin::Transaction());
if(trans.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) if(trans->ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
receiveTransaction(trans); receiveTransaction(trans);
else cout << "parse error: " << type << endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error; else cout << "parse error: " << type << endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error;
@@ -289,25 +289,22 @@ void Peer::receiveGetValidations(newcoin::GetValidations& request)
} }
} }
void Peer::receiveTransaction(newcoin::Transaction& trans) void Peer::receiveTransaction(TransactionPtr trans)
{ {
ConnectionPool& pool=theApp->getConnectionPool(); // add to the correct transaction bundle and relay if we need to
PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(new newcoin::Transaction(trans)),newcoin::TRANSACTION)); if(theApp->getLedgerMaster().addTransaction(trans))
// check if this transaction is already known
if(pool.isMessageKnown(packet)) return;
// check if this transaction is valid
// add to the correct transaction bundle
if(!theApp->getLedgerMaster().addTransaction(trans))
{ {
cout << "Invalid transaction: " << trans.transid() << endl; // tell the wallet in case it was to us
return; theApp->getWallet().transactionAdded(trans);
}
// broadcast it to other Peers // broadcast it to other Peers
pool.relayMessage(this,packet,trans.ledgerindex()); ConnectionPool& pool=theApp->getConnectionPool();
PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(new newcoin::Transaction(*(trans.get()))),newcoin::TRANSACTION));
pool.relayMessage(this,packet);
}else
{
cout << "Invalid transaction: " << trans->from() << endl;
}
} }
void Peer::receiveProposeLedger(newcoin::ProposeLedger& packet) void Peer::receiveProposeLedger(newcoin::ProposeLedger& packet)

5
Peer.h
View File

@@ -7,6 +7,7 @@
#include "newcoin.pb.h" #include "newcoin.pb.h"
#include "PackedMessage.h" #include "PackedMessage.h"
#include "Ledger.h" #include "Ledger.h"
#include "Transaction.h"
#include "list" #include "list"
class KnownNode; class KnownNode;
@@ -48,7 +49,7 @@ class Peer : public boost::enable_shared_from_this<Peer>
void sendTransaction(); void sendTransaction();
void sendValidation(); void sendValidation();
void receiveHello(newcoin::Hello& packet); void receiveHello(newcoin::Hello& packet);
void receiveTransaction(newcoin::Transaction& packet); void receiveTransaction(TransactionPtr trans);
void receiveValidation(newcoin::Validation& packet); void receiveValidation(newcoin::Validation& packet);
void receiveFullLedger(newcoin::FullLedger& packet); void receiveFullLedger(newcoin::FullLedger& packet);
void receiveProposeLedger(newcoin::ProposeLedger& packet); void receiveProposeLedger(newcoin::ProposeLedger& packet);
@@ -78,7 +79,7 @@ public:
void sendPacket(PackedMessage::pointer packet); void sendPacket(PackedMessage::pointer packet);
void sendLedgerProposal(Ledger::pointer ledger); void sendLedgerProposal(Ledger::pointer ledger);
void sendFullLedger(Ledger::pointer ledger); void sendFullLedger(Ledger::pointer ledger);
void sendGetFullLedger(uint64 index); void sendGetFullLedger(uint32 index);
//static PackedMessage::pointer createFullLedger(Ledger::pointer ledger); //static PackedMessage::pointer createFullLedger(Ledger::pointer ledger);
static PackedMessage::pointer createLedgerProposal(Ledger::pointer ledger); static PackedMessage::pointer createLedgerProposal(Ledger::pointer ledger);

35
Transaction.cpp Normal file
View File

@@ -0,0 +1,35 @@
#include "Transaction.h"
#include "BitcoinUtil.h"
using namespace std;
// sort by from address and then seqnum
bool gTransactionSorter(const TransactionPtr& lhs, const TransactionPtr& rhs)
{
if(lhs->from() == rhs->from())
{
return(lhs->seqnum() < rhs->seqnum() );
}else return lhs->from() < rhs->from();
}
// I don't think we need to bother checking the sig or public key
bool Transaction::isEqual(TransactionPtr t1,TransactionPtr t2)
{
if(t1->amount() != t2->amount()) return(false);
if(t1->seqnum() != t2->seqnum()) return(false);
if(t1->ledgerindex() != t2->ledgerindex()) return(false);
if(t1->from() != t2->from()) return(false);
if(t1->dest() != t2->dest()) return(false);
return(true);
}
uint256 Transaction::calcHash(TransactionPtr trans)
{
vector<unsigned char> buffer;
buffer.resize(trans->ByteSize());
trans->SerializeToArray(&(buffer[0]),buffer.size());
return Hash(buffer.begin(), buffer.end());
}

View File

@@ -1,17 +1,25 @@
#include "string" #ifndef __TRANSACTION__
#define __TRANSACTION__
#include "newcoin.pb.h"
#include "uint256.h"
#include <boost/shared_ptr.hpp>
/* /*
A transaction can have only one input and one output. We could have made something that inherited from the protobuf transaction but this seemed simpler
If you want to send an amount that is greater than any single address of yours
you must first combine coins from one address to another.
*/ */
typedef boost::shared_ptr<newcoin::Transaction> TransactionPtr;
class Transaction class Transaction
{ {
std::string mSource;
std::string mSig;
std::string mDest;
unsigned int mAmount;
public: public:
Transaction(); static bool isSigValid(TransactionPtr trans);
static bool isEqual(TransactionPtr t1, TransactionPtr t2);
static uint256 calcHash(TransactionPtr trans);
}; };
extern bool gTransactionSorter(const TransactionPtr& lhs, const TransactionPtr& rhs);
#endif

View File

@@ -3,9 +3,17 @@
using namespace std; using namespace std;
/*
// do we need to make sure this preserves the order you received the transactions? Otherwise when we run through it we can see invalid transactions that get funded later.
// No we can just allow this. We just check when a transaction comes in and at ledger close.
// There needs to be a predictable order or the hashes will all be different.
//
bool gTransactionSorter(const newcoin::Transaction& lhs, const newcoin::Transaction& rhs) bool gTransactionSorter(const newcoin::Transaction& lhs, const newcoin::Transaction& rhs)
{ {
return lhs.seconds() < rhs.seconds(); if(lhs.from() == rhs.from())
{
return(lhs.seqnum() < rhs.seqnum() );
}else return lhs.from() < rhs.from();
} }
@@ -14,23 +22,17 @@ TransactionBundle::TransactionBundle()
} }
bool TransactionBundle::isEqual(newcoin::Transaction& t1,newcoin::Transaction& t2)
bool TransactionBundle::hasTransaction(TransactionPtr needle)
{ {
return(t1.transid()==t2.transid()); BOOST_FOREACH(TransactionPtr trans,mTransactions)
{
if( Transaction::isEqual(needle,trans) ) return(true);
} }
bool TransactionBundle::hasTransaction(newcoin::Transaction& t) BOOST_FOREACH(TransactionPtr trans,mDisacrdedTransactions)
{ {
BOOST_FOREACH(newcoin::Transaction& trans,mTransactions) if( Transaction::isEqual(needle,trans) ) return(true);
{
if( t.transid()==trans.transid())
return(true);
}
BOOST_FOREACH(newcoin::Transaction& trans,mDisacrdedTransactions)
{
if( t.transid()==trans.transid())
return(true);
} }
return(false); return(false);
@@ -38,35 +40,25 @@ bool TransactionBundle::hasTransaction(newcoin::Transaction& t)
void TransactionBundle::addTransactionsToPB(newcoin::FullLedger* ledger) void TransactionBundle::addTransactionsToPB(newcoin::FullLedger* ledger)
{ {
BOOST_FOREACH(newcoin::Transaction& trans,mTransactions) BOOST_FOREACH(TransactionPtr trans,mTransactions)
{ {
newcoin::Transaction* newTrans=ledger->add_transactions(); TransactionPtr newTrans=ledger->add_transactions();
newTrans->operator=(trans); newTrans->operator=(trans);
} }
} }
void TransactionBundle::addDiscardedTransaction(newcoin::Transaction& trans) void TransactionBundle::addDiscardedTransaction(TransactionPtr trans)
{ {
mDisacrdedTransactions.push_back(trans); mDisacrdedTransactions.push_back(trans);
mDisacrdedTransactions.sort(gTransactionSorter); //mDisacrdedTransactions.sort(gTransactionSorter);
} }
void TransactionBundle::addTransaction(const newcoin::Transaction& trans) void TransactionBundle::addTransaction(const TransactionPtr trans)
{ {
mTransactions.push_back(trans); mTransactions.push_back(trans);
mTransactions.sort(gTransactionSorter); //mTransactions.sort(gTransactionSorter);
} }
uint64 TransactionBundle::getTotalTransAmount(newcoin::Transaction& trans)
{
uint64 total=0;
int numInputs=trans.inputs_size();
for(int n=0; n<numInputs; n++)
{
total += trans.inputs(n).amount();
}
return(total);
}
// determine if all the transactions until end time from this address are valid // determine if all the transactions until end time from this address are valid
// return the amount left in this account // return the amount left in this account
@@ -137,3 +129,5 @@ void TransactionBundle::updateMap(std::map<std::string,uint64>& moneyMap)
moneyMap[trans.dest()] += total; moneyMap[trans.dest()] += total;
} }
} }
*/

View File

@@ -4,11 +4,14 @@
#include <list> #include <list>
#include "newcoin.pb.h" #include "newcoin.pb.h"
#include "types.h" #include "types.h"
#include "Transaction.h"
class TransactionBundle class TransactionBundle
{ {
std::list<newcoin::Transaction> mTransactions; // we have to order this before we hash so the hash will be consistent
std::list<newcoin::Transaction> mDisacrdedTransactions; std::list<TransactionPtr> mTransactions;
std::list<TransactionPtr> mDisacrdedTransactions;
//std::list<newcoin::Transaction> mAllTransactions; //std::list<newcoin::Transaction> mAllTransactions;
public: public:
TransactionBundle(); TransactionBundle();
@@ -19,7 +22,8 @@ public:
void addTransactionsToPB(newcoin::FullLedger* ledger); void addTransactionsToPB(newcoin::FullLedger* ledger);
bool hasTransaction(newcoin::Transaction& trans); bool hasTransaction(TransactionPtr trans);
// returns the amount of money this address holds at the end time // returns the amount of money this address holds at the end time
// it will discard any transactions till endTime that bring amount held under 0 // it will discard any transactions till endTime that bring amount held under 0
uint64 checkValid(std::string address, uint64 startAmount, uint64 checkValid(std::string address, uint64 startAmount,
@@ -29,13 +33,13 @@ public:
// will check if all transactions after this are valid // will check if all transactions after this are valid
//void checkTransactions(); //void checkTransactions();
void addTransaction(const newcoin::Transaction& trans); void addTransaction(TransactionPtr trans);
// transaction is valid and signed except the guy didn't have the money // transaction is valid and signed except the guy didn't have the money
void addDiscardedTransaction(newcoin::Transaction& trans); void addDiscardedTransaction(TransactionPtr trans);
static bool isEqual(newcoin::Transaction& t1,newcoin::Transaction& t2);
static uint64 getTotalTransAmount(newcoin::Transaction& trans); //static uint64 getTotalTransAmount(TransactionPtr trans);
}; };
#endif #endif

View File

@@ -1,6 +1,8 @@
#ifndef __VALIDATION_COLLECTION__
#define __VALIDATION_COLLECTION__
#include "newcoin.pb.h" #include "newcoin.pb.h"
#include "types.h" #include "uint256.h"
class ValidationCollection class ValidationCollection
{ {
@@ -15,3 +17,5 @@ public:
std::vector<newcoin::Validation>* getValidations(uint64 ledgerIndex); std::vector<newcoin::Validation>* getValidations(uint64 ledgerIndex);
}; };
#endif

View File

@@ -17,9 +17,9 @@ void Wallet::load()
} }
uint64 Wallet::getBalance() int64 Wallet::getBalance()
{ {
uint64 total = 0; int64 total = 0;
LedgerMaster& ledgerMaster=theApp->getLedgerMaster(); LedgerMaster& ledgerMaster=theApp->getLedgerMaster();
@@ -31,79 +31,99 @@ uint64 Wallet::getBalance()
return total; return total;
} }
string Wallet::sendMoneyToAddress(NewcoinAddress& destAddress, uint64 amount) void Wallet::refreshAccounts()
{ {
// Check amount LedgerMaster& ledgerMaster=theApp->getLedgerMaster();
if(amount > getBalance())
return("Insufficient funds");
newcoin::Transaction trans; BOOST_FOREACH(Account& account, mYourAccounts)
if(!createTransaction(destAddress, amount, trans))
{ {
return "Error: Transaction creation failed "; Ledger::Account* ledgerAccount=ledgerMaster.getAccount(account.mAddress);
if(ledgerAccount)
{
account.mAmount= ledgerAccount->first;
account.mSeqNum= ledgerAccount->second;
}else
{
account.mAmount=0;
account.mSeqNum=0;
}
}
} }
void Wallet::transactionAdded(TransactionPtr trans)
{ // TODO: optimize
refreshAccounts();
}
Wallet::Account* Wallet::consolidateAccountOfSize(int64 amount)
{
int64 total=0;
BOOST_FOREACH(Account& account, mYourAccounts)
{
if(account.mAmount>=amount) return(&account);
total += account.mAmount;
}
if(total<amount) return(NULL);
Account* firstAccount=NULL;
uint160* firstAddr=NULL;
total=0;
BOOST_FOREACH(Account& account, mYourAccounts)
{
total += account.mAmount;
if(firstAccount)
{
TransactionPtr trans=createTransaction(account,firstAccount->mAddress,account.mAmount);
commitTransaction(trans);
}else firstAccount=&account;
if(total>=amount) return(firstAccount);
}
assert(0);
return(NULL);
}
string Wallet::sendMoneyToAddress(uint160& destAddress, int64 amount)
{
// we may have to bundle up money in order to send this amount
Account* fromAccount=consolidateAccountOfSize(amount);
if(fromAccount)
{
TransactionPtr trans=createTransaction(*fromAccount,destAddress,amount);
if(trans)
{
if(!commitTransaction(trans)) if(!commitTransaction(trans))
return("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); return("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
}
}else return("Insufficient funds");
return ""; return "";
} }
TransactionPtr Wallet::createTransaction(Account& fromAccount, uint160& destAddr, int64 amount)
bool Wallet::createTransaction(NewcoinAddress& destAddress, uint64 amount,newcoin::Transaction& trans)
{ {
// find accounts to send from TransactionPtr trans(new newcoin::Transaction());
// sign each account that is sending trans->set_amount(amount);
trans->set_seqnum(fromAccount.mSeqNum);
trans->set_from(fromAccount.mAddress.begin(), fromAccount.mAddress.GetSerializeSize());
trans->set_dest(destAddr.begin(),destAddr.GetSerializeSize());
trans->set_ledgerindex(theApp->getLedgerMaster().getCurrentLedgerIndex());
// TODO: trans->set_pubkey(fromAccount.mPublicKey);
fromAccount.signTransaction(trans);
return(trans);
trans.set_ledgerindex(theApp->getLedgerMaster().getCurrentLedgerIndex());
trans.set_seconds(theApp->getLedgerMaster().getCurrentLedgerSeconds());
trans.set_dest(destAddress.ToString());
list<newcoin::TransInput*> inputs;
BOOST_FOREACH(Account& account, mYourAccounts)
{
newcoin::TransInput* input=trans.add_inputs();
inputs.push_back(input);
input->set_from(account.mAddress);
if(account.mAmount < amount)
{ // this account can only fill a portion of the amount
input->set_amount(account.mAmount);
amount -= account.mAmount;
}else
{ // this account can fill the whole thing
input->set_amount(amount);
break;
}
}
uint256 hash = calcTransactionHash(trans);
BOOST_FOREACH(newcoin::TransInput* input,inputs)
{
vector<unsigned char> sig;
if(signTransInput(hash,*input,sig))
input->set_sig(&(sig[0]),sig.size());
else return(false);
}
trans.set_transid(hash.ToString());
return(true);
}
uint256 Wallet::calcTransactionHash(newcoin::Transaction& trans)
{
vector<unsigned char> buffer;
buffer.resize(trans.ByteSize());
trans.SerializeToArray(&(buffer[0]),buffer.size());
return Hash(buffer.begin(), buffer.end());
} }
bool Wallet::signTransInput(uint256 hash, newcoin::TransInput& input,vector<unsigned char>& retSig)
bool Wallet::Account::signTransaction(TransactionPtr trans)
{ {
/* TODO:
uint256 hash = Transaction::calcHash(trans);
CKey key; CKey key;
if(!GetKey(input.from(), key)) if(!GetKey(input.from(), key))
return false; return false;
@@ -114,6 +134,7 @@ bool Wallet::signTransInput(uint256 hash, newcoin::TransInput& input,vector<unsi
if(!key.Sign(hash, retSig)) if(!key.Sign(hash, retSig))
return false; return false;
} }
*/
return(true); return(true);
} }
@@ -122,12 +143,17 @@ bool Wallet::signTransInput(uint256 hash, newcoin::TransInput& input,vector<unsi
// Call after CreateTransaction unless you want to abort // Call after CreateTransaction unless you want to abort
bool Wallet::commitTransaction(newcoin::Transaction& trans) bool Wallet::commitTransaction(TransactionPtr trans)
{ {
// TODO: Q up the message if it can't be relayed properly. or we don't see it added. if(trans)
PackedMessage::pointer msg(new PackedMessage(PackedMessage::MessagePointer(new newcoin::Transaction(trans)),newcoin::TRANSACTION)); {
theApp->getConnectionPool().relayMessage(NULL,msg,trans.ledgerindex()); if(theApp->getLedgerMaster().addTransaction(trans))
theApp->getLedgerMaster().addTransaction(trans); {
ConnectionPool& pool=theApp->getConnectionPool();
PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(new newcoin::Transaction(*(trans.get()))),newcoin::TRANSACTION));
pool.relayMessage(NULL,packet);
return true; }else cout << "Problem adding the transaction to your local ledger" << endl;
}
return(false);
} }

View File

@@ -3,6 +3,7 @@
#include "keystore.h" #include "keystore.h"
#include "newcoin.pb.h" #include "newcoin.pb.h"
#include "Transaction.h"
#include <list> #include <list>
#include <vector> #include <vector>
@@ -17,28 +18,41 @@ class Wallet : public CBasicKeyStore
{ {
public: public:
//CKey mKey; //CKey mKey;
std::string mAddress; //std::string mHumanAddress;
//std::vector<unsigned char> mPublicKey; uint160 mAddress;
//std::vector<unsigned char> mPrivateKey; std::vector<unsigned char> mPublicKey;
uint64 mAmount; std::vector<unsigned char> mPrivateKey;
uint64 mAge; int64 mAmount;
uint64 mAge; // do we need this
uint32 mSeqNum;
Account(){} Account(){}
bool signTransaction(TransactionPtr input);
}; };
std::list<Account> mYourAccounts; std::list<Account> mYourAccounts;
bool signTransInput(uint256 hash, newcoin::TransInput& input,std::vector<unsigned char>& retSig);
uint256 calcTransactionHash(newcoin::Transaction& trans);
bool createTransaction(NewcoinAddress& destAddress, uint64 amount,newcoin::Transaction& trans);
bool commitTransaction(newcoin::Transaction& trans); TransactionPtr createTransaction(Account& fromAccount, uint160& destAddr, int64 amount);
bool commitTransaction(TransactionPtr trans);
Account* consolidateAccountOfSize(int64 amount);
public: public:
Wallet(); Wallet();
void refreshAccounts();
void load(); void load();
uint64 getBalance(); int64 getBalance();
// returns some human error str? // returns some human error str?
std::string sendMoneyToAddress(NewcoinAddress& destAddress, uint64 amount); std::string sendMoneyToAddress(uint160& destAddress, int64 amount);
// you may need to update your balances
void transactionAdded(TransactionPtr trans);
}; };
#endif #endif

View File

@@ -20,20 +20,20 @@ message Hello {
required int32 port = 3; required int32 port = 3;
} }
message TransInput {
required string from = 1;
required uint64 amount = 2;
required bytes sig = 3;
}
// TODO: do we need a transID? /*
A transaction can have only one input and one output.
If you want to send an amount that is greater than any single address of yours
you must first combine coins from one address to another.
*/
// TODO: do we need a transID? I don't think we do
// ledgerIndex should increase the ledger coherence // ledgerIndex should increase the ledger coherence
message Transaction { message Transaction {
required bytes transID = 1; required bytes transID = 1;
repeated bytes from = 2; required bytes from = 2;
required bytes dest = 3; required bytes dest = 3;
required uint64 amount = 4; required uint64 amount = 4;
required uint64 ledgerIndex = 5; required uint32 ledgerIndex = 5;
required int32 seqNum = 6; required int32 seqNum = 6;
required bytes pubKey = 7; required bytes pubKey = 7;
required bytes sig = 8; required bytes sig = 8;
@@ -42,13 +42,14 @@ message Transaction {
// Sequence number is incremented if you must change the ledger that you are validating // Sequence number is incremented if you must change the ledger that you are validating
// You will only need to change validation in cases of incompatible ledgers // You will only need to change validation in cases of incompatible ledgers
message Validation { message Validation {
required uint64 ledgerIndex = 1; required uint32 ledgerIndex = 1;
required string hash = 2; required bytes hash = 2;
required string hanko = 3; required bytes hanko = 3;
required int32 seqNum = 4; required int32 seqNum = 4;
required bytes sig = 5; required bytes sig = 5;
} }
// seqnum should == last transaction seqnum+1
message Account { message Account {
required bytes address = 1; required bytes address = 1;
required uint64 amount = 2; required uint64 amount = 2;
@@ -58,7 +59,7 @@ message Account {
// The ledger hash includes: // The ledger hash includes:
// index,accounts,feeHeld,transactions? // index,accounts,feeHeld,transactions?
message FullLedger { message FullLedger {
required uint64 index = 1; required uint32 index = 1;
required bytes hash = 2; required bytes hash = 2;
required bytes parentHash = 3; required bytes parentHash = 3;
required uint64 feeHeld = 4; required uint64 feeHeld = 4;
@@ -68,12 +69,12 @@ message FullLedger {
message GetFullLedger { message GetFullLedger {
required uint64 ledgerIndex = 1; required uint32 ledgerIndex = 1;
optional bytes hash = 2; optional bytes hash = 2;
} }
message GetValidations { message GetValidations {
required uint64 ledgerIndex = 1; required uint32 ledgerIndex = 1;
} }
message Contact { message Contact {
@@ -87,7 +88,7 @@ message Contact {
// but I think the times this will happen are very rare so it's probably not worth it // but I think the times this will happen are very rare so it's probably not worth it
// but it might be worth also sending a hash of the accounts since if these match you don't care that the transactions don't // but it might be worth also sending a hash of the accounts since if these match you don't care that the transactions don't
message ProposeLedger { message ProposeLedger {
required uint64 ledgerIndex = 1; required uint32 ledgerIndex = 1;
required bytes hash = 2; required bytes hash = 2;
optional uint64 numTransactions = 3; optional uint64 numTransactions = 3;
} }

View File

@@ -49,12 +49,12 @@
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>c:\code\OpenSSL\include;C:\code\boost_1_47_0;C:\code\protobuf-2.4.1\src\</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\OpenSSL\include;..\boost_1_47_0;..\protobuf-2.4.1\src\</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\code\OpenSSL\lib\VC;C:\code\boost_1_47_0\stage\lib;C:\code\protobuf-2.4.1\vsprojects\Debug</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\OpenSSL\lib\VC;..\boost_1_47_0\stage\lib;..\protobuf-2.4.1\vsprojects\Debug</AdditionalLibraryDirectories>
<AdditionalDependencies>libeay32MTd.lib;libprotobuf.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libeay32MTd.lib;libprotobuf.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PreBuildEvent> <PreBuildEvent>
@@ -112,6 +112,7 @@
<ClCompile Include="RPCCommands.cpp" /> <ClCompile Include="RPCCommands.cpp" />
<ClCompile Include="RPCDoor.cpp" /> <ClCompile Include="RPCDoor.cpp" />
<ClCompile Include="TimingService.cpp" /> <ClCompile Include="TimingService.cpp" />
<ClCompile Include="Transaction.cpp" />
<ClCompile Include="TransactionBundle.cpp" /> <ClCompile Include="TransactionBundle.cpp" />
<ClCompile Include="UniqueNodeList.cpp" /> <ClCompile Include="UniqueNodeList.cpp" />
<ClCompile Include="util\pugixml.cpp" /> <ClCompile Include="util\pugixml.cpp" />
@@ -119,6 +120,7 @@
<ClCompile Include="Wallet.cpp" /> <ClCompile Include="Wallet.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Account.h" />
<ClInclude Include="Application.h" /> <ClInclude Include="Application.h" />
<ClInclude Include="base58.h" /> <ClInclude Include="base58.h" />
<ClInclude Include="bignum.h" /> <ClInclude Include="bignum.h" />
@@ -167,6 +169,7 @@
<ClInclude Include="script.h" /> <ClInclude Include="script.h" />
<ClInclude Include="SecureAllocator.h" /> <ClInclude Include="SecureAllocator.h" />
<ClInclude Include="TimingService.h" /> <ClInclude Include="TimingService.h" />
<ClInclude Include="ExtendedTransaction.h" />
<ClInclude Include="Transaction.h" /> <ClInclude Include="Transaction.h" />
<ClInclude Include="TransactionBundle.h" /> <ClInclude Include="TransactionBundle.h" />
<ClInclude Include="types.h" /> <ClInclude Include="types.h" />
@@ -180,7 +183,7 @@
<None Include="config.xml" /> <None Include="config.xml" />
<CustomBuild Include="newcoin.proto"> <CustomBuild Include="newcoin.proto">
<FileType>Document</FileType> <FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">c:/code/protoc-2.4.1-win32/protoc -I=C:\code\newcoin --cpp_out=C:\code\newcoin C:\code\newcoin\newcoin.proto</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\protoc-2.4.1-win32\protoc -I=..\newcoin --cpp_out=..\newcoin ..\newcoin\newcoin.proto</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">newcoin.pb.h</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">newcoin.pb.h</Outputs>
</CustomBuild> </CustomBuild>
<None Include="html\newcoin.html"> <None Include="html\newcoin.html">

View File

@@ -129,6 +129,9 @@
<ClCompile Include="ValidationCollection.cpp"> <ClCompile Include="ValidationCollection.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Transaction.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Application.h"> <ClInclude Include="Application.h">
@@ -173,9 +176,6 @@
<ClInclude Include="PeerDoor.h"> <ClInclude Include="PeerDoor.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Transaction.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="json\json_spirit.h"> <ClInclude Include="json\json_spirit.h">
<Filter>Header Files\json</Filter> <Filter>Header Files\json</Filter>
</ClInclude> </ClInclude>
@@ -299,6 +299,15 @@
<ClInclude Include="ValidationCollection.h"> <ClInclude Include="ValidationCollection.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ExtendedTransaction.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Transaction.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Account.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="nodes.xml" /> <None Include="nodes.xml" />

View File

@@ -2,7 +2,7 @@
Dependencies: Dependencies:
Boost 1_47 Boost 1_47
boost log http://boost-log.sourceforge.net/libs/log/doc/html/log/installation.html boost log http://boost-log.sourceforge.net/libs/log/doc/html/log/installation.html
protocol buffers protocol buffers: expects ..\protobuf-2.4.1 and ..\protoc-2.4.1-win32
openssl openssl
Using: Using:
@@ -25,18 +25,9 @@ code from:
message Packet {
enum Type { HELLO = 1; TRANSACTION = 2; VALIDATION = 3; }
// Identifies which field is filled in.
required Type type = 1;
// One of the following will be filled in.
optional Hello hello=2;
optional Transaction transaction=3;
optional Validation validation=4;
}
Threads Threads
---- ----

View File

@@ -1,9 +1,15 @@
#if defined(_MSC_VER) || defined(__BORLANDC__) #if defined(_MSC_VER) || defined(__BORLANDC__)
typedef __int64 int64; typedef __int64 int64;
typedef unsigned __int64 uint64; typedef unsigned __int64 uint64;
typedef unsigned int uint32;
#else #else
typedef long long int64; typedef long long int64;
typedef unsigned long long uint64; typedef unsigned long long uint64;
typedef unsigned int uint32;
#endif #endif
#if defined(_MSC_VER) && _MSC_VER < 1300 #if defined(_MSC_VER) && _MSC_VER < 1300
#define for if (false) ; else for #define for if (false) ; else for

View File

@@ -364,7 +364,7 @@ public:
} }
unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const unsigned int GetSerializeSize(int nType=0) const
{ {
return sizeof(pn); return sizeof(pn);
} }