#include "LedgerMaster.h" #include "Application.h" #include "NewcoinAddress.h" #include using namespace std; LedgerMaster::LedgerMaster() { //mAfterProposed=false; } void LedgerMaster::load() { mLedgerHistory.load(); } void LedgerMaster::save() { } uint32 LedgerMaster::getCurrentLedgerIndex() { return(mCurrentLedger->getIndex()); } int64 LedgerMaster::getAmountHeld(uint160& addr) { return(mCurrentLedger->getAmountHeld(addr)); } int64 LedgerMaster::getAmountHeld(std::string& addr) { return(mCurrentLedger->getAmountHeld(NewcoinAddress::humanToInternal(addr))); } bool LedgerMaster::isTransactionOnFutureList(TransactionPtr needle) { BOOST_FOREACH(TransactionPtr straw,mFutureTransactions) { if(Transaction::isEqual(straw,needle)) { return(true); } } return(false); } // 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(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: since maybe we are adding a whole bunch at once. we should send at the end of the batch // TODO: do we ever really need to re-propose? //if(mAfterProposed) sendProposal(); theApp->getWallet().transactionChanged(trans); return(true); }else return(false); }else if(trans->ledgerindex()==mCurrentLedger->getIndex()) { if(mCurrentLedger->hasTransaction(trans)) return(false); if(!isValidTransaction(trans)) return(false); if(!mCurrentLedger->addTransaction(trans,false)) return(false); theApp->getWallet().transactionChanged(trans); return( true ); }else if(trans->ledgerindex()>mCurrentLedger->getIndex()) { // in the future if(isTransactionOnFutureList(trans)) return(false); if(!isValidTransaction(trans)) return(false); mFutureTransactions.push_back(trans); // broadcast once we get to that ledger. return(false); }else { // transaction is old but we don't have it. Add it to the current ledger 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.getAcceptedLedger(checkIndex); if(ledger) { if(ledger->hasTransaction(trans)) return(false); } checkIndex++; } if(!mCurrentLedger->addTransaction(trans,false)) return(false); theApp->getWallet().transactionChanged(trans); return(true); } } void LedgerMaster::addFullLedger(newcoin::FullLedger& ledger) { // check if we already have this ledger // check that the hash is correct uint256 inHash=Transaction::protobufToInternalHash(ledger.hash()); Ledger::pointer existingLedger=mLedgerHistory.getLedger( inHash ); if(existingLedger) return; Ledger::pointer newLedger=Ledger::pointer(new Ledger(ledger)); if(newLedger->getHash()==inHash) { mLedgerHistory.addLedger(newLedger); // add all these in case we are missing some BOOST_FOREACH(TransactionPtr trans, newLedger->getTransactions()) { addTransaction(trans); } }else cout << "We were sent a bad ledger hash" << endl; } void LedgerMaster::startFinalization() { mFinalizingLedger=mCurrentLedger; mCurrentLedger=Ledger::pointer(new Ledger(mCurrentLedger->getIndex()+1)); applyFutureProposals( mFinalizingLedger->getIndex() ); applyFutureTransactions( mCurrentLedger->getIndex() ); } void LedgerMaster::sendProposal() { PackedMessage::pointer packet=Peer::createLedgerProposal(mFinalizingLedger); theApp->getConnectionPool().relayMessage(NULL,packet); } void LedgerMaster::endFinalization() { mFinalizingLedger->publishValidation(); mLedgerHistory.addAcceptedLedger(mFinalizingLedger); mLedgerHistory.addLedger(mFinalizingLedger); mFinalizingLedger=Ledger::pointer(); } void LedgerMaster::addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger& otherLedger) { mFutureProposals.push_front(pair(peer,otherLedger)); } void LedgerMaster::applyFutureProposals(uint32 ledgerIndex) { for(list< pair >::iterator iter=mFutureProposals.begin(); iter !=mFutureProposals.end(); ) { if( (*iter).second.ledgerindex() == ledgerIndex) { checkLedgerProposal((*iter).first,(*iter).second); mFutureProposals.erase(iter); }else iter++; } } void LedgerMaster::applyFutureTransactions(uint32 ledgerIndex) { for(list::iterator iter=mFutureTransactions.begin(); iter !=mFutureTransactions.end(); ) { if( (*iter)->ledgerindex() == ledgerIndex) { addTransaction(*iter); mFutureTransactions.erase(iter); }else iter++; } } void LedgerMaster::checkLedgerProposal(Peer::pointer peer, newcoin::ProposeLedger& otherLedger) { // see if this matches yours // if you haven't finalized yet save it for when you do // if doesn't match and you have <= transactions ask for the complete ledger // if doesn't match and you have > transactions send your complete ledger if(otherLedger.ledgerindex()getIndex()) { // you have already closed this ledger Ledger::pointer oldLedger=mLedgerHistory.getAcceptedLedger(otherLedger.ledgerindex()); if(oldLedger) { if( (oldLedger->getHash()!=Transaction::protobufToInternalHash(otherLedger.hash())) && (oldLedger->getNumTransactions()>=otherLedger.numtransactions())) { peer->sendLedgerProposal(oldLedger); } } }else if(otherLedger.ledgerindex()>mFinalizingLedger->getIndex()) { // you haven't started finalizing this one yet save it for when you do addFutureProposal(peer,otherLedger); }else { // you guys are on the same page if(mFinalizingLedger->getHash()!= Transaction::protobufToInternalHash(otherLedger.hash())) { if( mFinalizingLedger->getNumTransactions()>=otherLedger.numtransactions()) { peer->sendLedgerProposal(mFinalizingLedger); }else { peer->sendGetFullLedger(otherLedger.ledgerindex()); } } } }