Files
rippled/Transaction.cpp

233 lines
6.4 KiB
C++

#include "boost/lexical_cast.hpp"
#include "Application.h"
#include "Transaction.h"
#include "Wallet.h"
#include "Account.h"
#include "BitcoinUtil.h"
using namespace std;
Transaction::Transaction() : mTransactionID(0), mAccountFrom(0), mAccountTo(0),
mAmount(0), mFromAccountSeq(0), mSourceLedger(0), mIdent(0),
mInLedger(0), mStatus(INVALID)
{
}
Transaction::Transaction(TransStatus status, LocalAccount& fromLocalAccount, uint32 fromSeq,
const uint160& toAccount, uint64 amount, uint32 ident, uint32 ledger) :
mAccountTo(toAccount), mAmount(amount), mFromAccountSeq(fromSeq), mSourceLedger(ledger),
mIdent(ident), mInLedger(0), mStatus(NEW)
{
assert(fromLocalAccount.mAmount>=amount);
mAccountFrom=fromLocalAccount.getAddress();
mFromPubKey=fromLocalAccount.peekPubKey();
updateFee();
sign(fromLocalAccount);
}
Transaction::Transaction(const std::vector<unsigned char> &t, bool validate) : mStatus(INVALID)
{
Serializer s(t);
if(s.getLength()<145) { assert(false); return; }
if(!s.get160(mAccountTo, 0)) { assert(false); return; }
if(!s.get64(mAmount, 20)) { assert(false); return; }
if(!s.get32(mFromAccountSeq, 28)) { assert(false); return; }
if(!s.get32(mSourceLedger, 32)) { assert(false); return; }
if(!s.get32(mIdent, 36)) { assert(false); return; }
if(!s.getRaw(mSignature, 69, 72)) { assert(false); return; }
std::vector<unsigned char> pubKey;
if(!s.getRaw(pubKey, 40, 33)) { assert(false); return; }
mFromPubKey=CKey::pointer(new CKey());
if(!mFromPubKey->SetPubKey(pubKey)) return;
mAccountFrom=Hash160(pubKey);
mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
updateID();
if(!validate || checkSign())
mStatus=NEW;
}
bool Transaction::sign(LocalAccount& fromLocalAccount)
{
if( (mAmount==0) || (mSourceLedger==0) || (mAccountTo==0) )
{
assert(false);
return false;
}
if(mAccountFrom!=fromLocalAccount.mAddress.GetHash160())
{
assert(false);
return false;
}
Serializer::pointer signBuf=getRaw(true);
assert(signBuf->getLength()==73+4);
if(!signBuf->makeSignature(mSignature, fromLocalAccount.peekPrivKey()))
{
assert(false);
return false;
}
assert(mSignature.size()==72);
updateID();
return true;
}
void Transaction::updateFee()
{ // for now, all transactions have a 1,000 unit fee
mFee=1000;
}
bool Transaction::checkSign() const
{
assert(mFromPubKey);
return mFromPubKey->Verify(getRaw(true)->getSHA512Half(), mSignature);
}
Serializer::pointer Transaction::getRaw(bool prefix) const
{
Serializer::pointer ret(new Serializer(77));
if(prefix) ret->add32(0x54584e00u);
ret->add160(mAccountTo);
ret->addRaw(mFromPubKey->GetPubKey());
ret->add64(mAmount);
ret->add32(mFromAccountSeq);
ret->add32(mInLedger);
ret->add32(mIdent);
assert( (prefix&&(ret->getLength()==77)) || (!prefix&&(ret->getLength()==73)) );
return ret;
}
Serializer::pointer Transaction::getSigned() const
{
Serializer::pointer ret(getRaw(false));
ret->addRaw(mSignature);
return ret;
}
void Transaction::updateID()
{
mTransactionID=getSigned()->getSHA512Half();
}
void Transaction::setStatus(TransStatus ts, uint32 lseq)
{
mStatus=ts;
mInLedger=lseq;
}
bool Transaction::save() const
{
if((mStatus==INVALID)||(mStatus==REMOVED)) return false;
std::string sql="INSERT INTO Transactions "
"(TransID,FromAcct,FromSeq,FromLedger,Identifier,ToAcct,Amount,Fee,FirstSeen,CommitSeq,Status, Signature)"
" VALUES ('";
sql.append(mTransactionID.GetHex());
sql.append("','");
sql.append(mAccountFrom.GetHex());
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mFromAccountSeq));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mSourceLedger));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mIdent));
sql.append("','");
sql.append(mAccountTo.GetHex());
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mAmount));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mFee));
sql.append("',now(),'");
sql.append(boost::lexical_cast<std::string>(mInLedger)); switch(mStatus)
{
case NEW: sql.append("','N',"); break;
case INCLUDED: sql.append("','A',"); break;
case CONFLICTED: sql.append("','C',"); break;
case COMMITTED: sql.append("','D',"); break;
case HELD: sql.append("','H',"); break;
default: sql.append("','U',"); break;
}
std::string signature;
theApp->getDB()->escape(&(mSignature.front()), mSignature.size(), signature);
sql.append(signature);
sql.append(";");
ScopedLock sl(theApp->getDBLock());
Database* db=theApp->getDB();
return db->executeSQL(sql.c_str());
}
Transaction::pointer Transaction::transactionFromSQL(const std::string& sql)
{
std::string transID, fromID, toID, status;
uint64 amount, fee;
uint32 ledgerSeq, fromSeq, fromLedger, ident;
std::vector<unsigned char> signature;
signature.reserve(78);
if(1)
{
ScopedLock sl(theApp->getDBLock());
Database* db=theApp->getDB();
if(!db->executeSQL(sql.c_str())) return Transaction::pointer();
if(!db->getNextRow()) return Transaction::pointer();
db->getStr("TransID", transID);
db->getStr("FromID", fromID);
fromSeq=db->getBigInt("FromSeq");
fromLedger=db->getBigInt("FromLedger");
db->getStr("ToID", toID);
amount=db->getBigInt("Amount");
fee=db->getBigInt("Fee");
ledgerSeq=db->getBigInt("CommitSeq");
ident=db->getBigInt("Identifier");
db->getStr("Status", status);
int sigSize=db->getBinary("Signature", &(signature.front()), signature.size());
signature.resize(sigSize);
}
uint256 trID;
uint160 frID, tID;
trID.SetHex(transID);
frID.SetHex(fromID);
tID.SetHex(toID);
CKey::pointer pubkey=theApp->getPubKeyCache().locate(tID);
if(!pubkey) return Transaction::pointer();
TransStatus st(INVALID);
switch(status[0])
{
case 'N': st=NEW; break;
case 'A': st=INCLUDED; break;
case 'C': st=CONFLICTED; break;
case 'D': st=COMMITTED; break;
case 'H': st=HELD; break;
}
return Transaction::pointer(new Transaction(trID, frID, tID, pubkey, amount, fee, fromSeq, fromLedger,
ident, signature, ledgerSeq, st));
}
Transaction::pointer Transaction::load(const uint256& id)
{
std::string sql="SELECT * FROM Transactions WHERE Hash='";
sql.append(id.GetHex());
sql.append("';");
return transactionFromSQL(sql);
}
Transaction::pointer Transaction::findFrom(const uint160& fromID, uint32 seq)
{
std::string sql="SELECT * FROM Transactions WHERE FromID='";
sql.append(fromID.GetHex());
sql.append("' AND FromSeq='");
sql.append(boost::lexical_cast<std::string>(seq));
sql.append("';");
return transactionFromSQL(sql);
}