mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
351 lines
9.4 KiB
C++
351 lines
9.4 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
Copyright (c) 2011-2013, OpenCoin, Inc.
|
|
*/
|
|
//==============================================================================
|
|
|
|
SETUP_LOG (SerializedTransaction)
|
|
|
|
SerializedTransaction::SerializedTransaction (TxType type)
|
|
: STObject (sfTransaction)
|
|
, mType (type)
|
|
, mSigGood (false)
|
|
, mSigBad (false)
|
|
{
|
|
mFormat = TxFormats::getInstance()->findByType (type);
|
|
|
|
if (mFormat == nullptr)
|
|
{
|
|
WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << type;
|
|
throw std::runtime_error ("invalid transaction type");
|
|
}
|
|
|
|
set (mFormat->elements);
|
|
setFieldU16 (sfTransactionType, mFormat->getType ());
|
|
}
|
|
|
|
SerializedTransaction::SerializedTransaction (STObject const& object)
|
|
: STObject (object)
|
|
, mSigGood (false)
|
|
, mSigBad (false)
|
|
{
|
|
mType = static_cast <TxType> (getFieldU16 (sfTransactionType));
|
|
|
|
mFormat = TxFormats::getInstance()->findByType (mType);
|
|
|
|
if (!mFormat)
|
|
{
|
|
WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << mType;
|
|
throw std::runtime_error ("invalid transaction type");
|
|
}
|
|
|
|
if (!setType (mFormat->elements))
|
|
{
|
|
throw std::runtime_error ("transaction not valid");
|
|
}
|
|
}
|
|
|
|
SerializedTransaction::SerializedTransaction (SerializerIterator& sit) : STObject (sfTransaction),
|
|
mSigGood (false), mSigBad (false)
|
|
{
|
|
int length = sit.getBytesLeft ();
|
|
|
|
if ((length < Protocol::txMinSizeBytes) || (length > Protocol::txMaxSizeBytes))
|
|
{
|
|
Log (lsERROR) << "Transaction has invalid length: " << length;
|
|
throw std::runtime_error ("Transaction length invalid");
|
|
}
|
|
|
|
set (sit);
|
|
mType = static_cast<TxType> (getFieldU16 (sfTransactionType));
|
|
|
|
mFormat = TxFormats::getInstance()->findByType (mType);
|
|
|
|
if (!mFormat)
|
|
{
|
|
WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << mType;
|
|
throw std::runtime_error ("invalid transaction type");
|
|
}
|
|
|
|
if (!setType (mFormat->elements))
|
|
{
|
|
WriteLog (lsWARNING, SerializedTransaction) << "Transaction not legal for format";
|
|
throw std::runtime_error ("transaction not valid");
|
|
}
|
|
}
|
|
|
|
std::string SerializedTransaction::getFullText () const
|
|
{
|
|
std::string ret = "\"";
|
|
ret += getTransactionID ().GetHex ();
|
|
ret += "\" = {";
|
|
ret += STObject::getFullText ();
|
|
ret += "}";
|
|
return ret;
|
|
}
|
|
|
|
std::string SerializedTransaction::getText () const
|
|
{
|
|
return STObject::getText ();
|
|
}
|
|
|
|
std::vector<RippleAddress> SerializedTransaction::getMentionedAccounts () const
|
|
{
|
|
std::vector<RippleAddress> accounts;
|
|
|
|
BOOST_FOREACH (const SerializedType & it, peekData ())
|
|
{
|
|
const STAccount* sa = dynamic_cast<const STAccount*> (&it);
|
|
|
|
if (sa != NULL)
|
|
{
|
|
bool found = false;
|
|
RippleAddress na = sa->getValueNCA ();
|
|
BOOST_FOREACH (const RippleAddress & it, accounts)
|
|
{
|
|
if (it == na)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
accounts.push_back (na);
|
|
}
|
|
|
|
const STAmount* sam = dynamic_cast<const STAmount*> (&it);
|
|
|
|
if (sam)
|
|
{
|
|
uint160 issuer = sam->getIssuer ();
|
|
|
|
if (issuer.isNonZero ())
|
|
{
|
|
RippleAddress na;
|
|
na.setAccountID (issuer);
|
|
bool found = false;
|
|
BOOST_FOREACH (const RippleAddress & it, accounts)
|
|
{
|
|
if (it == na)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
accounts.push_back (na);
|
|
}
|
|
}
|
|
}
|
|
return accounts;
|
|
}
|
|
|
|
uint256 SerializedTransaction::getSigningHash () const
|
|
{
|
|
return STObject::getSigningHash (getConfig ().SIGN_TRANSACTION);
|
|
}
|
|
|
|
uint256 SerializedTransaction::getTransactionID () const
|
|
{
|
|
// perhaps we should cache this
|
|
return getHash (HashPrefix::transactionID);
|
|
}
|
|
|
|
Blob SerializedTransaction::getSignature () const
|
|
{
|
|
try
|
|
{
|
|
return getFieldVL (sfTxnSignature);
|
|
}
|
|
catch (...)
|
|
{
|
|
return Blob ();
|
|
}
|
|
}
|
|
|
|
void SerializedTransaction::sign (const RippleAddress& naAccountPrivate)
|
|
{
|
|
Blob signature;
|
|
naAccountPrivate.accountPrivateSign (getSigningHash (), signature);
|
|
setFieldVL (sfTxnSignature, signature);
|
|
}
|
|
|
|
bool SerializedTransaction::checkSign () const
|
|
{
|
|
if (mSigGood)
|
|
return true;
|
|
|
|
if (mSigBad)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
RippleAddress n;
|
|
n.setAccountPublic (getFieldVL (sfSigningPubKey));
|
|
|
|
if (checkSign (n))
|
|
{
|
|
mSigGood = true;
|
|
return true;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
;
|
|
}
|
|
|
|
mSigBad = true;
|
|
return false;
|
|
}
|
|
|
|
bool SerializedTransaction::checkSign (const RippleAddress& naAccountPublic) const
|
|
{
|
|
try
|
|
{
|
|
return naAccountPublic.accountPublicVerify (getSigningHash (), getFieldVL (sfTxnSignature));
|
|
}
|
|
catch (...)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SerializedTransaction::setSigningPubKey (const RippleAddress& naSignPubKey)
|
|
{
|
|
setFieldVL (sfSigningPubKey, naSignPubKey.getAccountPublic ());
|
|
}
|
|
|
|
void SerializedTransaction::setSourceAccount (const RippleAddress& naSource)
|
|
{
|
|
setFieldAccount (sfAccount, naSource);
|
|
}
|
|
|
|
Json::Value SerializedTransaction::getJson (int options, bool binary) const
|
|
{
|
|
if (binary)
|
|
{
|
|
Json::Value ret;
|
|
Serializer s = STObject::getSerializer ();
|
|
ret["tx"] = strHex (s.peekData ());
|
|
ret["hash"] = getTransactionID ().GetHex ();
|
|
return ret;
|
|
}
|
|
|
|
Json::Value ret = STObject::getJson (0);
|
|
ret["hash"] = getTransactionID ().GetHex ();
|
|
return ret;
|
|
}
|
|
|
|
std::string SerializedTransaction::getSQLValueHeader ()
|
|
{
|
|
return "(TransID, TransType, FromAcct, FromSeq, LedgerSeq, Status, RawTxn)";
|
|
}
|
|
|
|
std::string SerializedTransaction::getMetaSQLValueHeader ()
|
|
{
|
|
return "(TransID, TransType, FromAcct, FromSeq, LedgerSeq, Status, RawTxn, TxnMeta)";
|
|
}
|
|
|
|
std::string SerializedTransaction::getMetaSQLInsertReplaceHeader ()
|
|
{
|
|
return "INSERT OR REPLACE INTO Transactions " + getMetaSQLValueHeader () + " VALUES ";
|
|
}
|
|
|
|
std::string SerializedTransaction::getSQL (uint32 inLedger, char status) const
|
|
{
|
|
Serializer s;
|
|
add (s);
|
|
return getSQL (s, inLedger, status);
|
|
}
|
|
|
|
std::string SerializedTransaction::getMetaSQL (uint32 inLedger, const std::string& escapedMetaData) const
|
|
{
|
|
Serializer s;
|
|
add (s);
|
|
return getMetaSQL (s, inLedger, TXN_SQL_VALIDATED, escapedMetaData);
|
|
}
|
|
|
|
std::string SerializedTransaction::getSQL (Serializer rawTxn, uint32 inLedger, char status) const
|
|
{
|
|
static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s)");
|
|
std::string rTxn = sqlEscape (rawTxn.peekData ());
|
|
|
|
return str (boost::format (bfTrans)
|
|
% getTransactionID ().GetHex () % getTransactionType () % getSourceAccount ().humanAccountID ()
|
|
% getSequence () % inLedger % status % rTxn);
|
|
}
|
|
|
|
std::string SerializedTransaction::getMetaSQL (Serializer rawTxn, uint32 inLedger, char status,
|
|
const std::string& escapedMetaData) const
|
|
{
|
|
static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)");
|
|
std::string rTxn = sqlEscape (rawTxn.peekData ());
|
|
|
|
return str (boost::format (bfTrans)
|
|
% getTransactionID ().GetHex () % getTransactionType () % getSourceAccount ().humanAccountID ()
|
|
% getSequence () % inLedger % status % rTxn % escapedMetaData);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
class SerializedTransactionTests : public UnitTest
|
|
{
|
|
public:
|
|
SerializedTransactionTests () : UnitTest ("SerializedTransaction", "ripple")
|
|
{
|
|
}
|
|
|
|
void runTest ()
|
|
{
|
|
beginTestCase ("tx");
|
|
|
|
RippleAddress seed;
|
|
seed.setSeedRandom ();
|
|
RippleAddress generator = RippleAddress::createGeneratorPublic (seed);
|
|
RippleAddress publicAcct = RippleAddress::createAccountPublic (generator, 1);
|
|
RippleAddress privateAcct = RippleAddress::createAccountPrivate (generator, seed, 1);
|
|
|
|
SerializedTransaction j (ttACCOUNT_SET);
|
|
j.setSourceAccount (publicAcct);
|
|
j.setSigningPubKey (publicAcct);
|
|
j.setFieldVL (sfMessageKey, publicAcct.getAccountPublic ());
|
|
j.sign (privateAcct);
|
|
|
|
unexpected (!j.checkSign (), "Transaction fails signature test");
|
|
|
|
Serializer rawTxn;
|
|
j.add (rawTxn);
|
|
SerializerIterator sit (rawTxn);
|
|
SerializedTransaction copy (sit);
|
|
|
|
if (copy != j)
|
|
{
|
|
Log (lsFATAL) << j.getJson (0);
|
|
Log (lsFATAL) << copy.getJson (0);
|
|
fail ("Transaction fails serialize/deserialize test");
|
|
}
|
|
else
|
|
{
|
|
pass ();
|
|
}
|
|
|
|
UPTR_T<STObject> new_obj = STObject::parseJson (j.getJson (0), sfGeneric);
|
|
|
|
if (new_obj.get () == NULL) fail ("Unable to build object from json");
|
|
|
|
if (STObject (j) != *new_obj)
|
|
{
|
|
Log (lsINFO) << "ORIG: " << j.getJson (0);
|
|
Log (lsINFO) << "BUILT " << new_obj->getJson (0);
|
|
fail ("Built a different transaction");
|
|
}
|
|
else
|
|
{
|
|
pass ();
|
|
}
|
|
}
|
|
};
|
|
|
|
static SerializedTransactionTests serializedTransactionTests; |