#include #include #include #include "Application.h" #include "Ledger.h" #include "newcoin.pb.h" #include "PackedMessage.h" #include "Config.h" #include "Conversion.h" #include "BitcoinUtil.h" #include "Wallet.h" Ledger::Ledger(const uint160& masterID, uint64 startAmount) : mFeeHeld(0), mTimeStamp(0), mLedgerSeq(0), mClosed(false), mValidHash(false), mAccepted(false) { mTransactionMap=SHAMap::pointer(new SHAMap()); mAccountStateMap=SHAMap::pointer(new SHAMap()); AccountState::pointer startAccount=AccountState::pointer(new AccountState(masterID)); startAccount->credit(startAmount); if(!addAccountState(startAccount)) assert(false); } 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), mClosed(false), mValidHash(false), mAccepted(false) { updateHash(); } Ledger::Ledger(Ledger &prevLedger, uint64 ts) : mTimeStamp(ts), mClosed(false), mValidHash(false), mAccepted(false), mTransactionMap(new SHAMap()), mAccountStateMap(prevLedger.mAccountStateMap) { mParentHash=prevLedger.getHash(); mLedgerSeq=prevLedger.mLedgerSeq+1; } void Ledger::updateHash() { Serializer s(116); addRaw(s); mHash=s.getSHA512Half(); mValidHash=true; } void Ledger::addRaw(Serializer &s) { s.add32(mLedgerSeq); s.add64(mFeeHeld); s.add256(mParentHash); s.add256(mTransHash); s.add256(mAccountHash); s.add64(mTimeStamp); } AccountState::pointer Ledger::getAccountState(const uint160& accountID) { #ifdef DEBUG std::cerr << "Ledger:getAccountState(" << accountID.GetHex() << ")" << std::endl; #endif ScopedLock l(mTransactionMap->Lock()); SHAMapItem::pointer item=mAccountStateMap->peekItem(accountID.to256()); if(!item) { #ifdef DEBUG std::cerr << " notfound" << std::endl; #endif return AccountState::pointer(); } return AccountState::pointer(new AccountState(item->getData())); } uint64 Ledger::getBalance(const uint160& accountID) { ScopedLock l(mTransactionMap->Lock()); SHAMapItem::pointer item=mAccountStateMap->peekItem(accountID.to256()); if(!item) return 0; return AccountState(item->getData()).getBalance(); } bool Ledger::updateAccountState(AccountState::pointer state) { assert(!mAccepted); SHAMapItem::pointer item(new SHAMapItem(state->getAccountID(), state->getRaw())); return mAccountStateMap->updateGiveItem(item); } bool Ledger::addAccountState(AccountState::pointer state) { assert(!mAccepted); SHAMapItem::pointer item(new SHAMapItem(state->getAccountID(), state->getRaw())); return mAccountStateMap->addGiveItem(item); } bool Ledger::addTransaction(Transaction::pointer trans) { // low-level - just add to table assert(!mAccepted); assert(!!trans->getID()); SHAMapItem::pointer item(new SHAMapItem(trans->getID(), trans->getSigned()->getData())); return mTransactionMap->addGiveItem(item); } bool Ledger::delTransaction(const uint256& transID) { assert(!mAccepted); return mTransactionMap->delItem(transID); } Transaction::pointer Ledger::getTransaction(const uint256& transID) { ScopedLock l(mTransactionMap->Lock()); SHAMapItem::pointer item=mTransactionMap->peekItem(transID); if(!item) return Transaction::pointer(); Transaction::pointer trans(new Transaction(item->getData(), true)); if(trans->getStatus()==NEW) trans->setStatus(mClosed ? COMMITTED : INCLUDED, mLedgerSeq); return trans; } Ledger::TransResult Ledger::applyTransaction(Transaction::pointer trans) { assert(!mAccepted); boost::recursive_mutex::scoped_lock sl(mLock); if(trans->getSourceLedger()>mLedgerSeq) return TR_BADLSEQ; if(trans->getAmount()getFee()) { #ifdef DEBUG std::cerr << "Transaction for " << trans->getAmount() << ", but fee is " << trans->getFee() << std::endl; #endif return TR_TOOSMALL; } if(!mTransactionMap || !mAccountStateMap) return TR_ERROR; try { // already applied? Transaction::pointer dupTrans=getTransaction(trans->getID()); if(dupTrans) return TR_ALREADY; // accounts exist? AccountState::pointer fromAccount=getAccountState(trans->getFromAccount()); AccountState::pointer toAccount=getAccountState(trans->getToAccount()); // temporary code -- if toAccount doesn't exist but fromAccount does, create it if(!!fromAccount && !toAccount) { toAccount=AccountState::pointer(new AccountState(trans->getToAccount())); updateAccountState(toAccount); } if(!fromAccount || !toAccount) return TR_BADACCT; // pass sanity checks? if(fromAccount->getBalance()getAmount()) { #ifdef DEBUG std::cerr << "Transaction for " << trans->getAmount() << ", but account has " << fromAccount->getBalance() << std::endl; #endif return TR_INSUFF; } #ifdef DEBUG if(fromAccount->getSeq()!=trans->getFromAccountSeq()) std::cerr << "aSeq=" << fromAccount->getSeq() << ", tSeq=" << trans->getFromAccountSeq() << std::endl; #endif if(fromAccount->getSeq()>trans->getFromAccountSeq()) return TR_PASTASEQ; if(fromAccount->getSeq()getFromAccountSeq()) return TR_PREASEQ; // apply fromAccount->charge(trans->getAmount()); fromAccount->incSeq(); toAccount->credit(trans->getAmount()-trans->getFee()); mFeeHeld+=trans->getFee(); trans->setStatus(INCLUDED, mLedgerSeq); updateAccountState(fromAccount); updateAccountState(toAccount); addTransaction(trans); return TR_SUCCESS; } catch (SHAMapException) { return TR_ERROR; } } Ledger::TransResult Ledger::removeTransaction(Transaction::pointer trans) { // high-level - reverse application of transaction assert(!mAccepted); boost::recursive_mutex::scoped_lock sl(mLock); if(!mTransactionMap || !mAccountStateMap) return TR_ERROR; try { Transaction::pointer ourTrans=getTransaction(trans->getID()); if(!ourTrans) return TR_NOTFOUND; // accounts exist AccountState::pointer fromAccount=getAccountState(trans->getFromAccount()); AccountState::pointer toAccount=getAccountState(trans->getToAccount()); if(!fromAccount || !toAccount) return TR_BADACCT; // pass sanity checks? if(toAccount->getBalance()getAmount()) return TR_INSUFF; if(fromAccount->getSeq()!=(trans->getFromAccountSeq()+1)) return TR_PASTASEQ; // reverse fromAccount->credit(trans->getAmount()); fromAccount->decSeq(); toAccount->charge(trans->getAmount()-trans->getFee()); mFeeHeld-=trans->getFee(); trans->setStatus(REMOVED, mLedgerSeq); if(!delTransaction(trans->getID())) { assert(false); return TR_ERROR; } updateAccountState(fromAccount); updateAccountState(toAccount); return TR_SUCCESS; } catch (SHAMapException) { return TR_ERROR; } } Ledger::TransResult Ledger::hasTransaction(Transaction::pointer trans) { boost::recursive_mutex::scoped_lock sl(mLock); if(mTransactionMap==NULL) return TR_ERROR; try { Transaction::pointer t=getTransaction(trans->getID()); if(t==NULL) return TR_NOTFOUND; return TR_SUCCESS; } catch (SHAMapException) { return TR_ERROR; } } Ledger::pointer Ledger::closeLedger(uint64 timeStamp) { // close this ledger, return a pointer to the next ledger // CAUTION: New ledger needs its SHAMap's connected to storage setClosed(); return Ledger::pointer(new Ledger(*this, timeStamp)); } bool Ledger::unitTest() { uint160 la1=theApp->getWallet().addFamily(CKey::PassPhraseToKey("This is my payphrase!"), false); uint160 la2=theApp->getWallet().addFamily(CKey::PassPhraseToKey("Another payphrase"), false); LocalAccount::pointer l1=theApp->getWallet().getLocalAccount(la1, 0); LocalAccount::pointer l2=theApp->getWallet().getLocalAccount(la2, 0); assert(l1->getAddress()==la1); #ifdef DEBUG std::cerr << "Account1: " << la1.GetHex() << std::endl; std::cerr << "Account2: " << la2.GetHex() << std::endl; #endif Ledger::pointer ledger(new Ledger(la1, 100000)); ledger=Ledger::pointer(new Ledger(*ledger, 0)); AccountState::pointer as=ledger->getAccountState(la1); assert(as); assert(as->getBalance()==100000); assert(as->getSeq()==0); as=ledger->getAccountState(la2); assert(!as); Transaction::pointer t(new Transaction(NEW, l1, l1->getAcctSeq(), l2->getAddress(), 2500, 0, 1)); assert(!!t->getID()); Ledger::TransResult tr=ledger->applyTransaction(t); #ifdef DEBUG std::cerr << "Transaction: " << tr << std::endl; #endif assert(tr==TR_SUCCESS); return true; } uint256 Ledger::getHash() { if(!mValidHash) updateHash(); return(mHash); } void Ledger::saveAcceptedLedger(Ledger::pointer ledger) { std::string sql="INSERT INTO Ledgers " "(LedgerHash,LedgerSeq,PrevHash,FeeHeld,ClosingTime,AccountSetHash,TransSetHash) VALUES ('"; sql.append(ledger->getHash().GetHex()); sql.append("','"); sql.append(boost::lexical_cast(ledger->mLedgerSeq)); sql.append("','"); sql.append(ledger->mParentHash.GetHex()); sql.append("','"); sql.append(boost::lexical_cast(ledger->mFeeHeld)); sql.append("','"); sql.append(boost::lexical_cast(ledger->mTimeStamp)); sql.append("','"); sql.append(ledger->mAccountHash.GetHex()); sql.append("','"); sql.append(ledger->mTransHash.GetHex()); sql.append("');"); ScopedLock sl(theApp->getLedgerDB()->getDBLock()); theApp->getLedgerDB()->getDB()->executeSQL(sql.c_str()); // write out dirty nodes while(ledger->mTransactionMap->flushDirty(64, TRANSACTION_NODE, ledger->mLedgerSeq)) { ; } while(ledger->mAccountStateMap->flushDirty(64, ACCOUNT_NODE, ledger->mLedgerSeq)) { ; } } Ledger::pointer Ledger::getSQL(const std::string& sql) { uint256 ledgerHash, prevHash, accountHash, transHash; uint64 feeHeld, closingTime; uint32 ledgerSeq; std::string hash; if(1) { ScopedLock sl(theApp->getLedgerDB()->getDBLock()); Database *db=theApp->getLedgerDB()->getDB(); if(!db->executeSQL(sql.c_str()) || !db->startIterRows() || !db->getNextRow()) return Ledger::pointer(); db->getStr("LedgerHash", hash); ledgerHash.SetHex(hash); db->getStr("PrevHash", hash); prevHash.SetHex(hash); db->getStr("AccountSetHash", hash); accountHash.SetHex(hash); db->getStr("TransSetHash", hash); transHash.SetHex(hash); feeHeld=db->getBigInt("FeeHeld"); closingTime=db->getBigInt("ClosingTime"); ledgerSeq=db->getBigInt("LedgerSeq"); db->endIterRows(); } Ledger::pointer ret(new Ledger(prevHash, transHash, accountHash, feeHeld, closingTime, ledgerSeq)); if(ret->getHash()!=ledgerHash) { assert(false); return Ledger::pointer(); } return ret; } Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex) { std::string sql="SELECT * from Ledgers WHERE LedgerSeq='"; sql.append(boost::lexical_cast(ledgerIndex)); sql.append("';"); return getSQL(sql); } Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash) { std::string sql="SELECT * from Ledgers WHERE LedgerHash='"; sql.append(ledgerHash.GetHex()); sql.append("';"); return getSQL(sql); } #if 0 // returns true if the from account has enough for the transaction and seq num is correct bool Ledger::addTransaction(Transaction::pointer 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(Transaction::pointer 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); } } } // 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(); ) { Transaction::pointer 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