#include "Ledger.h" #include "newcoin.pb.h" #include "PackedMessage.h" #include "Config.h" #include "Conversion.h" #include "BitcoinUtil.h" #include #include #include using namespace boost; using namespace std; Ledger::Ledger(uint32 index) : mFeeHeld(0), mTimeStamp(0), mLedgerSeq(index) { } Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, uint64 feeHeld, uint64 timeStamp, uint32 ledgerSeq) : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mFeeHeld(feeHeld), mTimeStamp(timeStamp), mLedgerSeq(ledgerSeq) { updateHash(); } void Ledger::updateHash() { Serializer s(116); addRaw(s); mHash=s.getSHA512Half(); } void Ledger::addRaw(Serializer &s) { s.add32(mLedgerSeq); s.add64(mFeeHeld); s.add256(mParentHash); s.add256(mTransHash); s.add256(mAccountHash); s.add64(mTimeStamp); } Ledger::TransResult Ledger::applyTransaction(TransactionPtr &trans) { } #if 0 // TODO: we should probably make a shared pointer type for each of these PB types newcoin::FullLedger* Ledger::createFullLedger() { newcoin::FullLedger* ledger=new newcoin::FullLedger(); ledger->set_index(mIndex); ledger->set_hash(getHash().begin(),getHash().GetSerializeSize()); ledger->set_parenthash(mParentHash.begin(),mParentHash.GetSerializeSize()); BOOST_FOREACH(PAIR(const uint160, Account)& account, mAccounts) { newcoin::Account* saveAccount=ledger->add_accounts(); saveAccount->set_address(account.first.begin(),account.first.GetSerializeSize()); saveAccount->set_amount(account.second.first); saveAccount->set_seqnum(account.second.second); } return(ledger); } void Ledger::setTo(newcoin::FullLedger& ledger) { mIndex=ledger.index(); mTransactions.clear(); mDiscardedTransactions.clear(); mAccounts.clear(); mValidSig=false; mValidHash=false; mParentHash=protobufTo256(ledger.parenthash()); int numAccounts=ledger.accounts_size(); for(int n=0; ngetLedgerMaster().getLedger(mParentHash); } return(mParent); } // TODO: we can optimize so the ledgers only hold the delta from the accepted ledger // TODO: check to make sure the ledger is consistent after we load it bool Ledger::load(const uint256& hash) { Database* db=theApp->getDB(); string sql="SELECT * from Ledgers where hash="; string hashStr; db->escape(hash.begin(),hash.GetSerializeSize(),hashStr); sql.append(hashStr); if(db->executeSQL(sql.c_str())) { if(db->getNextRow()) { mIndex=db->getInt("LedgerIndex"); mHash=hash; mValidSig=false; mAccounts.clear(); mTransactions.clear(); mDiscardedTransactions.clear(); db->getBinary("ParentHash",mParentHash.begin(),mParentHash.GetSerializeSize()); mFeeHeld=db->getBigInt("FeeHeld"); char buf[100]; sql="SELECT Transactions.* from Transactions,LedgerTransactionMap where Transactions.TransactionID=LedgerTransactionMap.TransactionID and LedgerTransactionMap.LedgerID="; sprintf(buf, "%d", db->getInt(0)); sql.append(buf); if(db->executeSQL(sql.c_str())) { unsigned char tbuf[1000]; while(db->getNextRow()) { TransactionPtr trans=TransactionPtr(new newcoin::Transaction()); trans->set_amount( db->getBigInt("Amount")); trans->set_seqnum( db->getInt("seqnum")); trans->set_ledgerindex( db->getInt("ledgerIndex")); db->getBinary("from",tbuf,1000); trans->set_from(tbuf,20); db->getBinary("dest",tbuf,1000); trans->set_dest(tbuf,20); db->getBinary("pubkey",tbuf,1000); trans->set_pubkey(tbuf,128); db->getBinary("sig",tbuf,1000); trans->set_sig(tbuf,32); mTransactions.push_back(trans); } } sql="SELECT Accounts.* from Acconts,LedgerAcountMap where Accounts.AccountID=LedgerAccountMap.AccountID and LedgerAccountMap.LedgerID="; sql.append(buf); if(db->executeSQL(sql.c_str())) { while(db->getNextRow()) { uint160 address; db->getBinary("Address",address.begin(),address.GetSerializeSize()); mAccounts[address].first=db->getBigInt("Amount"); mAccounts[address].second=db->getInt("SeqNum"); } } return(true); } } return(false); } void Ledger::save() { Database* db=theApp->getDB(); string sql="SELECT ledgerID from Ledgers where hash="; string hashStr; db->escape(mHash.begin(),mHash.GetSerializeSize(),hashStr); sql.append(hashStr); if(db->executeSQL(sql.c_str())) { db->startIterRows(); if(db->getNextRow()) { // this Ledger is already in the DB. We don't need to do anything since the hashes are the same db->endIterRows(); }else { // this ledger isn't in the DB char buf[100]; sql="INSERT INTO Ledgers (LedgerIndex,Hash,ParentHash,FeeHeld) values ("; sprintf(buf, "%d", mIndex); sql.append(buf); sql.append(","); sql.append(buf); sql.append(","); sql.append(buf); sql.append(","); sprintf(buf, "%llu", mFeeHeld); sql.append(buf); sql.append(")"); sql="SELECT LAST_INSERT_ID()"; } } } int64 Ledger::getAmountHeld(const uint160& address) { if(mAccounts.count(address)) { return(mAccounts[address].first); } return(0); } Ledger::Account* Ledger::getAccount(const uint160& address) { if(mAccounts.count(address)) { return(&(mAccounts[address])); } return(NULL); } uint256& Ledger::getHash() { if(!mValidHash) hash(); return(mHash); } uint256& Ledger::getSignature() { if(!mValidSig) sign(); return(mSignature); } void Ledger::publishValidation() { PackedMessage::pointer packet=Peer::createValidation(shared_from_this()); theApp->getConnectionPool().relayMessage(NULL,packet); } void Ledger::sign() { // TODO: Ledger::sign() } void Ledger::hash() { // TODO: Ledger::hash() } /* uint64 Ledger::getAmount(std::string address) { return(mAccounts[NewcoinAddress:: address].first); }*/ // returns true if the from account has enough for the transaction and seq num is correct bool Ledger::addTransaction(TransactionPtr trans,bool checkDuplicate) { if(checkDuplicate && hasTransaction(trans)) return(false); if(mParent) { // check the lineage of the from addresses uint160 address=protobufTo160(trans->from()); if(mAccounts.count(address)) { pair account=mAccounts[address]; if( (account.firstamount()) && (trans->seqnum()==account.second) ) { account.first -= trans->amount(); account.second++; mAccounts[address]=account; uint160 destAddress=protobufTo160(trans->dest()); Account destAccount=mAccounts[destAddress]; destAccount.first += trans->amount(); mAccounts[destAddress]=destAccount; mValidSig=false; mValidHash=false; 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 { // we have no way to know so just hold on to it but don't add to the accounts mValidSig=false; mValidHash=false; mDiscardedTransactions.push_back(trans); return(true); } } // Don't check the amounts. We will do this at the end. void Ledger::addTransactionAllowNeg(TransactionPtr trans) { uint160 fromAddress=protobufTo160(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=protobufTo160(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=protobufTo160(trans->dest()); Account destAccount=mAccounts[destAddress]; destAccount.first += trans->amount(); mAccounts[destAddress]=destAccount; mTransactions.push_back(trans); }else { mDiscardedTransactions.push_back(trans); } } } // 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 firstTransactions=mTransactions; list secondTransactions=mDiscardedTransactions; mTransactions.clear(); mDiscardedTransactions.clear(); firstTransactions.sort(gTransactionSorter); secondTransactions.sort(gTransactionSorter); // don't check balances until the end BOOST_FOREACH(TransactionPtr trans,firstTransactions) { addTransactionAllowNeg(trans); } BOOST_FOREACH(TransactionPtr trans,secondTransactions) { addTransactionAllowNeg(trans); } correctAccounts(); if(mChild && recursive) mChild->recalculate(); }else { cout << "Can't recalculate if there is no parent" << endl; } } void Ledger::parentAddedTransaction(TransactionPtr cause) { // TODO: optimize 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=protobufTo160(cause->from()); uint160 destAddress=protobufTo160(cause->dest()); Account* fromAccount=getAccount(fromAddress); Account* destAccount=getAccount(destAddress); if(fromAccount) { if(fromAccount->firstamount()) { 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(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); } // Ledgers are compatible if both sets of transactions merged together would lead to the same ending balance bool Ledger::isCompatible(Ledger::pointer other) { Ledger::pointer l1=Ledger::pointer(new Ledger(*this)); Ledger::pointer l2=Ledger::pointer(new Ledger(*other)); l1->mergeIn(l2); l2->mergeIn(l1); map a1=l1->getAccounts(); map a2=l2->getAccounts(); return(a1==a2); } void Ledger::mergeIn(Ledger::pointer other) { list& otherTransactions=other->getTransactions(); BOOST_FOREACH(TransactionPtr trans,otherTransactions) { addTransactionAllowNeg(trans); } correctAccounts(); } void Ledger::correctAccounts() { BOOST_FOREACH(PAIR(const uint160, Account)& fullAccount, mAccounts) { if(fullAccount.second.first <0 ) { correctAccount(fullAccount.first); } } } // 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(const uint160& address) { list effected; // do this in reverse so we take of the higher seqnum first for( list::reverse_iterator iter=mTransactions.rbegin(); iter != mTransactions.rend(); ) { TransactionPtr trans= *iter; if(protobufTo160(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=protobufTo160(trans->dest()); Account destAccount=mAccounts[destAddress]; destAccount.first -= trans->amount(); mAccounts[destAddress]=destAccount; if(destAccount.first<0) effected.push_back(destAddress); list::iterator temp=mTransactions.erase( --iter.base() ); if(fromAccount.first>=0) break; iter=list::reverse_iterator(temp); }else break; }else iter--; } BOOST_FOREACH(uint160& address,effected) { correctAccount(address); } } #endif