mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
Conflicts: src/Peer.cpp
This commit is contained in:
@@ -1,42 +1,32 @@
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "AccountState.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "Serializer.h"
|
||||
|
||||
AccountState::AccountState(const std::vector<unsigned char>& v)
|
||||
AccountState::AccountState(const NewcoinAddress& id) : mAccountID(id), mValid(false)
|
||||
{
|
||||
Serializer s(v);
|
||||
mValid=false;
|
||||
uint160 acct160 = mAccountID.getAccountID();
|
||||
|
||||
if(!s.get160(acct160, 0)) { assert(false); return; }
|
||||
if(!s.get64(mBalance, 20)) { assert(false); return; }
|
||||
if(!s.get32(mAccountSeq, 28)) { assert(false); return; }
|
||||
mValid=true;
|
||||
if (!id.IsValid()) return;
|
||||
mLedgerEntry = boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT);
|
||||
mLedgerEntry->setIndex(Ledger::getAccountRootIndex(id));
|
||||
mLedgerEntry->setIFieldAccount(sfAccount, id);
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
AccountState::AccountState(const NewcoinAddress& id) : mAccountID(id), mBalance(0), mAccountSeq(0), mValid(true)
|
||||
{ ; }
|
||||
|
||||
std::vector<unsigned char> AccountState::getRaw() const
|
||||
{ // 20-byte acct ID, 8-byte balance, 4-byte sequence
|
||||
Serializer s(32);
|
||||
s.add160(mAccountID.getAccountID());
|
||||
s.add64(mBalance);
|
||||
s.add32(mAccountSeq);
|
||||
return s.getData();
|
||||
AccountState::AccountState(SerializedLedgerEntry::pointer ledgerEntry) : mLedgerEntry(ledgerEntry), mValid(false)
|
||||
{
|
||||
if (!mLedgerEntry) return;
|
||||
if (mLedgerEntry->getType()!=ltACCOUNT_ROOT) return;
|
||||
mAccountID = mLedgerEntry->getValueFieldAccount(sfAccount);
|
||||
if (mAccountID.IsValid()) mValid = true;
|
||||
}
|
||||
|
||||
void AccountState::addJson(Json::Value& val)
|
||||
{
|
||||
Json::Value as(Json::objectValue);
|
||||
|
||||
as["AccountID"]=mAccountID.humanAccountID();
|
||||
as["Balance"]=boost::lexical_cast<std::string>(mBalance);
|
||||
as["SendSequence"]=mAccountSeq;
|
||||
if(!mValid) as["Invalid"]=true;
|
||||
|
||||
val[mAccountID.humanAccountID()]=as;
|
||||
val = mLedgerEntry->getJson(0);
|
||||
if(!mValid) val["Invalid"]=true;
|
||||
}
|
||||
// vim:ts=4
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef __ACCOUNTSTATE__
|
||||
#define __ACCOUNTSTATE__
|
||||
|
||||
// An account's state in one or more accepted ledgers
|
||||
// An account's state
|
||||
// Used to track information about a local account
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -10,8 +11,8 @@
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "uint256.h"
|
||||
#include "NewcoinAddress.h"
|
||||
#include "SerializedLedger.h"
|
||||
|
||||
class AccountState
|
||||
{
|
||||
@@ -20,37 +21,20 @@ public:
|
||||
|
||||
private:
|
||||
NewcoinAddress mAccountID;
|
||||
uint64 mBalance;
|
||||
uint32 mAccountSeq;
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
bool mValid;
|
||||
|
||||
public:
|
||||
AccountState(const NewcoinAddress& mAccountID); // new account
|
||||
AccountState(const std::vector<unsigned char>&); // raw form
|
||||
AccountState(const NewcoinAddress& AccountID); // For new accounts
|
||||
AccountState(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
|
||||
|
||||
const NewcoinAddress& getAccountID() const { return mAccountID; }
|
||||
uint64 getBalance() const { return mBalance; }
|
||||
uint32 getSeq() const { return mAccountSeq; }
|
||||
uint64 getBalance() const { return mLedgerEntry->getIFieldU64(sfBalance); }
|
||||
uint32 getSeq() const { return mLedgerEntry->getIFieldU32(sfSequence); }
|
||||
|
||||
void credit(const uint64& a)
|
||||
{
|
||||
mBalance+=a;
|
||||
if(!mAccountSeq) mAccountSeq=1; // an account with non-0 balance cannot have 0 sequence
|
||||
}
|
||||
void charge(const uint64& a)
|
||||
{
|
||||
assert(mBalance>=a);
|
||||
mBalance-=a;
|
||||
}
|
||||
void incSeq()
|
||||
{
|
||||
mAccountSeq++;
|
||||
}
|
||||
void decSeq()
|
||||
{
|
||||
assert(mAccountSeq!=0);
|
||||
mAccountSeq--;
|
||||
}
|
||||
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
|
||||
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }
|
||||
SerializedLedgerEntry& peekSLE() { return *mLedgerEntry; }
|
||||
|
||||
std::vector<unsigned char> getRaw() const;
|
||||
void addJson(Json::Value& value);
|
||||
|
||||
@@ -117,7 +117,7 @@ void Application::run()
|
||||
// temporary
|
||||
|
||||
mWallet.load();
|
||||
mWallet.syncToLedger(true, &(*secondLedger));
|
||||
// mWallet.syncToLedger(true, &(*secondLedger));
|
||||
|
||||
// temporary
|
||||
mIOService.run(); // This blocks
|
||||
@@ -128,7 +128,7 @@ void Application::run()
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
delete mTxnDB;;
|
||||
delete mTxnDB;
|
||||
delete mLedgerDB;
|
||||
delete mWalletDB;
|
||||
delete mHashNodeDB;
|
||||
|
||||
194
src/Ledger.cpp
194
src/Ledger.cpp
@@ -22,10 +22,11 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) :
|
||||
mTransactionMap = boost::make_shared<SHAMap>();
|
||||
mAccountStateMap = boost::make_shared<SHAMap>();
|
||||
|
||||
// special case: put coins in root account
|
||||
AccountState::pointer startAccount = boost::make_shared<AccountState>(masterID);
|
||||
startAccount->credit(startAmount);
|
||||
if (!addAccountState(startAccount))
|
||||
assert(false);
|
||||
startAccount->peekSLE().setIFieldU64(sfBalance, startAmount);
|
||||
startAccount->peekSLE().setIFieldU32(sfSequence, 1);
|
||||
writeBack(lepCREATE, startAccount->getSLE());
|
||||
}
|
||||
|
||||
Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
|
||||
@@ -116,7 +117,7 @@ AccountState::pointer Ledger::getAccountState(const NewcoinAddress& accountID)
|
||||
std::cerr << "Ledger:getAccountState(" << accountID.humanAccountID() << ")" << std::endl;
|
||||
#endif
|
||||
ScopedLock l(mTransactionMap->Lock());
|
||||
SHAMapItem::pointer item = mAccountStateMap->peekItem(accountID.getAccountID().to256());
|
||||
SHAMapItem::pointer item = mAccountStateMap->peekItem(Ledger::getAccountRootIndex(accountID));
|
||||
if (!item)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@@ -124,30 +125,10 @@ AccountState::pointer Ledger::getAccountState(const NewcoinAddress& accountID)
|
||||
#endif
|
||||
return AccountState::pointer();
|
||||
}
|
||||
return boost::make_shared<AccountState>(item->getData());
|
||||
}
|
||||
|
||||
uint64 Ledger::getBalance(const NewcoinAddress& accountID) const
|
||||
{
|
||||
ScopedLock l(mTransactionMap->Lock());
|
||||
SHAMapItem::pointer item = mAccountStateMap->peekItem(accountID.getAccountID().to256());
|
||||
if (!item) return 0;
|
||||
return AccountState(item->getData()).getBalance();
|
||||
}
|
||||
|
||||
bool Ledger::updateAccountState(AccountState::pointer state)
|
||||
{
|
||||
assert(!mAccepted);
|
||||
return mAccountStateMap->updateGiveItem(boost::make_shared<SHAMapItem>(state->getAccountID().getAccountID(),
|
||||
state->getRaw()), false);
|
||||
}
|
||||
|
||||
bool Ledger::addAccountState(AccountState::pointer state)
|
||||
{
|
||||
assert(!mAccepted);
|
||||
assert( (state->getBalance()==0) || (state->getSeq()>0) );
|
||||
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(state->getAccountID().getAccountID(), state->getRaw());
|
||||
return mAccountStateMap->addGiveItem(item, false);
|
||||
SerializedLedgerEntry::pointer sle =
|
||||
boost::make_shared<SerializedLedgerEntry>(item->peekSerializer(), item->getTag());
|
||||
if (sle->getType() != ltACCOUNT_ROOT) return AccountState::pointer();
|
||||
return boost::make_shared<AccountState>(sle);
|
||||
}
|
||||
|
||||
bool Ledger::addTransaction(Transaction::pointer trans)
|
||||
@@ -160,6 +141,12 @@ bool Ledger::addTransaction(Transaction::pointer trans)
|
||||
return mTransactionMap->addGiveItem(item, true);
|
||||
}
|
||||
|
||||
bool Ledger::addTransaction(const uint256& txID, const Serializer& txn)
|
||||
{ // low-level - just add to table
|
||||
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(txID, txn.peekData());
|
||||
return mTransactionMap->addGiveItem(item, true);
|
||||
}
|
||||
|
||||
bool Ledger::delTransaction(const uint256& transID)
|
||||
{
|
||||
assert(!mAccepted);
|
||||
@@ -187,145 +174,6 @@ Transaction::pointer Ledger::getTransaction(const uint256& transID) const
|
||||
return txn;
|
||||
}
|
||||
|
||||
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()<trans->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=boost::make_shared<AccountState>(trans->getToAccount());
|
||||
toAccount->incSeq(); // an account in a ledger has a sequence of 1
|
||||
updateAccountState(toAccount);
|
||||
}
|
||||
|
||||
if(!fromAccount || !toAccount) return TR_BADACCT;
|
||||
|
||||
// pass sanity checks?
|
||||
if(fromAccount->getBalance()<trans->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()<trans->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()<trans->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)
|
||||
{ // Is this transaction in this ledger? If not, could it go in it?
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if(mTransactionMap==NULL) return TR_ERROR;
|
||||
try
|
||||
{
|
||||
Transaction::pointer t=getTransaction(trans->getID());
|
||||
if(!!t) return TR_ALREADY;
|
||||
|
||||
if(trans->getSourceLedger()>mLedgerSeq) return TR_BADLSEQ;
|
||||
|
||||
AccountState::pointer fromAccount=getAccountState(trans->getFromAccount());
|
||||
if(!fromAccount) return TR_BADACCT; // cannot send from non-existent account
|
||||
|
||||
// may be in a previous ledger
|
||||
if(fromAccount->getSeq()>trans->getFromAccountSeq()) return TR_PASTASEQ;
|
||||
|
||||
if(fromAccount->getSeq()<trans->getFromAccountSeq()) return TR_PREASEQ;
|
||||
if(fromAccount->getBalance()<trans->getAmount()) return TR_INSUFF;
|
||||
return TR_NOTFOUND;
|
||||
}
|
||||
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
|
||||
@@ -334,18 +182,6 @@ Ledger::pointer Ledger::closeLedger(uint64 timeStamp)
|
||||
return Ledger::pointer(new Ledger(*this, timeStamp)); // can't use make_shared, constructor is protected
|
||||
}
|
||||
|
||||
void LocalAccount::syncLedger()
|
||||
{
|
||||
AccountState::pointer as=theApp->getMasterLedger().getAccountState(getAddress());
|
||||
if(!as) mLgrBalance=0;
|
||||
else
|
||||
{
|
||||
mLgrBalance=as->getBalance();
|
||||
if( (mLgrBalance!=0) && (mTxnSeq==0) ) mTxnSeq=1;
|
||||
if(mTxnSeq<as->getSeq()) mTxnSeq=as->getSeq();
|
||||
}
|
||||
}
|
||||
|
||||
bool Ledger::unitTest()
|
||||
{
|
||||
#if 0
|
||||
|
||||
28
src/Ledger.h
28
src/Ledger.h
@@ -10,16 +10,16 @@
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "Transaction.h"
|
||||
#include "AccountState.h"
|
||||
#include "types.h"
|
||||
#include "BitcoinUtil.h"
|
||||
#include "Hanko.h"
|
||||
#include "AccountState.h"
|
||||
#include "SHAMap.h"
|
||||
#include "SerializedLedger.h"
|
||||
|
||||
|
||||
enum LedgerStateParms
|
||||
{
|
||||
lepNONE = 0, // no special flags
|
||||
|
||||
// input flags
|
||||
lepCREATE, // Create if not present
|
||||
|
||||
@@ -33,6 +33,7 @@ enum LedgerStateParms
|
||||
|
||||
class Ledger : public boost::enable_shared_from_this<Ledger>
|
||||
{ // The basic Ledger structure, can be opened, closed, or synching
|
||||
friend class TransactionEngine;
|
||||
public:
|
||||
typedef boost::shared_ptr<Ledger> pointer;
|
||||
|
||||
@@ -70,13 +71,15 @@ protected:
|
||||
Ledger(Ledger& previous, uint64 timestamp); // ledger after this one
|
||||
void updateHash();
|
||||
|
||||
bool addAccountState(AccountState::pointer);
|
||||
bool updateAccountState(AccountState::pointer);
|
||||
bool addTransaction(Transaction::pointer);
|
||||
bool addTransaction(const uint256& id, const Serializer& txn);
|
||||
bool delTransaction(const uint256& id);
|
||||
|
||||
static Ledger::pointer getSQL(const std::string& sqlStatement);
|
||||
|
||||
SerializedLedgerEntry::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID,
|
||||
LedgerEntryType let);
|
||||
|
||||
public:
|
||||
Ledger(const NewcoinAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger
|
||||
Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
|
||||
@@ -114,15 +117,10 @@ public:
|
||||
bool hasTransaction(const uint256& TransID) const;
|
||||
Transaction::pointer getTransaction(const uint256& transID) const;
|
||||
|
||||
// OLD high level functions
|
||||
uint64 getBalance(const NewcoinAddress& acctID) const;
|
||||
AccountState::pointer getAccountState(const NewcoinAddress& acctID);
|
||||
TransResult applyTransaction(Transaction::pointer trans);
|
||||
TransResult removeTransaction(Transaction::pointer trans);
|
||||
TransResult hasTransaction(Transaction::pointer trans);
|
||||
Ledger::pointer switchPreviousLedger(Ledger::pointer oldPrevious, Ledger::pointer newPrevious, int limit);
|
||||
|
||||
// high-level functions
|
||||
AccountState::pointer getAccountState(const NewcoinAddress& acctID);
|
||||
LedgerStateParms writeBack(LedgerStateParms parms, SerializedLedgerEntry::pointer);
|
||||
SerializedLedgerEntry::pointer getAccountRoot(LedgerStateParms& parms, const uint160& accountID);
|
||||
SerializedLedgerEntry::pointer getNickname(LedgerStateParms& parms, const std::string& nickname);
|
||||
@@ -137,7 +135,15 @@ public:
|
||||
|
||||
// index calculation functions
|
||||
static uint256 getAccountRootIndex(const uint160& account);
|
||||
static uint256 getAccountRootIndex(const NewcoinAddress& account)
|
||||
{ return getAccountRootIndex(account.getAccountID()); }
|
||||
|
||||
static uint256 getRippleIndex(const uint160& account, const uint160& extendTo, const uint160& currency);
|
||||
static uint256 getRippleIndex(const uint160& account, const uint160& extendTo)
|
||||
{ return getRippleIndex(account, extendTo, uint160()); }
|
||||
static uint256 getRippleIndex(const NewcoinAddress& account, const NewcoinAddress& extendTo,
|
||||
const uint160& currency)
|
||||
{ return getRippleIndex(account.getAccountID(), extendTo.getAccountID(), currency); }
|
||||
|
||||
Ledger::pointer closeLedger(uint64 timestamp);
|
||||
bool isCompatible(boost::shared_ptr<Ledger> other);
|
||||
|
||||
@@ -14,20 +14,6 @@ uint32 LedgerMaster::getCurrentLedgerIndex()
|
||||
return mCurrentLedger->getLedgerSeq();
|
||||
}
|
||||
|
||||
uint64 LedgerMaster::getBalance(const NewcoinAddress& acctID)
|
||||
{
|
||||
return mCurrentLedger->getBalance(acctID);
|
||||
}
|
||||
|
||||
uint64 LedgerMaster::getBalance(std::string& strAcctID)
|
||||
{
|
||||
NewcoinAddress acctID;
|
||||
|
||||
acctID.setAccountID(strAcctID);
|
||||
|
||||
return mCurrentLedger->getBalance(acctID);
|
||||
}
|
||||
|
||||
bool LedgerMaster::addHeldTransaction(Transaction::pointer transaction)
|
||||
{ // returns true if transaction was added
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
@@ -43,8 +29,9 @@ void LedgerMaster::pushLedger(Ledger::pointer newLedger)
|
||||
mFinalizingLedger->setAccepted();
|
||||
mLedgerHistory.addAcceptedLedger(mFinalizingLedger);
|
||||
}
|
||||
mFinalizingLedger=mCurrentLedger;
|
||||
mCurrentLedger=newLedger;
|
||||
mFinalizingLedger = mCurrentLedger;
|
||||
mCurrentLedger = newLedger;
|
||||
mEngine.setLedger(newLedger);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Peer.h"
|
||||
#include "types.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionEngine.h"
|
||||
|
||||
// Tracks the current ledger and any ledgers in the process of closing
|
||||
// Tracks ledger history
|
||||
@@ -16,6 +17,8 @@ class LedgerMaster
|
||||
boost::recursive_mutex mLock;
|
||||
bool mIsSynced;
|
||||
|
||||
TransactionEngine mEngine;
|
||||
|
||||
Ledger::pointer mCurrentLedger;
|
||||
Ledger::pointer mFinalizingLedger;
|
||||
|
||||
@@ -38,6 +41,9 @@ public:
|
||||
Ledger::pointer getCurrentLedger() { return mCurrentLedger; }
|
||||
Ledger::pointer getClosingLedger() { return mFinalizingLedger; }
|
||||
|
||||
TransactionEngineResult doTransaction(const SerializedTransaction& txn, TransactionEngineParams params)
|
||||
{ return mEngine.applyTransaction(txn, params); }
|
||||
|
||||
void pushLedger(Ledger::pointer newLedger);
|
||||
|
||||
Ledger::pointer getLedgerBySeq(uint32 index)
|
||||
@@ -54,12 +60,9 @@ public:
|
||||
return mLedgerHistory.getLedgerByHash(hash);
|
||||
}
|
||||
|
||||
uint64 getBalance(std::string& strAcctID);
|
||||
uint64 getBalance(const NewcoinAddress& acctID);
|
||||
AccountState::pointer getAccountState(const NewcoinAddress& addr)
|
||||
{ return mCurrentLedger->getAccountState(addr); }
|
||||
|
||||
bool addHeldTransaction(Transaction::pointer trans);
|
||||
uint64 getBalance(std::string& strAcctID, const uint160 currency = 0);
|
||||
uint64 getBalance(const NewcoinAddress& acctID, const uint160 currency = 0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,12 +3,30 @@
|
||||
|
||||
#include "boost/make_shared.hpp"
|
||||
|
||||
SerializedLedgerEntry::pointer Ledger::getAccountRoot(LedgerStateParms& parms, const uint160& accountID)
|
||||
LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SerializedLedgerEntry::pointer entry)
|
||||
{
|
||||
uint256 nodeID=getAccountRootIndex(accountID);
|
||||
|
||||
ScopedLock l(mAccountStateMap->Lock());
|
||||
bool create = false;
|
||||
|
||||
if (!mAccountStateMap->hasItem(entry->getIndex()))
|
||||
{
|
||||
if ((parms & lepCREATE) == 0)
|
||||
return lepMISSING;
|
||||
create = true;
|
||||
}
|
||||
|
||||
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(entry->getIndex());
|
||||
entry->add(item->peekSerializer());
|
||||
|
||||
if (!mAccountStateMap->updateGiveItem(item, false))
|
||||
return lepERROR;
|
||||
|
||||
return create ? lepCREATED : lepOKAY;
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer Ledger::getASNode(LedgerStateParms& parms, const uint256& nodeID,
|
||||
LedgerEntryType let )
|
||||
{
|
||||
SHAMapItem::pointer account = mAccountStateMap->peekItem(nodeID);
|
||||
if (!account)
|
||||
{
|
||||
@@ -18,26 +36,57 @@ SerializedLedgerEntry::pointer Ledger::getAccountRoot(LedgerStateParms& parms, c
|
||||
return SerializedLedgerEntry::pointer();
|
||||
}
|
||||
|
||||
parms = lepCREATED;
|
||||
SerializedLedgerEntry::pointer sle=boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT);
|
||||
parms = parms | lepCREATED | lepOKAY;
|
||||
SerializedLedgerEntry::pointer sle=boost::make_shared<SerializedLedgerEntry>(let);
|
||||
sle->setIndex(nodeID);
|
||||
return sle;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SerializedLedgerEntry::pointer sle =
|
||||
boost::make_shared<SerializedLedgerEntry>(account->peekSerializer(), nodeID);
|
||||
|
||||
if(sle->getType() != ltACCOUNT_ROOT)
|
||||
if(sle->getType() != let)
|
||||
{ // maybe it's a currency or something
|
||||
parms = lepWRONGTYPE;
|
||||
parms = parms | lepWRONGTYPE;
|
||||
return SerializedLedgerEntry::pointer();
|
||||
}
|
||||
parms = lepOKAY;
|
||||
|
||||
parms = parms | lepOKAY;
|
||||
return sle;
|
||||
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer Ledger::getAccountRoot(LedgerStateParms& parms, const uint160& accountID)
|
||||
{
|
||||
uint256 nodeID=getAccountRootIndex(accountID);
|
||||
|
||||
ScopedLock l(mAccountStateMap->Lock());
|
||||
|
||||
try
|
||||
{
|
||||
return getASNode(parms, nodeID, ltACCOUNT_ROOT);
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
parms = lepERROR;
|
||||
return SerializedLedgerEntry::pointer();
|
||||
}
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer Ledger::getNickname(LedgerStateParms& parms, const std::string& nickname)
|
||||
{
|
||||
return getNickname(parms, Serializer::getSHA512Half(nickname));
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer Ledger::getNickname(LedgerStateParms& parms, const uint256& nickHash)
|
||||
{
|
||||
ScopedLock l(mAccountStateMap->Lock());
|
||||
|
||||
try
|
||||
{
|
||||
return getASNode(parms, nickHash, ltNICKNAME);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
parms = lepERROR;
|
||||
return SerializedLedgerEntry::pointer();
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "AccountState.h"
|
||||
|
||||
class LocalAccountFamily;
|
||||
|
||||
class LocalAccount
|
||||
@@ -21,11 +23,6 @@ protected:
|
||||
boost::shared_ptr<LocalAccountFamily> mFamily;
|
||||
int mAccountFSeq;
|
||||
|
||||
// local usage tracking
|
||||
uint64 mLgrBalance; // The balance, from the last ledger
|
||||
int64 mTxnDelta; // The balance changes from local/pending transactions
|
||||
uint32 mTxnSeq; // The sequence number of the next transaction
|
||||
|
||||
public:
|
||||
LocalAccount(boost::shared_ptr<LocalAccountFamily> family, int accountSeq);
|
||||
|
||||
@@ -48,16 +45,8 @@ public:
|
||||
CKey::pointer getPrivateKey();
|
||||
Json::Value getJson() const;
|
||||
|
||||
void update(uint64 balance, uint32 seq);
|
||||
uint32 getTxnSeq() const { return mTxnSeq; }
|
||||
uint32 incTxnSeq() { return mTxnSeq++; }
|
||||
|
||||
int64 getEffectiveBalance() const { return static_cast<int64_t>(mLgrBalance)+mTxnDelta; }
|
||||
void credit(uint64 amount) { mTxnDelta+=amount; }
|
||||
void debit(uint64 amount) { mTxnDelta-=amount; }
|
||||
void setLedgerBalance(uint64_t lb) { mLgrBalance=lb; if(mTxnSeq==0) mTxnSeq=1; }
|
||||
|
||||
void syncLedger();
|
||||
AccountState::pointer getAccountState() const;
|
||||
uint64 getEffectiveBalance() const;
|
||||
};
|
||||
|
||||
class LocalAccountFamily : public boost::enable_shared_from_this<LocalAccountFamily>
|
||||
|
||||
@@ -38,10 +38,10 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
|
||||
return trans;
|
||||
}
|
||||
|
||||
Ledger::TransResult r=theApp->getMasterLedger().getCurrentLedger()->applyTransaction(trans);
|
||||
if(r==Ledger::TR_ERROR) throw Fault(IO_ERROR);
|
||||
TransactionEngineResult r=theApp->getMasterLedger().doTransaction(*trans->getSTransaction(), tepNONE);
|
||||
if(r==terFAILED) throw Fault(IO_ERROR);
|
||||
|
||||
if((r==Ledger::TR_PREASEQ) || (r==Ledger::TR_BADLSEQ))
|
||||
if(r == terPRE_SEQ)
|
||||
{ // transaction should be held
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Transaction should be held" << std::endl;
|
||||
@@ -51,7 +51,7 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
|
||||
theApp->getMasterLedger().addHeldTransaction(trans);
|
||||
return trans;
|
||||
}
|
||||
if( (r==Ledger::TR_PASTASEQ) || (r==Ledger::TR_ALREADY) )
|
||||
if ( (r==terPAST_SEQ || r==terPAST_LEDGER) )
|
||||
{ // duplicate or conflict
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Transaction is obsolete" << std::endl;
|
||||
@@ -60,14 +60,18 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
|
||||
return trans;
|
||||
}
|
||||
|
||||
if(r==Ledger::TR_SUCCESS)
|
||||
if(r==terSUCCESS)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Transaction is now included, synching to wallet" << std::endl;
|
||||
#endif
|
||||
trans->setStatus(INCLUDED);
|
||||
theApp->getMasterTransaction().canonicalize(trans, true);
|
||||
theApp->getWallet().applyTransaction(trans);
|
||||
|
||||
// FIXME: Need code to get all accounts affected by a transaction and re-synch
|
||||
// any of them that affect local accounts cached in memory. Or, we need to
|
||||
// no cache the account balance information and always get it from the current ledger
|
||||
// theApp->getWallet().applyTransaction(trans);
|
||||
|
||||
newcoin::TMTransaction *tx=new newcoin::TMTransaction();
|
||||
|
||||
|
||||
11
src/Peer.cpp
11
src/Peer.cpp
@@ -339,14 +339,13 @@ void Peer::recvTransaction(newcoin::TMTransaction& packet)
|
||||
Transaction::pointer tx;
|
||||
try
|
||||
{
|
||||
std::string rawTx=packet.rawtransaction();
|
||||
std::string rawTx = packet.rawtransaction();
|
||||
Serializer s(std::vector<unsigned char>(rawTx.begin(), rawTx.end()));
|
||||
SerializerIterator sit(s);
|
||||
SerializedTransaction::pointer stx=boost::make_shared<SerializedTransaction>(boost::ref(sit), -1);
|
||||
SerializedTransaction::pointer stx = boost::make_shared<SerializedTransaction>(boost::ref(sit), -1);
|
||||
|
||||
if(stx->getTxnType()!=ttMAKE_PAYMENT) throw(0); // FIXME to support other transaction
|
||||
tx=boost::make_shared<Transaction>(stx, true);
|
||||
if(tx->getStatus()==INVALID) throw(0);
|
||||
tx = boost::make_shared<Transaction>(stx, true);
|
||||
if (tx->getStatus() == INVALID) throw(0);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -358,7 +357,7 @@ void Peer::recvTransaction(newcoin::TMTransaction& packet)
|
||||
return;
|
||||
}
|
||||
|
||||
tx=theApp->getOPs().processTransaction(tx, this);
|
||||
tx = theApp->getOPs().processTransaction(tx, this);
|
||||
|
||||
if(tx->getStatus()!=INCLUDED)
|
||||
{ // transaction wasn't accepted into ledger
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
|
||||
#include "PeerDoor.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
//#include <boost/log/trivial.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
|
||||
@@ -155,10 +155,6 @@ SHAMapItem::SHAMapItem(const uint256& tag, const std::vector<unsigned char>& dat
|
||||
: mTag(tag), mData(data)
|
||||
{ ; }
|
||||
|
||||
SHAMapItem::SHAMapItem(const uint160& tag, const std::vector<unsigned char>& data)
|
||||
: mTag(tag.to256()), mData(data)
|
||||
{ ; }
|
||||
|
||||
SHAMapItem::pointer SHAMap::firstBelow(SHAMapTreeNode::pointer node)
|
||||
{
|
||||
// Return the first item below this node
|
||||
|
||||
@@ -95,13 +95,10 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
// for transactions
|
||||
SHAMapItem(const uint256& tag) : mTag(tag) { ; }
|
||||
SHAMapItem(const uint256& tag, const std::vector<unsigned char>& data);
|
||||
SHAMapItem(const std::vector<unsigned char>& data); // tag by hash
|
||||
|
||||
// for account balances
|
||||
SHAMapItem(const uint160& tag, const std::vector<unsigned char>& data);
|
||||
|
||||
const uint256& getTag() const { return mTag; }
|
||||
std::vector<unsigned char> getData() const { return mData.getData(); }
|
||||
const std::vector<unsigned char>& peekData() const { return mData.peekData(); }
|
||||
@@ -287,9 +284,6 @@ public:
|
||||
SHAMapItem::pointer peekNextItem(const uint256&);
|
||||
SHAMapItem::pointer peekPrevItem(const uint256&);
|
||||
|
||||
SHAMapItem::pointer peekPrevItem(const uint160& u) { return peekPrevItem(u.to256()); }
|
||||
SHAMapItem::pointer peekNextItem(const uint160& u) { return peekNextItem(u.to256()); }
|
||||
|
||||
// comparison/sync functions
|
||||
void getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max);
|
||||
bool getNodeFat(const SHAMapNode& node, std::vector<SHAMapNode>& nodeIDs,
|
||||
|
||||
@@ -192,9 +192,9 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned
|
||||
}
|
||||
else if(type==1)
|
||||
{ // account state
|
||||
uint160 u;
|
||||
s.get160(u, len-20);
|
||||
s.chop(20);
|
||||
uint256 u;
|
||||
s.get256(u, len-32);
|
||||
s.chop(256/8);
|
||||
if(u.isZero()) throw SHAMapException(InvalidNode);
|
||||
mItem=boost::make_shared<SHAMapItem>(u, s.peekData());
|
||||
mType=tnACCOUNT_STATE;
|
||||
@@ -236,7 +236,7 @@ void SHAMapTreeNode::addRaw(Serializer &s)
|
||||
if(mType==tnACCOUNT_STATE)
|
||||
{
|
||||
mItem->addRaw(s);
|
||||
s.add160(mItem->getTag().to160());
|
||||
s.add256(mItem->getTag());
|
||||
s.add8(1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ static SHAMapItem::pointer makeRandomAS()
|
||||
Serializer s;
|
||||
for(int d=0; d<3; d++)
|
||||
s.add32(rand());
|
||||
return boost::make_shared<SHAMapItem>(s.getRIPEMD160(), s.peekData());
|
||||
return boost::make_shared<SHAMapItem>(s.getRIPEMD160().to256(), s.peekData());
|
||||
}
|
||||
|
||||
static bool confuseMap(SHAMap &map, int count)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/interprocess/sync/scoped_lock.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
|
||||
// A lock holder that can be returned and copied by value
|
||||
// When the last reference goes away, the lock is released
|
||||
|
||||
class ScopedLock
|
||||
{
|
||||
@@ -11,8 +16,8 @@ protected:
|
||||
mutable boost::shared_ptr<boost::interprocess::scoped_lock<boost::recursive_mutex> > mHolder;
|
||||
|
||||
public:
|
||||
ScopedLock(boost::recursive_mutex &mutex) :
|
||||
mHolder(new boost::interprocess::scoped_lock<boost::recursive_mutex>(mutex))
|
||||
ScopedLock(boost::recursive_mutex& mutex) :
|
||||
mHolder(boost::make_shared<boost::interprocess::scoped_lock<boost::recursive_mutex> >(boost::ref(mutex)))
|
||||
{ ; }
|
||||
void lock() const
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "SerializedObject.h"
|
||||
#include "LedgerFormats.h"
|
||||
#include "NewcoinAddress.h"
|
||||
|
||||
class SerializedLedgerEntry : public STObject
|
||||
{
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
uint256 getIFieldH256(SOE_Field field) const { return mObject.getValueFieldH256(field); }
|
||||
std::vector<unsigned char> getIFieldVL(SOE_Field field) const { return mObject.getValueFieldVL(field); }
|
||||
std::vector<TaggedListItem> getIFieldTL(SOE_Field field) const { return mObject.getValueFieldTL(field); }
|
||||
NewcoinAddress getIValueFieldAccount(SOE_Field field) const { return mObject.getValueFieldAccount(field); }
|
||||
void setIFieldU8(SOE_Field field, unsigned char v) { return mObject.setValueFieldU8(field, v); }
|
||||
void setIFieldU16(SOE_Field field, uint16 v) { return mObject.setValueFieldU16(field, v); }
|
||||
void setIFieldU32(SOE_Field field, uint32 v) { return mObject.setValueFieldU32(field, v); }
|
||||
@@ -60,12 +62,14 @@ public:
|
||||
{ return mObject.setValueFieldVL(field, v); }
|
||||
void setIFieldTL(SOE_Field field, const std::vector<TaggedListItem>& v)
|
||||
{ return mObject.setValueFieldTL(field, v); }
|
||||
void setIFieldAccount(SOE_Field field, const uint160& account)
|
||||
{ return mObject.setValueFieldAccount(field, account); }
|
||||
void setIFieldAccount(SOE_Field field, const NewcoinAddress& account)
|
||||
{ return mObject.setValueFieldAccount(field, account); }
|
||||
|
||||
bool getIFieldPresent(SOE_Field field) const { return mObject.isFieldPresent(field); }
|
||||
void makeIFieldPresent(SOE_Field field) { return mObject.makeFieldPresent(field); }
|
||||
void makeIFieldAbsent(SOE_Field field) { return mObject.makeFieldAbsent(field); }
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -350,6 +350,17 @@ uint256 STObject::getValueFieldH256(SOE_Field field) const
|
||||
return cf->getValue();
|
||||
}
|
||||
|
||||
NewcoinAddress STObject::getValueFieldAccount(SOE_Field field) const
|
||||
{
|
||||
const SerializedType* rf = peekAtPField(field);
|
||||
if (!rf) throw std::runtime_error("Field not found");
|
||||
SerializedTypeID id = rf->getSType();
|
||||
if (id == STI_OBJECT) return NewcoinAddress(); // optional field not present
|
||||
const STAccount* cf = dynamic_cast<const STAccount *>(rf);
|
||||
if (!cf) throw std::runtime_error("Wrong field type");
|
||||
return cf->getValueNCA();
|
||||
}
|
||||
|
||||
std::vector<unsigned char> STObject::getValueFieldVL(SOE_Field field) const
|
||||
{
|
||||
const SerializedType* rf = peekAtPField(field);
|
||||
@@ -452,6 +463,22 @@ void STObject::setValueFieldH160(SOE_Field field, const uint160& v)
|
||||
cf->setValue(v);
|
||||
}
|
||||
|
||||
void STObject::setValueFieldAccount(SOE_Field field, const uint160& v)
|
||||
{
|
||||
SerializedType* rf = getPField(field);
|
||||
if (!rf) throw std::runtime_error("Field not found");
|
||||
SerializedTypeID id = rf->getSType();
|
||||
if (id == STI_OBJECT)
|
||||
{
|
||||
makeFieldPresent(field);
|
||||
rf = getPField(field);
|
||||
id = rf->getSType();
|
||||
}
|
||||
STAccount* cf = dynamic_cast<STAccount*>(rf);
|
||||
if (!cf) throw std::runtime_error("Wrong field type");
|
||||
cf->setValueH160(v);
|
||||
}
|
||||
|
||||
void STObject::setValueFieldVL(SOE_Field field, const std::vector<unsigned char>& v)
|
||||
{
|
||||
SerializedType* rf = getPField(field);
|
||||
|
||||
@@ -102,6 +102,7 @@ public:
|
||||
uint64 getValueFieldU64(SOE_Field field) const;
|
||||
uint160 getValueFieldH160(SOE_Field field) const;
|
||||
uint256 getValueFieldH256(SOE_Field field) const;
|
||||
NewcoinAddress getValueFieldAccount(SOE_Field field) const;
|
||||
std::vector<unsigned char> getValueFieldVL(SOE_Field field) const;
|
||||
std::vector<TaggedListItem> getValueFieldTL(SOE_Field field) const;
|
||||
|
||||
@@ -113,6 +114,9 @@ public:
|
||||
void setValueFieldH256(SOE_Field field, const uint256&);
|
||||
void setValueFieldVL(SOE_Field field, const std::vector<unsigned char>&);
|
||||
void setValueFieldTL(SOE_Field field, const std::vector<TaggedListItem>&);
|
||||
void setValueFieldAccount(SOE_Field field, const uint160&);
|
||||
void setValueFieldAccount(SOE_Field field, const NewcoinAddress& addr)
|
||||
{ setValueFieldAccount(field, addr.getAccountID()); }
|
||||
|
||||
bool isFieldPresent(SOE_Field field) const;
|
||||
void makeFieldPresent(SOE_Field field);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
#include "SerializedTransaction.h"
|
||||
|
||||
SerializedTransaction::SerializedTransaction(TransactionType type)
|
||||
SerializedTransaction::SerializedTransaction(TransactionType type) : mType(type)
|
||||
{
|
||||
mFormat=getTxnFormat(type);
|
||||
mFormat = getTxnFormat(type);
|
||||
if (mFormat == NULL) throw std::runtime_error("invalid transaction type");
|
||||
|
||||
mMiddleTxn.giveObject(new STUInt32("Magic", TransactionMagic));
|
||||
mMiddleTxn.giveObject(new STVariableLength("SigningAccount"));
|
||||
mMiddleTxn.giveObject(new STVariableLength("SigningPubKey"));
|
||||
mMiddleTxn.giveObject(new STUInt32("Sequence"));
|
||||
mMiddleTxn.giveObject(new STUInt8("Type", static_cast<unsigned char>(type)));
|
||||
mMiddleTxn.giveObject(new STUInt64("Fee"));
|
||||
@@ -28,16 +28,24 @@ SerializedTransaction::SerializedTransaction(SerializerIterator& sit, int length
|
||||
throw std::runtime_error("Transaction has invalid magic");
|
||||
|
||||
mMiddleTxn.giveObject(new STUInt32("Magic", TransactionMagic));
|
||||
mMiddleTxn.giveObject(new STVariableLength("SigningAccount", sit.getVL()));
|
||||
mMiddleTxn.giveObject(new STVariableLength("SigningPubKey", sit.getVL()));
|
||||
mMiddleTxn.giveObject(new STUInt32("Sequence", sit.get32()));
|
||||
|
||||
int type = sit.get32();
|
||||
mMiddleTxn.giveObject(new STUInt32("Type", type));
|
||||
mFormat = getTxnFormat(static_cast<TransactionType>(type));
|
||||
mType = static_cast<TransactionType>(sit.get32());
|
||||
mMiddleTxn.giveObject(new STUInt32("Type", static_cast<uint32>(mType)));
|
||||
mFormat = getTxnFormat(mType);
|
||||
if (!mFormat) throw std::runtime_error("Transaction has invalid type");
|
||||
mMiddleTxn.giveObject(new STUInt64("Fee", sit.get64()));
|
||||
|
||||
mInnerTxn = STObject(mFormat->elements, sit, "InnerTransaction");
|
||||
updateSourceAccount();
|
||||
}
|
||||
|
||||
void SerializedTransaction::updateSourceAccount()
|
||||
{
|
||||
NewcoinAddress a;
|
||||
a.setAccountPublic(peekSigningPubKey());
|
||||
mSourceAccount.setAccountID(a.getAccountID());
|
||||
}
|
||||
|
||||
int SerializedTransaction::getLength() const
|
||||
@@ -67,6 +75,21 @@ std::string SerializedTransaction::getText() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<NewcoinAddress> SerializedTransaction::getAffectedAccounts() const
|
||||
{
|
||||
std::vector<NewcoinAddress> accounts;
|
||||
accounts.push_back(mSourceAccount);
|
||||
|
||||
for(boost::ptr_vector<SerializedType>::const_iterator it = mInnerTxn.peekData().begin(),
|
||||
end = mInnerTxn.peekData().end(); it != end ; ++it)
|
||||
{
|
||||
const STAccount* sa = dynamic_cast<const STAccount*>(&*it);
|
||||
if (sa != NULL) // FIXME: Should we check for duplicates?
|
||||
accounts.push_back(sa->getValueNCA());
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
int SerializedTransaction::getTransaction(Serializer& s, bool include_length) const
|
||||
{
|
||||
int l = getLength();
|
||||
@@ -81,7 +104,7 @@ bool SerializedTransaction::isEquivalent(const SerializedType& t) const
|
||||
{ // Signatures are not compared
|
||||
const SerializedTransaction* v = dynamic_cast<const SerializedTransaction*>(&t);
|
||||
if (!v) return false;
|
||||
if (type != v->type) return false;
|
||||
if (mType != v->mType) return false;
|
||||
if (mMiddleTxn != v->mMiddleTxn) return false;
|
||||
if (mInnerTxn != v->mInnerTxn) return false;
|
||||
return true;
|
||||
@@ -171,34 +194,48 @@ void SerializedTransaction::setSequence(uint32 seq)
|
||||
v->setValue(seq);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> SerializedTransaction::getSigningAccount() const
|
||||
std::vector<unsigned char> SerializedTransaction::getSigningPubKey() const
|
||||
{
|
||||
const STVariableLength* v =
|
||||
dynamic_cast<const STVariableLength*>(mMiddleTxn.peekAtPIndex(TransactionISigningAccount));
|
||||
dynamic_cast<const STVariableLength*>(mMiddleTxn.peekAtPIndex(TransactionISigningPubKey));
|
||||
if (!v) throw std::runtime_error("corrupt transaction");
|
||||
return v->getValue();
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& SerializedTransaction::peekSigningAccount() const
|
||||
const std::vector<unsigned char>& SerializedTransaction::peekSigningPubKey() const
|
||||
{
|
||||
const STVariableLength* v=
|
||||
dynamic_cast<const STVariableLength*>(mMiddleTxn.peekAtPIndex(TransactionISigningAccount));
|
||||
dynamic_cast<const STVariableLength*>(mMiddleTxn.peekAtPIndex(TransactionISigningPubKey));
|
||||
if (!v) throw std::runtime_error("corrupt transaction");
|
||||
return v->peekValue();
|
||||
}
|
||||
|
||||
std::vector<unsigned char>& SerializedTransaction::peekSigningAccount()
|
||||
std::vector<unsigned char>& SerializedTransaction::peekSigningPubKey()
|
||||
{
|
||||
STVariableLength* v = dynamic_cast<STVariableLength*>(mMiddleTxn.getPIndex(TransactionISigningAccount));
|
||||
STVariableLength* v = dynamic_cast<STVariableLength*>(mMiddleTxn.getPIndex(TransactionISigningPubKey));
|
||||
if (!v) throw std::runtime_error("corrupt transaction");
|
||||
return v->peekValue();
|
||||
}
|
||||
|
||||
void SerializedTransaction::setSigningAccount(const std::vector<unsigned char>& s)
|
||||
const NewcoinAddress& SerializedTransaction::setSigningPubKey(const std::vector<unsigned char>& s)
|
||||
{
|
||||
STVariableLength* v = dynamic_cast<STVariableLength*>(mMiddleTxn.getPIndex(TransactionISigningAccount));
|
||||
STVariableLength* v = dynamic_cast<STVariableLength*>(mMiddleTxn.getPIndex(TransactionISigningPubKey));
|
||||
if (!v) throw std::runtime_error("corrupt transaction");
|
||||
v->setValue(s);
|
||||
updateSourceAccount();
|
||||
return mSourceAccount;
|
||||
}
|
||||
|
||||
uint160 SerializedTransaction::getITFieldAccount(SOE_Field field) const
|
||||
{
|
||||
uint160 r;
|
||||
const SerializedType* st = mInnerTxn.peekAtPField(field);
|
||||
if (!st) return r;
|
||||
|
||||
const STAccount* ac = dynamic_cast<const STAccount*>(st);
|
||||
if (!ac) return r;
|
||||
ac->getValueH160(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
int SerializedTransaction::getITFieldIndex(SOE_Field field) const
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "uint256.h"
|
||||
#include "SerializedObject.h"
|
||||
#include "TransactionFormats.h"
|
||||
#include "NewcoinAddress.h"
|
||||
|
||||
class SerializedTransaction : public STObject
|
||||
{
|
||||
@@ -15,11 +16,14 @@ public:
|
||||
typedef boost::shared_ptr<SerializedTransaction> pointer;
|
||||
|
||||
protected:
|
||||
TransactionType type;
|
||||
NewcoinAddress mSourceAccount;
|
||||
TransactionType mType;
|
||||
STVariableLength mSignature;
|
||||
STObject mMiddleTxn, mInnerTxn;
|
||||
TransactionFormat* mFormat;
|
||||
|
||||
void updateSourceAccount();
|
||||
|
||||
public:
|
||||
SerializedTransaction(SerializerIterator& sit, int length); // -1=all remaining, 0=get from sit
|
||||
SerializedTransaction(TransactionType type);
|
||||
@@ -42,13 +46,16 @@ public:
|
||||
// middle transaction functions
|
||||
uint32 getVersion() const;
|
||||
void setVersion(uint32);
|
||||
TransactionType getTxnType() const { return type; }
|
||||
|
||||
TransactionType getTxnType() const { return mType; }
|
||||
uint64 getTransactionFee() const;
|
||||
void setTransactionFee(uint64);
|
||||
std::vector<unsigned char> getSigningAccount() const;
|
||||
const std::vector<unsigned char>& peekSigningAccount() const;
|
||||
std::vector<unsigned char>& peekSigningAccount();
|
||||
void setSigningAccount(const std::vector<unsigned char>& s);
|
||||
|
||||
const NewcoinAddress& getSourceAccount() const { return mSourceAccount; }
|
||||
std::vector<unsigned char> getSigningPubKey() const;
|
||||
const std::vector<unsigned char>& peekSigningPubKey() const;
|
||||
std::vector<unsigned char>& peekSigningPubKey();
|
||||
const NewcoinAddress& setSigningPubKey(const std::vector<unsigned char>& s);
|
||||
std::string getTransactionType() const { return mFormat->t_name; }
|
||||
|
||||
// inner transaction functions
|
||||
@@ -72,6 +79,7 @@ public:
|
||||
uint32 getITFieldU32(SOE_Field field) const { return mInnerTxn.getValueFieldU32(field); }
|
||||
uint64 getITFieldU64(SOE_Field field) const { return mInnerTxn.getValueFieldU64(field); }
|
||||
uint160 getITFieldH160(SOE_Field field) const { return mInnerTxn.getValueFieldH160(field); }
|
||||
uint160 getITFieldAccount(SOE_Field field) const;
|
||||
uint256 getITFieldH256(SOE_Field field) const { return mInnerTxn.getValueFieldH256(field); }
|
||||
std::vector<unsigned char> getITFieldVL(SOE_Field field) const { return mInnerTxn.getValueFieldVL(field); }
|
||||
std::vector<TaggedListItem> getITFieldTL(SOE_Field field) const { return mInnerTxn.getValueFieldTL(field); }
|
||||
@@ -85,12 +93,18 @@ public:
|
||||
{ return mInnerTxn.setValueFieldVL(field, v); }
|
||||
void setITFieldTL(SOE_Field field, const std::vector<TaggedListItem>& v)
|
||||
{ return mInnerTxn.setValueFieldTL(field, v); }
|
||||
void setITFieldAccount(SOE_Field field, const uint160& v)
|
||||
{ return mInnerTxn.setValueFieldAccount(field, v); }
|
||||
void setITFieldAccount(SOE_Field field, const NewcoinAddress& v)
|
||||
{ return mInnerTxn.setValueFieldAccount(field, v); }
|
||||
|
||||
// optional field functions
|
||||
bool getITFieldPresent(SOE_Field field) const;
|
||||
void makeITFieldPresent(SOE_Field field);
|
||||
void makeITFieldAbsent(SOE_Field field);
|
||||
|
||||
std::vector<NewcoinAddress> getAffectedAccounts() const;
|
||||
|
||||
// whole transaction functions
|
||||
int getTransaction(Serializer& s, bool include_length) const;
|
||||
uint256 getTransactionID() const;
|
||||
|
||||
@@ -199,10 +199,24 @@ void STAccount::setValueH160(const uint160& v)
|
||||
bool STAccount::getValueH160(uint160& v) const
|
||||
{
|
||||
if (!isValueH160()) return false;
|
||||
memcpy(v.begin(), &(peekValue().front()), 32);
|
||||
memcpy(v.begin(), &(peekValue().front()), 20);
|
||||
return true;
|
||||
}
|
||||
|
||||
NewcoinAddress STAccount::getValueNCA() const
|
||||
{
|
||||
NewcoinAddress a;
|
||||
uint160 v;
|
||||
if (getValueH160(v))
|
||||
a.setAccountID(v);
|
||||
return a;
|
||||
}
|
||||
|
||||
void STAccount::setValueNCA(const NewcoinAddress& nca)
|
||||
{
|
||||
setValueH160(nca.getAccountID());
|
||||
}
|
||||
|
||||
std::string STTaggedList::getText() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
@@ -362,6 +362,9 @@ public:
|
||||
virtual STAccount* duplicate() const { return new STAccount(name, value); }
|
||||
std::string getText() const;
|
||||
|
||||
NewcoinAddress getValueNCA() const;
|
||||
void setValueNCA(const NewcoinAddress& nca);
|
||||
|
||||
void setValueH160(const uint160& v);
|
||||
bool getValueH160(uint160&) const;
|
||||
bool isValueH160() const;
|
||||
|
||||
@@ -14,69 +14,66 @@
|
||||
Transaction::Transaction(LocalAccount::pointer fromLocalAccount, const NewcoinAddress& toAccount, uint64 amount,
|
||||
uint32 ident, uint32 ledger) : mInLedger(0), mStatus(NEW)
|
||||
{
|
||||
mAccountFrom=fromLocalAccount->getAddress();
|
||||
mAccountTo=toAccount;
|
||||
AccountState::pointer accountState = fromLocalAccount->getAccountState();
|
||||
if (!accountState) throw std::runtime_error("transaction on non-existent account");
|
||||
|
||||
mTransaction=boost::make_shared<SerializedTransaction>(ttMAKE_PAYMENT);
|
||||
mAccountFrom = fromLocalAccount->getAddress();
|
||||
|
||||
mFromPubKey=fromLocalAccount->getPublicKey();
|
||||
mTransaction = boost::make_shared<SerializedTransaction>(ttMAKE_PAYMENT);
|
||||
|
||||
mFromPubKey = fromLocalAccount->getPublicKey();
|
||||
assert(mFromPubKey);
|
||||
mTransaction->setSigningAccount(mFromPubKey->GetPubKey());
|
||||
mTransaction->setSigningPubKey(mFromPubKey->GetPubKey());
|
||||
|
||||
mTransaction->setSequence(accountState->getSeq());
|
||||
assert(mTransaction->getSequence() != 0);
|
||||
|
||||
mTransaction->setSequence(fromLocalAccount->getTxnSeq());
|
||||
assert(mTransaction->getSequence()!=0);
|
||||
mTransaction->setTransactionFee(100); // for now
|
||||
|
||||
mTransaction->setITFieldVL(sfDestination, toAccount.getAccountPublic());
|
||||
mTransaction->setITFieldAccount(sfDestination, toAccount);
|
||||
mTransaction->setITFieldU64(sfAmount, amount);
|
||||
if(ledger!=0)
|
||||
if (ledger != 0)
|
||||
{
|
||||
mTransaction->makeITFieldPresent(sfTargetLedger);
|
||||
mTransaction->setITFieldU32(sfTargetLedger, ledger);
|
||||
}
|
||||
if(ident!=0)
|
||||
if (ident != 0)
|
||||
{
|
||||
mTransaction->makeITFieldPresent(sfSourceTag);
|
||||
mTransaction->setITFieldU32(sfSourceTag, ident);
|
||||
}
|
||||
|
||||
assert(mFromPubKey);
|
||||
if(!sign(fromLocalAccount))
|
||||
if (!sign(fromLocalAccount))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Unable to sign transaction" << std::endl;
|
||||
#endif
|
||||
mStatus=INCOMPLETE;
|
||||
mStatus = INCOMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
Transaction::Transaction(SerializedTransaction::pointer sit, bool validate) : mStatus(INVALID), mTransaction(sit)
|
||||
{
|
||||
uint160 toAccountID;
|
||||
uint160 fromAccountID;
|
||||
std::vector<unsigned char> pubKey;
|
||||
|
||||
try
|
||||
{
|
||||
toAccountID=mTransaction->getITFieldH160(sfDestination);
|
||||
pubKey=mTransaction->getSigningAccount();
|
||||
mTransactionID=mTransaction->getTransactionID();
|
||||
pubKey = mTransaction->peekSigningPubKey();
|
||||
mTransactionID = mTransaction->getTransactionID();
|
||||
mAccountFrom = mTransaction->getSourceAccount();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mAccountTo.setAccountID(toAccountID);
|
||||
mAccountFrom.setAccountID(fromAccountID);
|
||||
mFromPubKey = boost::make_shared<CKey>();
|
||||
if (!mFromPubKey->SetPubKey(pubKey)) return;
|
||||
mFromPubKey = theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
|
||||
|
||||
mFromPubKey=boost::make_shared<CKey>();
|
||||
if(!mFromPubKey->SetPubKey(pubKey)) return;
|
||||
mAccountFrom.setAccountPublic(pubKey);
|
||||
mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
|
||||
|
||||
if(!validate || checkSign())
|
||||
mStatus=NEW;
|
||||
if (!validate || checkSign())
|
||||
mStatus = NEW;
|
||||
}
|
||||
|
||||
Transaction::Transaction(const std::vector<unsigned char>& raw, bool validate) : mStatus(INVALID)
|
||||
@@ -89,51 +86,52 @@ Transaction::Transaction(const std::vector<unsigned char>& raw, bool validate) :
|
||||
{
|
||||
Serializer s(raw);
|
||||
SerializerIterator sit(s);
|
||||
mTransaction=boost::make_shared<SerializedTransaction>(boost::ref(sit), -1);
|
||||
mTransaction = boost::make_shared<SerializedTransaction>(boost::ref(sit), -1);
|
||||
|
||||
mFromPubKey=boost::make_shared<CKey>();
|
||||
if(!mFromPubKey->SetPubKey(pubKey)) return;
|
||||
mFromPubKey = boost::make_shared<CKey>();
|
||||
if (!mFromPubKey->SetPubKey(pubKey)) return;
|
||||
mAccountFrom.setAccountPublic(pubKey);
|
||||
mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
|
||||
if(!mFromPubKey->SetPubKey(pubKey)) return;
|
||||
mFromPubKey = theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
|
||||
if (!mFromPubKey->SetPubKey(pubKey)) return;
|
||||
mAccountFrom.setAccountPublic(pubKey);
|
||||
mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
|
||||
mFromPubKey = theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
|
||||
}
|
||||
catch(...)
|
||||
catch (...)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!validate || checkSign())
|
||||
mStatus=NEW;
|
||||
if (!validate || checkSign())
|
||||
mStatus = NEW;
|
||||
}
|
||||
|
||||
Transaction::Transaction(const NewcoinAddress& fromID, const NewcoinAddress& toID,
|
||||
CKey::pointer pubKey, uint64 amount, uint64 fee, uint32 fromSeq, uint32 fromLedger,
|
||||
uint32 ident, const std::vector<unsigned char>& signature, uint32 ledgerSeq, TransStatus st) :
|
||||
mAccountFrom(fromID), mAccountTo(toID), mFromPubKey(pubKey), mInLedger(ledgerSeq), mStatus(st)
|
||||
mAccountFrom(fromID), mFromPubKey(pubKey), mInLedger(ledgerSeq), mStatus(st)
|
||||
{
|
||||
mTransaction=boost::make_shared<SerializedTransaction>(ttMAKE_PAYMENT);
|
||||
mTransaction = boost::make_shared<SerializedTransaction>(ttMAKE_PAYMENT);
|
||||
mTransaction->setSignature(signature);
|
||||
mTransaction->setTransactionFee(fee);
|
||||
mTransaction->setSigningAccount(pubKey->GetPubKey());
|
||||
mTransaction->setSigningPubKey(pubKey->GetPubKey());
|
||||
mTransaction->setSequence(fromSeq);
|
||||
if(fromLedger!=0)
|
||||
if (fromLedger != 0)
|
||||
{
|
||||
mTransaction->makeITFieldPresent(sfTargetLedger);
|
||||
mTransaction->setITFieldU32(sfTargetLedger, fromLedger);
|
||||
}
|
||||
if(ident!=0)
|
||||
if (ident != 0)
|
||||
{
|
||||
mTransaction->makeITFieldPresent(sfSourceTag);
|
||||
mTransaction->setITFieldU32(sfSourceTag, ident);
|
||||
}
|
||||
mTransaction->setValueFieldU64(sfAmount, amount);
|
||||
mTransaction->setValueFieldAccount(sfDestination, toID.getAccountID());
|
||||
updateID();
|
||||
}
|
||||
|
||||
bool Transaction::sign(LocalAccount::pointer fromLocalAccount)
|
||||
{
|
||||
CKey::pointer privateKey=fromLocalAccount->getPrivateKey();
|
||||
CKey::pointer privateKey = fromLocalAccount->getPrivateKey();
|
||||
if(!privateKey)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@@ -142,7 +140,7 @@ bool Transaction::sign(LocalAccount::pointer fromLocalAccount)
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (mTransaction->getITFieldU64(sfAmount)==0) || !mAccountTo.IsValid() )
|
||||
if( (mTransaction->getITFieldU64(sfAmount)==0) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Bad amount or destination" << std::endl;
|
||||
@@ -355,7 +353,9 @@ bool Transaction::isHexTxID(const std::string& txid)
|
||||
Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const
|
||||
{
|
||||
Json::Value ret(mTransaction->getJson(0));
|
||||
|
||||
if(mInLedger) ret["InLedger"]=mInLedger;
|
||||
if(paid) ret["Paid"]=true;
|
||||
|
||||
switch(mStatus)
|
||||
{
|
||||
@@ -371,12 +371,7 @@ Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const
|
||||
default: ret["Status"]="unknown";
|
||||
}
|
||||
|
||||
Json::Value source(Json::objectValue);
|
||||
source["AccountID"]=mAccountFrom.humanAccountID();
|
||||
|
||||
Json::Value destination(Json::objectValue);
|
||||
destination["AccountID"]=mAccountTo.humanAccountID();
|
||||
|
||||
#if 0
|
||||
if(decorate)
|
||||
{
|
||||
LocalAccount::pointer lac=theApp->getWallet().getLocalAccount(mAccountFrom);
|
||||
@@ -384,9 +379,7 @@ Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const
|
||||
lac=theApp->getWallet().getLocalAccount(mAccountTo);
|
||||
if(!!lac) destination=lac->getJson();
|
||||
}
|
||||
if(paid) source["Paid"]=true;
|
||||
if(credited) destination["Credited"]=true;
|
||||
ret["Source"]=source;
|
||||
ret["Destination"]=destination;
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
|
||||
private:
|
||||
uint256 mTransactionID;
|
||||
NewcoinAddress mAccountFrom, mAccountTo;
|
||||
NewcoinAddress mAccountFrom;
|
||||
CKey::pointer mFromPubKey;
|
||||
|
||||
uint32 mInLedger;
|
||||
@@ -66,7 +66,6 @@ public:
|
||||
|
||||
const uint256& getID() const { return mTransactionID; }
|
||||
const NewcoinAddress& getFromAccount() const { return mAccountFrom; }
|
||||
const NewcoinAddress& getToAccount() const { return mAccountTo; }
|
||||
uint64 getAmount() const { return mTransaction->getITFieldU64(sfAmount); }
|
||||
uint64 getFee() const { return mTransaction->getTransactionFee(); }
|
||||
uint32 getFromAccountSeq() const { return mTransaction->getSequence(); }
|
||||
|
||||
189
src/TransactionEngine.cpp
Normal file
189
src/TransactionEngine.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
|
||||
#include "TransactionEngine.h"
|
||||
|
||||
#include "TransactionFormats.h"
|
||||
|
||||
TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTransaction& txn,
|
||||
TransactionEngineParams params)
|
||||
{
|
||||
uint256 txID = txn.getTransactionID();
|
||||
if(!txID) return terINVALID;
|
||||
|
||||
// extract signing key
|
||||
CKey acctKey;
|
||||
if (!acctKey.SetPubKey(txn.peekSigningPubKey())) return terINVALID;
|
||||
|
||||
// check signature
|
||||
if (!txn.checkSign(acctKey)) return terINVALID;
|
||||
|
||||
uint64 txnFee = txn.getTransactionFee();
|
||||
if ( (params & tepNO_CHECK_FEE) != tepNONE)
|
||||
{
|
||||
// WRITEME: Check if fee is adequate
|
||||
if (txnFee == 0) return terINSUF_FEE_P;
|
||||
}
|
||||
|
||||
// get source account ID
|
||||
uint160 srcAccount = txn.getSourceAccount().getAccountID();
|
||||
if (!srcAccount) return terINVALID;
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLedger->mLock);
|
||||
|
||||
// find source account
|
||||
LedgerStateParms qry = lepNONE;
|
||||
SerializedLedgerEntry::pointer src = mLedger->getAccountRoot(qry, srcAccount);
|
||||
if (!src) return terNO_ACCOUNT;
|
||||
|
||||
// deduct the fee, so it's not available during the transaction
|
||||
// we only write the account back if the transaction succeeds
|
||||
uint64 balance = src->getIFieldU64(sfBalance);
|
||||
if (balance < txnFee)
|
||||
return terINSUF_FEE_B;
|
||||
src->setIFieldU64(sfBalance, balance - txnFee);
|
||||
|
||||
// validate sequence
|
||||
uint32 t_seq = txn.getSequence();
|
||||
uint32 a_seq = src->getIFieldU32(sfSequence);
|
||||
if (t_seq != a_seq)
|
||||
{
|
||||
// WRITEME: Special case code for changing transaction key
|
||||
if (a_seq < t_seq) return terPRE_SEQ;
|
||||
if (mLedger->hasTransaction(txID))
|
||||
return terALREADY;
|
||||
return terPAST_SEQ;
|
||||
}
|
||||
else src->setIFieldU32(sfSequence, t_seq);
|
||||
|
||||
std::vector<AffectedAccount> accounts;
|
||||
accounts.push_back(std::make_pair(taaMODIFY, src));
|
||||
TransactionEngineResult result = terUNKNOWN;
|
||||
switch(txn.getTxnType())
|
||||
{
|
||||
case ttINVALID:
|
||||
result = terINVALID;
|
||||
break;
|
||||
|
||||
case ttMAKE_PAYMENT:
|
||||
result = doPayment(txn, accounts);
|
||||
break;
|
||||
|
||||
case ttINVOICE:
|
||||
result = doInvoice(txn, accounts);
|
||||
break;
|
||||
|
||||
case ttEXCHANGE_OFFER:
|
||||
result = doOffer(txn, accounts);
|
||||
break;
|
||||
|
||||
default:
|
||||
result = terUNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == terSUCCESS)
|
||||
{ // Write back the account states and add the transaction to the ledger
|
||||
// WRITEME: Special case code for changing transaction key
|
||||
for(std::vector<AffectedAccount>::iterator it=accounts.begin(), end=accounts.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
if ( (it->first==taaMODIFY) || (it->first==taaCREATE) )
|
||||
{
|
||||
if(mLedger->writeBack(lepNONE, it->second) & lepERROR)
|
||||
assert(false);
|
||||
}
|
||||
else if (it->first == taaDELETE)
|
||||
{
|
||||
if(!mLedger->peekAccountStateMap()->delItem(it->second->getIndex()))
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Serializer s;
|
||||
txn.add(s);
|
||||
mLedger->addTransaction(txID, s);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
uint32 txFlags = txn.getFlags();
|
||||
uint160 destAccount = txn.getITFieldAccount(sfDestination);
|
||||
|
||||
// Does the destination account exist?
|
||||
if (!destAccount) return terINVALID;
|
||||
LedgerStateParms qry = lepNONE;
|
||||
SerializedLedgerEntry::pointer dest = mLedger->getAccountRoot(qry, destAccount);
|
||||
if (!dest)
|
||||
{ // can this transaction create an account
|
||||
if ((txFlags & 0x00010000) == 0) // no
|
||||
return terNO_TARGET;
|
||||
|
||||
dest = boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT);
|
||||
dest->setIndex(Ledger::getAccountRootIndex(destAccount));
|
||||
dest->setIFieldAccount(sfAccount, destAccount);
|
||||
dest->setIFieldU32(sfSequence, 1);
|
||||
accounts.push_back(std::make_pair(taaCREATE, dest));
|
||||
}
|
||||
else accounts.push_back(std::make_pair(taaMODIFY, dest));
|
||||
|
||||
uint64 amount = txn.getITFieldU64(sfAmount);
|
||||
|
||||
uint160 currency;
|
||||
if(txn.getITFieldPresent(sfCurrency))
|
||||
currency = txn.getITFieldH160(sfCurrency);
|
||||
bool native = !!currency;
|
||||
|
||||
if (native)
|
||||
{
|
||||
uint64 balance = accounts[0].second->getIFieldU64(sfBalance);
|
||||
if (balance < amount) return terUNFUNDED;
|
||||
accounts[0].second->setIFieldU64(sfBalance, balance - amount);
|
||||
accounts[1].second->setIFieldU64(sfBalance, accounts[1].second->getIFieldU64(sfBalance) + amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// WRITEME: Handle non-native currencies, paths
|
||||
return terUNKNOWN;
|
||||
}
|
||||
|
||||
return terSUCCESS;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doInvoice(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
return terUNKNOWN;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doOffer(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
return terUNKNOWN;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doTake(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
return terUNKNOWN;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doCancel(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
return terUNKNOWN;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doStore(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
return terUNKNOWN;
|
||||
}
|
||||
|
||||
TransactionEngineResult TransactionEngine::doDelete(const SerializedTransaction& txn,
|
||||
std::vector<AffectedAccount>& accounts)
|
||||
{
|
||||
return terUNKNOWN;
|
||||
}
|
||||
@@ -12,13 +12,17 @@
|
||||
|
||||
enum TransactionEngineResult
|
||||
{ // <0 = Can never succeed, 0 = success, >0 = failed, but could succeed
|
||||
terFAILED = -4, // Something broke horribly
|
||||
terUNKNOWN = -3, // The transactions requires logic not implemented yet
|
||||
terINSUF_FEE_P = -2, // fee totally insufficient
|
||||
terINVALID = -1, // The transaction is ill-formed
|
||||
terSUCCESS = 0, // The transaction was applied
|
||||
terALREADY, // The transaction was already in the ledger
|
||||
terNO_ACCOUNT, // The source account does not exist
|
||||
terNO_TARGET, // The destination does not exist
|
||||
terINSUF_FEE_T, // fee insufficient now (account doesn't exist, network load)
|
||||
terUNFUNDED, // Source account had insufficient balance
|
||||
terINSUF_FEE_B, // Account balance can't pay fee
|
||||
terUNFUNDED, // Source account had insufficient balance for transactin
|
||||
terNO_PATH, // No path existed or met transaction/balance requirements
|
||||
terPAST_SEQ, // This sequence number has already past
|
||||
terPRE_SEQ, // Missing/inapplicable prior transaction
|
||||
@@ -27,22 +31,33 @@ enum TransactionEngineResult
|
||||
|
||||
enum TransactionEngineParams
|
||||
{
|
||||
tepNONE = 0,
|
||||
tepNO_CHECK_SIGN = 1, // Signature already checked
|
||||
tepNO_CHECK_FEE = 2, // It was voted into a ledger anyway
|
||||
};
|
||||
|
||||
enum TransactionAccountAction
|
||||
{
|
||||
taaACCESS,
|
||||
taaCREATE,
|
||||
taaMODIFY,
|
||||
taaDELETE
|
||||
};
|
||||
|
||||
typedef std::pair<TransactionAccountAction, SerializedLedgerEntry::pointer> AffectedAccount;
|
||||
|
||||
class TransactionEngine
|
||||
{
|
||||
protected:
|
||||
Ledger::pointer mLedger;
|
||||
|
||||
TransactionEngineResult doPayment(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doInvoice(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doOffer(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doTake(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doCancel(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doStore(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doDelete(const SerializedTransaction&, SerializedLedgerEntry& source);
|
||||
TransactionEngineResult doPayment(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
TransactionEngineResult doInvoice(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
TransactionEngineResult doOffer(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
TransactionEngineResult doTake(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
TransactionEngineResult doCancel(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
TransactionEngineResult doStore(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
TransactionEngineResult doDelete(const SerializedTransaction&, std::vector<AffectedAccount>&);
|
||||
|
||||
public:
|
||||
TransactionEngine() { ; }
|
||||
@@ -54,4 +69,14 @@ public:
|
||||
TransactionEngineResult applyTransaction(const SerializedTransaction&, TransactionEngineParams);
|
||||
};
|
||||
|
||||
inline TransactionEngineParams operator|(const TransactionEngineParams& l1, const TransactionEngineParams& l2)
|
||||
{
|
||||
return static_cast<TransactionEngineParams>(static_cast<int>(l1) | static_cast<int>(l2));
|
||||
}
|
||||
|
||||
inline TransactionEngineParams operator&(const TransactionEngineParams& l1, const TransactionEngineParams& l2)
|
||||
{
|
||||
return static_cast<TransactionEngineParams>(static_cast<int>(l1) & static_cast<int>(l2));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,7 @@ struct TransactionFormat
|
||||
|
||||
const int32 TransactionMagic=0x54584E00;
|
||||
|
||||
const int TransactionIVersion=0, TransactionISigningAccount=1, TransactionISequence=2;
|
||||
const int TransactionIVersion=0, TransactionISigningPubKey=1, TransactionISequence=2;
|
||||
const int TransactionIType=3, TransactionIFee=4;
|
||||
|
||||
const int TransactionMinLen=32;
|
||||
|
||||
@@ -127,7 +127,7 @@ void ValidationCollection::addToGroup(newcoin::Validation& newValid)
|
||||
// see if this hash is already on the list. If so add it there.
|
||||
vector< Group >& groups=mIndexGroups[newValid.ledgerindex()];
|
||||
BOOST_FOREACH(Group& group,groups)
|
||||
{
|
||||
{ // FIXME: Cannot modify *at* *all* inside a BOOST_FOREACH
|
||||
BOOST_FOREACH(newcoin::Validation& valid,group.mValidations)
|
||||
{
|
||||
if(valid.hash()==newValid.hash())
|
||||
|
||||
@@ -24,12 +24,10 @@
|
||||
//
|
||||
|
||||
LocalAccount::LocalAccount(boost::shared_ptr<LocalAccountFamily> family, int familySeq) :
|
||||
mPublicKey(family->getPublicKey(familySeq)), mFamily(family), mAccountFSeq(familySeq),
|
||||
mLgrBalance(0), mTxnDelta(0), mTxnSeq(0)
|
||||
mPublicKey(family->getPublicKey(familySeq)), mFamily(family), mAccountFSeq(familySeq)
|
||||
{
|
||||
mAcctID.setAccountPublic(mPublicKey->GetPubKey());
|
||||
|
||||
if(theApp!=NULL) mPublicKey=theApp->getPubKeyCache().store(mAcctID, mPublicKey);
|
||||
if(theApp!=NULL) mPublicKey = theApp->getPubKeyCache().store(mAcctID, mPublicKey);
|
||||
}
|
||||
|
||||
std::string LocalAccount::getFullName() const
|
||||
@@ -37,7 +35,6 @@ std::string LocalAccount::getFullName() const
|
||||
std::string ret(mFamily->getFamily().humanFamilyGenerator());
|
||||
ret.append(":");
|
||||
ret.append(boost::lexical_cast<std::string>(mAccountFSeq));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -51,21 +48,36 @@ std::string LocalAccount::getFamilyName() const
|
||||
return mFamily->getFamily().humanFamilyGenerator();
|
||||
}
|
||||
|
||||
AccountState::pointer LocalAccount::getAccountState() const
|
||||
{
|
||||
return theApp->getOPs().getAccountState(mAcctID);
|
||||
}
|
||||
|
||||
uint64 LocalAccount::getEffectiveBalance() const
|
||||
{
|
||||
AccountState::pointer as = getAccountState();
|
||||
if (!as) return 0;
|
||||
return as->getBalance();
|
||||
}
|
||||
|
||||
Json::Value LocalAccount::getJson() const
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
ret["Family"]=getFamilyName();
|
||||
ret["AccountID"]=getAddress().humanAccountID();
|
||||
ret["AccountPublic"]=getAddress().humanAccountPublic();
|
||||
ret["FullName"]=getFullName();
|
||||
ret["Issued"]=Json::Value(isIssued());
|
||||
ret["IsLocked"]=mFamily->isLocked();
|
||||
ret["Family"] = getFamilyName();
|
||||
ret["AccountID"] = getAddress().humanAccountID();
|
||||
ret["AccountPublic"] = getAddress().humanAccountPublic();
|
||||
ret["FullName"] = getFullName();
|
||||
ret["Issued"] = Json::Value(isIssued());
|
||||
ret["IsLocked"] = mFamily->isLocked();
|
||||
|
||||
uint64 eb=getEffectiveBalance();
|
||||
if(eb!=0) ret["Balance"]=boost::lexical_cast<std::string>(eb);
|
||||
|
||||
uint32 sq=getTxnSeq();
|
||||
if(sq!=0) ret["TxnSeq"]=boost::lexical_cast<std::string>(sq);
|
||||
AccountState::pointer as = getAccountState();
|
||||
if (as) ret["Account"] = "None";
|
||||
else
|
||||
{
|
||||
Json::Value acct(Json::objectValue);
|
||||
as->addJson(acct);
|
||||
ret["Account"] = acct;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -475,7 +487,6 @@ LocalAccount::pointer Wallet::getNewLocalAccount(const NewcoinAddress& family)
|
||||
mAccounts.insert(std::make_pair(acct, lac));
|
||||
|
||||
sl.unlock();
|
||||
lac->syncLedger();
|
||||
|
||||
return lac;
|
||||
}
|
||||
@@ -495,7 +506,6 @@ LocalAccount::pointer Wallet::getLocalAccount(const NewcoinAddress& family, int
|
||||
mAccounts.insert(std::make_pair(acct, lac));
|
||||
|
||||
sl.unlock();
|
||||
lac->syncLedger();
|
||||
|
||||
return lac;
|
||||
}
|
||||
@@ -743,24 +753,11 @@ bool Wallet::unitTest()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wallet::syncToLedger(bool force, Ledger* ledger)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if(!force && (mLedger>=ledger->getLedgerSeq())) return;
|
||||
for(std::map<uint256, LocalTransaction::pointer>::iterator xit=mTransactions.begin();
|
||||
xit!=mTransactions.end(); ++xit)
|
||||
{ // check each transaction, see if it's in the ledger or allowed in the ledger
|
||||
// WRITEME
|
||||
}
|
||||
for(std::map<NewcoinAddress, LocalAccount::pointer>::iterator ait=mAccounts.begin(); ait!=mAccounts.end(); ++ait)
|
||||
{ // check each account, see if our ledger balance matches
|
||||
LocalAccount::pointer& lac=ait->second;
|
||||
AccountState::pointer acs=ledger->getAccountState(ait->first);
|
||||
if(!acs) lac->setLedgerBalance(0);
|
||||
else lac->setLedgerBalance(acs->getBalance());
|
||||
}
|
||||
if(mLedger<ledger->getLedgerSeq()) mLedger=ledger->getLedgerSeq();
|
||||
}
|
||||
#if 0
|
||||
|
||||
// We can't replicate the transaction logic in the wallet
|
||||
// The right way is to apply the transactions to the ledger
|
||||
// And then sync all affected accounts to the ledger
|
||||
|
||||
void Wallet::applyTransaction(Transaction::pointer txn)
|
||||
{
|
||||
@@ -775,7 +772,7 @@ void Wallet::applyTransaction(Transaction::pointer txn)
|
||||
if(lti!=mTransactions.end()) ltx=lti->second;
|
||||
|
||||
std::map<NewcoinAddress, LocalAccount::pointer>::iterator lac=mAccounts.find(txn->getToAccount());
|
||||
if(lac!=mAccounts.end())
|
||||
if(lac != mAccounts.end())
|
||||
{ // this is to a local account
|
||||
if(!ltx)
|
||||
{ // this is to a local account, and we don't have a local transaction for it
|
||||
@@ -788,8 +785,8 @@ void Wallet::applyTransaction(Transaction::pointer txn)
|
||||
}
|
||||
}
|
||||
|
||||
lac=mAccounts.find(txn->getFromAccount());
|
||||
if(lac==mAccounts.end()) return;
|
||||
lac = mAccounts.find(txn->getFromAccount());
|
||||
if(lac == mAccounts.end()) return;
|
||||
|
||||
if ( (st!=INVALID) && (lac->second->getTxnSeq()==txn->getFromAccountSeq()) )
|
||||
lac->second->incTxnSeq();
|
||||
@@ -830,6 +827,8 @@ void Wallet::applyTransaction(Transaction::pointer txn)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Wallet::addLocalTransactions(Json::Value& ret)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
@@ -841,9 +840,9 @@ void Wallet::addLocalTransactions(Json::Value& ret)
|
||||
bool Wallet::getTxJson(const uint256& txn, Json::Value& ret)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
std::map<uint256, LocalTransaction::pointer>::iterator it=mTransactions.find(txn);
|
||||
if(it==mTransactions.end()) return false;
|
||||
ret=it->second->getJson();
|
||||
std::map<uint256, LocalTransaction::pointer>::iterator it = mTransactions.find(txn);
|
||||
if (it == mTransactions.end()) return false;
|
||||
ret = it->second->getJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -851,12 +850,12 @@ bool Wallet::getTxJson(const uint256& txn, Json::Value& ret)
|
||||
bool Wallet::getTxsJson(const NewcoinAddress& account, Json::Value& ret)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
for(std::map<uint256, LocalTransaction::pointer>::iterator it=mTransactions.begin();
|
||||
it!=mTransactions.end(); ++it)
|
||||
for(std::map<uint256, LocalTransaction::pointer>::iterator it = mTransactions.begin(),
|
||||
end = mTransactions.end(); it != end; ++it)
|
||||
{
|
||||
Transaction::pointer txn=it->second->getTransaction();
|
||||
if(txn && ((account==txn->getFromAccount())||(account==txn->getToAccount())) )
|
||||
ret[it->first.GetHex()]=it->second->getJson();
|
||||
Transaction::pointer txn = it->second->getTransaction();
|
||||
if(txn && (account == txn->getFromAccount())) // FIXME: Need a way to get all accounts a txn affects
|
||||
ret[it->first.GetHex()] = it->second->getJson();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user