mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Create TransactionErr.* and move dir functions to LedgerEntrySet.
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
|
||||
#include "LedgerEntrySet.h"
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
// Small for testing, should likely be 32 or 64.
|
||||
#define DIR_NODE_MAX 2
|
||||
|
||||
void LedgerEntrySet::init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID)
|
||||
{
|
||||
mEntries.clear();
|
||||
@@ -428,4 +434,326 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, Ledger::ref origLedger)
|
||||
mSet.addRaw(s);
|
||||
}
|
||||
|
||||
// <-- uNodeDir: For deletion, present to make dirDelete efficient.
|
||||
// --> uRootIndex: The index of the base of the directory. Nodes are based off of this.
|
||||
// --> uLedgerIndex: Value to add to directory.
|
||||
// We only append. This allow for things that watch append only structure to just monitor from the last node on ward.
|
||||
// Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random.
|
||||
TER LedgerEntrySet::dirAdd(
|
||||
uint64& uNodeDir,
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex)
|
||||
{
|
||||
SLE::pointer sleNode;
|
||||
STVector256 svIndexes;
|
||||
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
|
||||
|
||||
if (!sleRoot)
|
||||
{
|
||||
// No root, make it.
|
||||
sleRoot = entryCreate(ltDIR_NODE, uRootIndex);
|
||||
|
||||
sleNode = sleRoot;
|
||||
uNodeDir = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uNodeDir = sleRoot->getIFieldU64(sfIndexPrevious); // Get index to last directory node.
|
||||
|
||||
if (uNodeDir)
|
||||
{
|
||||
// Try adding to last node.
|
||||
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
|
||||
|
||||
assert(sleNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try adding to root. Didn't have a previous set to the last node.
|
||||
sleNode = sleRoot;
|
||||
}
|
||||
|
||||
svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
|
||||
if (DIR_NODE_MAX != svIndexes.peekValue().size())
|
||||
{
|
||||
// Add to current node.
|
||||
entryModify(sleNode);
|
||||
}
|
||||
// Add to new node.
|
||||
else if (!++uNodeDir)
|
||||
{
|
||||
return terDIR_FULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have old last point to new node, if it was not root.
|
||||
if (uNodeDir == 1)
|
||||
{
|
||||
// Previous node is root node.
|
||||
|
||||
sleRoot->setIFieldU64(sfIndexNext, uNodeDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previous node is not root node.
|
||||
|
||||
SLE::pointer slePrevious = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir-1));
|
||||
|
||||
slePrevious->setIFieldU64(sfIndexNext, uNodeDir);
|
||||
entryModify(slePrevious);
|
||||
|
||||
sleNode->setIFieldU64(sfIndexPrevious, uNodeDir-1);
|
||||
}
|
||||
|
||||
// Have root point to new node.
|
||||
sleRoot->setIFieldU64(sfIndexPrevious, uNodeDir);
|
||||
entryModify(sleRoot);
|
||||
|
||||
// Create the new node.
|
||||
sleNode = entryCreate(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
|
||||
svIndexes = STVector256();
|
||||
}
|
||||
}
|
||||
|
||||
svIndexes.peekValue().push_back(uLedgerIndex); // Append entry.
|
||||
sleNode->setIFieldV256(sfIndexes, svIndexes); // Save entry.
|
||||
|
||||
Log(lsINFO) << "dirAdd: creating: root: " << uRootIndex.ToString();
|
||||
Log(lsINFO) << "dirAdd: appending: Entry: " << uLedgerIndex.ToString();
|
||||
Log(lsINFO) << "dirAdd: appending: Node: " << strHex(uNodeDir);
|
||||
// Log(lsINFO) << "dirAdd: appending: PREV: " << svIndexes.peekValue()[0].ToString();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Ledger must be in a state for this to work.
|
||||
TER LedgerEntrySet::dirDelete(
|
||||
const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node.
|
||||
const uint64& uNodeDir, // --> Node containing entry.
|
||||
const uint256& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this.
|
||||
const uint256& uLedgerIndex, // --> Value to add to directory.
|
||||
const bool bStable) // --> True, not to change relative order of entries.
|
||||
{
|
||||
uint64 uNodeCur = uNodeDir;
|
||||
SLE::pointer sleNode = entryCache(ltDIR_NODE, uNodeCur ? Ledger::getDirNodeIndex(uRootIndex, uNodeCur) : uRootIndex);
|
||||
|
||||
assert(sleNode);
|
||||
|
||||
if (!sleNode)
|
||||
{
|
||||
Log(lsWARNING) << "dirDelete: no such node";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
|
||||
std::vector<uint256>::iterator it;
|
||||
|
||||
it = std::find(vuiIndexes.begin(), vuiIndexes.end(), uLedgerIndex);
|
||||
|
||||
assert(vuiIndexes.end() != it);
|
||||
if (vuiIndexes.end() == it)
|
||||
{
|
||||
assert(false);
|
||||
|
||||
Log(lsWARNING) << "dirDelete: no such entry";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
// Remove the element.
|
||||
if (vuiIndexes.size() > 1)
|
||||
{
|
||||
if (bStable)
|
||||
{
|
||||
vuiIndexes.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
*it = vuiIndexes[vuiIndexes.size()-1];
|
||||
vuiIndexes.resize(vuiIndexes.size()-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vuiIndexes.clear();
|
||||
}
|
||||
|
||||
sleNode->setIFieldV256(sfIndexes, svIndexes);
|
||||
entryModify(sleNode);
|
||||
|
||||
if (vuiIndexes.empty())
|
||||
{
|
||||
// May be able to delete nodes.
|
||||
uint64 uNodePrevious = sleNode->getIFieldU64(sfIndexPrevious);
|
||||
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
|
||||
|
||||
if (!uNodeCur)
|
||||
{
|
||||
// Just emptied root node.
|
||||
|
||||
if (!uNodePrevious)
|
||||
{
|
||||
// Never overflowed the root node. Delete it.
|
||||
entryDelete(sleNode);
|
||||
}
|
||||
// Root overflowed.
|
||||
else if (bKeepRoot)
|
||||
{
|
||||
// If root overflowed and not allowed to delete overflowed root node.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else if (uNodePrevious != uNodeNext)
|
||||
{
|
||||
// Have more than 2 nodes. Can't delete root node.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have only a root node and a last node.
|
||||
SLE::pointer sleLast = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
|
||||
|
||||
assert(sleLast);
|
||||
|
||||
if (sleLast->getIFieldV256(sfIndexes).peekValue().empty())
|
||||
{
|
||||
// Both nodes are empty.
|
||||
|
||||
entryDelete(sleNode); // Delete root.
|
||||
entryDelete(sleLast); // Delete last.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have an entry, can't delete root node.
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just emptied a non-root node.
|
||||
else if (uNodeNext)
|
||||
{
|
||||
// Not root and not last node. Can delete node.
|
||||
|
||||
SLE::pointer slePrevious = entryCache(ltDIR_NODE, uNodePrevious ? Ledger::getDirNodeIndex(uRootIndex, uNodePrevious) : uRootIndex);
|
||||
|
||||
assert(slePrevious);
|
||||
|
||||
SLE::pointer sleNext = entryCache(ltDIR_NODE, uNodeNext ? Ledger::getDirNodeIndex(uRootIndex, uNodeNext) : uRootIndex);
|
||||
|
||||
assert(slePrevious);
|
||||
assert(sleNext);
|
||||
|
||||
if (!slePrevious)
|
||||
{
|
||||
Log(lsWARNING) << "dirDelete: previous node is missing";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
if (!sleNext)
|
||||
{
|
||||
Log(lsWARNING) << "dirDelete: next node is missing";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
// Fix previous to point to its new next.
|
||||
slePrevious->setIFieldU64(sfIndexNext, uNodeNext);
|
||||
entryModify(slePrevious);
|
||||
|
||||
// Fix next to point to its new previous.
|
||||
sleNext->setIFieldU64(sfIndexPrevious, uNodePrevious);
|
||||
entryModify(sleNext);
|
||||
}
|
||||
// Last node.
|
||||
else if (bKeepRoot || uNodePrevious)
|
||||
{
|
||||
// Not allowed to delete last node as root was overflowed.
|
||||
// Or, have pervious entries preventing complete delete.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Last and only node besides the root.
|
||||
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
|
||||
|
||||
assert(sleRoot);
|
||||
|
||||
if (sleRoot->getIFieldV256(sfIndexes).peekValue().empty())
|
||||
{
|
||||
// Both nodes are empty.
|
||||
|
||||
entryDelete(sleRoot); // Delete root.
|
||||
entryDelete(sleNode); // Delete last.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Root has an entry, can't delete.
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Return the first entry and advance uDirEntry.
|
||||
// <-- true, if had a next entry.
|
||||
bool LedgerEntrySet::dirFirst(
|
||||
const uint256& uRootIndex, // --> Root of directory.
|
||||
SLE::pointer& sleNode, // <-- current node
|
||||
unsigned int& uDirEntry, // <-- next entry
|
||||
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
|
||||
{
|
||||
sleNode = entryCache(ltDIR_NODE, uRootIndex);
|
||||
uDirEntry = 0;
|
||||
|
||||
assert(sleNode); // We never probe for directories.
|
||||
|
||||
return LedgerEntrySet::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
|
||||
// Return the current entry and advance uDirEntry.
|
||||
// <-- true, if had a next entry.
|
||||
bool LedgerEntrySet::dirNext(
|
||||
const uint256& uRootIndex, // --> Root of directory
|
||||
SLE::pointer& sleNode, // <-> current node
|
||||
unsigned int& uDirEntry, // <-> next entry
|
||||
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
|
||||
{
|
||||
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
|
||||
|
||||
if (uDirEntry == vuiIndexes.size())
|
||||
{
|
||||
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
|
||||
|
||||
if (!uNodeNext)
|
||||
{
|
||||
uEntryIndex.zero();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
|
||||
uDirEntry = 0;
|
||||
|
||||
return dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
uEntryIndex = vuiIndexes[uDirEntry++];
|
||||
Log(lsINFO) << boost::str(boost::format("dirNext: uDirEntry=%d uEntryIndex=%s") % uDirEntry % uEntryIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "SerializedLedger.h"
|
||||
#include "TransactionMeta.h"
|
||||
#include "Ledger.h"
|
||||
#include "TransactionErr.h"
|
||||
|
||||
enum LedgerEntryAction
|
||||
{
|
||||
@@ -79,6 +80,23 @@ public:
|
||||
SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex);
|
||||
SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex);
|
||||
|
||||
// Utility entry functions.
|
||||
TER dirAdd(
|
||||
uint64& uNodeDir, // Node of entry.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex);
|
||||
|
||||
TER dirDelete(
|
||||
const bool bKeepRoot,
|
||||
const uint64& uNodeDir, // Node item is mentioned in.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex, // Item being deleted
|
||||
const bool bStable);
|
||||
|
||||
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
|
||||
|
||||
Json::Value getJson(int) const;
|
||||
void calcRawMeta(Serializer&, Ledger::ref originalLedger);
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#include "Interpreter.h"
|
||||
#include "Contract.h"
|
||||
|
||||
// Small for testing, should likely be 32 or 64.
|
||||
#define DIR_NODE_MAX 2
|
||||
#define RIPPLE_PATHS_MAX 3
|
||||
|
||||
static STAmount saZero(CURRENCY_ONE, ACCOUNT_ONE, 0);
|
||||
@@ -36,97 +34,6 @@ std::size_t hash_value(const aciSource& asValue)
|
||||
return seed;
|
||||
}
|
||||
|
||||
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
|
||||
{
|
||||
static struct {
|
||||
TER terCode;
|
||||
const char* cpToken;
|
||||
const char* cpHuman;
|
||||
} transResultInfoA[] = {
|
||||
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger" },
|
||||
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
|
||||
{ tefBAD_AUTH, "tefBAD_AUTH", "Transaction's public key is not authorized." },
|
||||
{ tefBAD_CLAIM_ID, "tefBAD_CLAIM_ID", "Malformed." },
|
||||
{ tefBAD_GEN_AUTH, "tefBAD_GEN_AUTH", "Not authorized to claim generator." },
|
||||
{ tefBAD_LEDGER, "tefBAD_LEDGER", "Ledger in unexpected state." },
|
||||
{ tefCLAIMED, "tefCLAIMED", "Can not claim a previously claimed account." },
|
||||
{ tefEXCEPTION, "tefEXCEPTION", "Unexpected program state." },
|
||||
{ tefCREATED, "tefCREATED", "Can't add an already created account." },
|
||||
{ tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." },
|
||||
{ tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past" },
|
||||
|
||||
{ telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: too many paths." },
|
||||
{ telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." },
|
||||
|
||||
{ temBAD_AMOUNT, "temBAD_AMOUNT", "Can only send positive amounts." },
|
||||
{ temBAD_AUTH_MASTER, "temBAD_AUTH_MASTER", "Auth for unclaimed account needs correct master key." },
|
||||
{ temBAD_EXPIRATION, "temBAD_EXPIRATION", "Malformed." },
|
||||
{ temBAD_ISSUER, "temBAD_ISSUER", "Malformed." },
|
||||
{ temBAD_OFFER, "temBAD_OFFER", "Malformed." },
|
||||
{ temBAD_PATH, "temBAD_PATH", "Malformed." },
|
||||
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed." },
|
||||
{ temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: bad publish." },
|
||||
{ temBAD_SET_ID, "temBAD_SET_ID", "Malformed." },
|
||||
{ temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." },
|
||||
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
||||
{ temDST_NEEDED, "temDST_NEEDED", "Destination not specified." },
|
||||
{ temINSUF_FEE_P, "temINSUF_FEE_P", "Fee not allowed." },
|
||||
{ temINVALID, "temINVALID", "The transaction is ill-formed" },
|
||||
{ temREDUNDANT, "temREDUNDANT", "Sends same currency to self." },
|
||||
{ temRIPPLE_EMPTY, "temRIPPLE_EMPTY", "PathSet with no paths." },
|
||||
{ temUNCERTAIN, "temUNCERTAIN", "In process of determining result. Never returned." },
|
||||
{ temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet." },
|
||||
|
||||
{ tepPATH_DRY, "tepPATH_DRY", "Path could not send partial amount." },
|
||||
{ tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." },
|
||||
|
||||
{ terDIR_FULL, "terDIR_FULL", "Can not add entry to full dir." },
|
||||
{ terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." },
|
||||
{ terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
|
||||
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." },
|
||||
{ terNO_DST, "terNO_DST", "The destination does not exist" },
|
||||
{ terNO_LINE, "terNO_LINE", "No such line." },
|
||||
{ terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." },
|
||||
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
|
||||
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction" },
|
||||
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
|
||||
{ terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." },
|
||||
|
||||
{ tesSUCCESS, "tesSUCCESS", "The transaction was applied" },
|
||||
};
|
||||
|
||||
int iIndex = NUMBER(transResultInfoA);
|
||||
|
||||
while (iIndex-- && transResultInfoA[iIndex].terCode != terCode)
|
||||
;
|
||||
|
||||
if (iIndex >= 0)
|
||||
{
|
||||
strToken = transResultInfoA[iIndex].cpToken;
|
||||
strHuman = transResultInfoA[iIndex].cpHuman;
|
||||
}
|
||||
|
||||
return iIndex >= 0;
|
||||
}
|
||||
|
||||
static std::string transToken(TER terCode)
|
||||
{
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
return transResultInfo(terCode, strToken, strHuman) ? strToken : "-";
|
||||
}
|
||||
|
||||
#if 0
|
||||
static std::string transHuman(TER terCode)
|
||||
{
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
return transResultInfo(terCode, strToken, strHuman) ? strHuman : "-";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Returns amount owed by uToAccountID to uFromAccountID.
|
||||
// <-- $owed/uCurrencyID/uToAccountID: positive: uFromAccountID holds IOUs., negative: uFromAccountID owes IOUs.
|
||||
STAmount TransactionEngine::rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
|
||||
@@ -468,14 +375,14 @@ void TransactionEngine::accountSend(const uint160& uSenderID, const uint160& uRe
|
||||
TER TransactionEngine::offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID)
|
||||
{
|
||||
uint64 uOwnerNode = sleOffer->getIFieldU64(sfOwnerNode);
|
||||
TER terResult = dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex, false);
|
||||
TER terResult = mNodes.dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex, false);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
uint256 uDirectory = sleOffer->getIFieldH256(sfBookDirectory);
|
||||
uint64 uBookNode = sleOffer->getIFieldU64(sfBookNode);
|
||||
|
||||
terResult = dirDelete(false, uBookNode, uDirectory, uOfferIndex, true);
|
||||
terResult = mNodes.dirDelete(false, uBookNode, uDirectory, uOfferIndex, true);
|
||||
}
|
||||
|
||||
entryDelete(sleOffer);
|
||||
@@ -491,328 +398,6 @@ TER TransactionEngine::offerDelete(const uint256& uOfferIndex)
|
||||
return offerDelete(sleOffer, uOfferIndex, uOwnerID);
|
||||
}
|
||||
|
||||
// <-- uNodeDir: For deletion, present to make dirDelete efficient.
|
||||
// --> uRootIndex: The index of the base of the directory. Nodes are based off of this.
|
||||
// --> uLedgerIndex: Value to add to directory.
|
||||
// We only append. This allow for things that watch append only structure to just monitor from the last node on ward.
|
||||
// Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random.
|
||||
TER TransactionEngine::dirAdd(
|
||||
uint64& uNodeDir,
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex)
|
||||
{
|
||||
SLE::pointer sleNode;
|
||||
STVector256 svIndexes;
|
||||
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
|
||||
|
||||
if (!sleRoot)
|
||||
{
|
||||
// No root, make it.
|
||||
sleRoot = entryCreate(ltDIR_NODE, uRootIndex);
|
||||
|
||||
sleNode = sleRoot;
|
||||
uNodeDir = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uNodeDir = sleRoot->getIFieldU64(sfIndexPrevious); // Get index to last directory node.
|
||||
|
||||
if (uNodeDir)
|
||||
{
|
||||
// Try adding to last node.
|
||||
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
|
||||
|
||||
assert(sleNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try adding to root. Didn't have a previous set to the last node.
|
||||
sleNode = sleRoot;
|
||||
}
|
||||
|
||||
svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
|
||||
if (DIR_NODE_MAX != svIndexes.peekValue().size())
|
||||
{
|
||||
// Add to current node.
|
||||
entryModify(sleNode);
|
||||
}
|
||||
// Add to new node.
|
||||
else if (!++uNodeDir)
|
||||
{
|
||||
return terDIR_FULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have old last point to new node, if it was not root.
|
||||
if (uNodeDir == 1)
|
||||
{
|
||||
// Previous node is root node.
|
||||
|
||||
sleRoot->setIFieldU64(sfIndexNext, uNodeDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previous node is not root node.
|
||||
|
||||
SLE::pointer slePrevious = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir-1));
|
||||
|
||||
slePrevious->setIFieldU64(sfIndexNext, uNodeDir);
|
||||
entryModify(slePrevious);
|
||||
|
||||
sleNode->setIFieldU64(sfIndexPrevious, uNodeDir-1);
|
||||
}
|
||||
|
||||
// Have root point to new node.
|
||||
sleRoot->setIFieldU64(sfIndexPrevious, uNodeDir);
|
||||
entryModify(sleRoot);
|
||||
|
||||
// Create the new node.
|
||||
sleNode = entryCreate(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
|
||||
svIndexes = STVector256();
|
||||
}
|
||||
}
|
||||
|
||||
svIndexes.peekValue().push_back(uLedgerIndex); // Append entry.
|
||||
sleNode->setIFieldV256(sfIndexes, svIndexes); // Save entry.
|
||||
|
||||
Log(lsINFO) << "dirAdd: creating: root: " << uRootIndex.ToString();
|
||||
Log(lsINFO) << "dirAdd: appending: Entry: " << uLedgerIndex.ToString();
|
||||
Log(lsINFO) << "dirAdd: appending: Node: " << strHex(uNodeDir);
|
||||
// Log(lsINFO) << "dirAdd: appending: PREV: " << svIndexes.peekValue()[0].ToString();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Ledger must be in a state for this to work.
|
||||
TER TransactionEngine::dirDelete(
|
||||
const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node.
|
||||
const uint64& uNodeDir, // --> Node containing entry.
|
||||
const uint256& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this.
|
||||
const uint256& uLedgerIndex, // --> Value to add to directory.
|
||||
const bool bStable) // --> True, not to change relative order of entries.
|
||||
{
|
||||
uint64 uNodeCur = uNodeDir;
|
||||
SLE::pointer sleNode = entryCache(ltDIR_NODE, uNodeCur ? Ledger::getDirNodeIndex(uRootIndex, uNodeCur) : uRootIndex);
|
||||
|
||||
assert(sleNode);
|
||||
|
||||
if (!sleNode)
|
||||
{
|
||||
Log(lsWARNING) << "dirDelete: no such node";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
|
||||
std::vector<uint256>::iterator it;
|
||||
|
||||
it = std::find(vuiIndexes.begin(), vuiIndexes.end(), uLedgerIndex);
|
||||
|
||||
assert(vuiIndexes.end() != it);
|
||||
if (vuiIndexes.end() == it)
|
||||
{
|
||||
assert(false);
|
||||
|
||||
Log(lsWARNING) << "dirDelete: no such entry";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
// Remove the element.
|
||||
if (vuiIndexes.size() > 1)
|
||||
{
|
||||
if (bStable)
|
||||
{
|
||||
vuiIndexes.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
*it = vuiIndexes[vuiIndexes.size()-1];
|
||||
vuiIndexes.resize(vuiIndexes.size()-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vuiIndexes.clear();
|
||||
}
|
||||
|
||||
sleNode->setIFieldV256(sfIndexes, svIndexes);
|
||||
entryModify(sleNode);
|
||||
|
||||
if (vuiIndexes.empty())
|
||||
{
|
||||
// May be able to delete nodes.
|
||||
uint64 uNodePrevious = sleNode->getIFieldU64(sfIndexPrevious);
|
||||
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
|
||||
|
||||
if (!uNodeCur)
|
||||
{
|
||||
// Just emptied root node.
|
||||
|
||||
if (!uNodePrevious)
|
||||
{
|
||||
// Never overflowed the root node. Delete it.
|
||||
entryDelete(sleNode);
|
||||
}
|
||||
// Root overflowed.
|
||||
else if (bKeepRoot)
|
||||
{
|
||||
// If root overflowed and not allowed to delete overflowed root node.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else if (uNodePrevious != uNodeNext)
|
||||
{
|
||||
// Have more than 2 nodes. Can't delete root node.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have only a root node and a last node.
|
||||
SLE::pointer sleLast = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
|
||||
|
||||
assert(sleLast);
|
||||
|
||||
if (sleLast->getIFieldV256(sfIndexes).peekValue().empty())
|
||||
{
|
||||
// Both nodes are empty.
|
||||
|
||||
entryDelete(sleNode); // Delete root.
|
||||
entryDelete(sleLast); // Delete last.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have an entry, can't delete root node.
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just emptied a non-root node.
|
||||
else if (uNodeNext)
|
||||
{
|
||||
// Not root and not last node. Can delete node.
|
||||
|
||||
SLE::pointer slePrevious = entryCache(ltDIR_NODE, uNodePrevious ? Ledger::getDirNodeIndex(uRootIndex, uNodePrevious) : uRootIndex);
|
||||
|
||||
assert(slePrevious);
|
||||
|
||||
SLE::pointer sleNext = entryCache(ltDIR_NODE, uNodeNext ? Ledger::getDirNodeIndex(uRootIndex, uNodeNext) : uRootIndex);
|
||||
|
||||
assert(slePrevious);
|
||||
assert(sleNext);
|
||||
|
||||
if (!slePrevious)
|
||||
{
|
||||
Log(lsWARNING) << "dirDelete: previous node is missing";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
if (!sleNext)
|
||||
{
|
||||
Log(lsWARNING) << "dirDelete: next node is missing";
|
||||
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
// Fix previous to point to its new next.
|
||||
slePrevious->setIFieldU64(sfIndexNext, uNodeNext);
|
||||
entryModify(slePrevious);
|
||||
|
||||
// Fix next to point to its new previous.
|
||||
sleNext->setIFieldU64(sfIndexPrevious, uNodePrevious);
|
||||
entryModify(sleNext);
|
||||
}
|
||||
// Last node.
|
||||
else if (bKeepRoot || uNodePrevious)
|
||||
{
|
||||
// Not allowed to delete last node as root was overflowed.
|
||||
// Or, have pervious entries preventing complete delete.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Last and only node besides the root.
|
||||
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
|
||||
|
||||
assert(sleRoot);
|
||||
|
||||
if (sleRoot->getIFieldV256(sfIndexes).peekValue().empty())
|
||||
{
|
||||
// Both nodes are empty.
|
||||
|
||||
entryDelete(sleRoot); // Delete root.
|
||||
entryDelete(sleNode); // Delete last.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Root has an entry, can't delete.
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Return the first entry and advance uDirEntry.
|
||||
// <-- true, if had a next entry.
|
||||
bool TransactionEngine::dirFirst(
|
||||
const uint256& uRootIndex, // --> Root of directory.
|
||||
SLE::pointer& sleNode, // <-- current node
|
||||
unsigned int& uDirEntry, // <-- next entry
|
||||
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
|
||||
{
|
||||
sleNode = entryCache(ltDIR_NODE, uRootIndex);
|
||||
uDirEntry = 0;
|
||||
|
||||
assert(sleNode); // We never probe for directories.
|
||||
|
||||
return TransactionEngine::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
|
||||
// Return the current entry and advance uDirEntry.
|
||||
// <-- true, if had a next entry.
|
||||
bool TransactionEngine::dirNext(
|
||||
const uint256& uRootIndex, // --> Root of directory
|
||||
SLE::pointer& sleNode, // <-> current node
|
||||
unsigned int& uDirEntry, // <-> next entry
|
||||
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
|
||||
{
|
||||
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
|
||||
|
||||
if (uDirEntry == vuiIndexes.size())
|
||||
{
|
||||
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
|
||||
|
||||
if (!uNodeNext)
|
||||
{
|
||||
uEntryIndex.zero();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
|
||||
uDirEntry = 0;
|
||||
|
||||
return dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
uEntryIndex = vuiIndexes[uDirEntry++];
|
||||
Log(lsINFO) << boost::str(boost::format("dirNext: uDirEntry=%d uEntryIndex=%s") % uDirEntry % uEntryIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the authorized public key for an account. May also set the generator map.
|
||||
TER TransactionEngine::setAuthorized(const SerializedTransaction& txn, bool bMustSetGenerator)
|
||||
{
|
||||
@@ -1624,10 +1209,10 @@ TER TransactionEngine::doCreditSet(const SerializedTransaction& txn)
|
||||
|
||||
uint64 uSrcRef; // Ignored, dirs never delete.
|
||||
|
||||
terResult = dirAdd(uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex());
|
||||
terResult = mNodes.dirAdd(uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex());
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = dirAdd(uSrcRef, Ledger::getOwnerDirIndex(uDstAccountID), sleRippleState->getIndex());
|
||||
terResult = mNodes.dirAdd(uSrcRef, Ledger::getOwnerDirIndex(uDstAccountID), sleRippleState->getIndex());
|
||||
}
|
||||
|
||||
Log(lsINFO) << "doCreditSet<";
|
||||
@@ -1846,7 +1431,7 @@ TER TransactionEngine::calcNodeAdvance(
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
else if (!dirNext(uDirectTip, sleDirectDir, uEntry, uOfferIndex))
|
||||
else if (!mNodes.dirNext(uDirectTip, sleDirectDir, uEntry, uOfferIndex))
|
||||
{
|
||||
// Failed to find an entry in directory.
|
||||
|
||||
@@ -3965,7 +3550,7 @@ TER TransactionEngine::takeOffers(
|
||||
unsigned int uBookEntry;
|
||||
uint256 uOfferIndex;
|
||||
|
||||
dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex);
|
||||
mNodes.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex);
|
||||
|
||||
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
|
||||
|
||||
@@ -4255,7 +3840,7 @@ Log(lsWARNING) << "doOfferCreate: saTakerGets=" << saTakerGets.getFullText();
|
||||
// We need to place the remainder of the offer into its order book.
|
||||
|
||||
// Add offer to owner's directory.
|
||||
terResult = dirAdd(uOwnerNode, Ledger::getOwnerDirIndex(mTxnAccountID), uLedgerIndex);
|
||||
terResult = mNodes.dirAdd(uOwnerNode, Ledger::getOwnerDirIndex(mTxnAccountID), uLedgerIndex);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
@@ -4271,7 +3856,7 @@ Log(lsWARNING) << "doOfferCreate: saTakerGets=" << saTakerGets.getFullText();
|
||||
uDirectory = Ledger::getQualityIndex(uBookBase, uRate); // Use original rate.
|
||||
|
||||
// Add offer to order book.
|
||||
terResult = dirAdd(uBookNode, uDirectory, uLedgerIndex);
|
||||
terResult = mNodes.dirAdd(uBookNode, uDirectory, uLedgerIndex);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
@@ -4418,7 +4003,7 @@ void TransactionEngine::calcOfferBridgeNext(
|
||||
uint256 uOfferIndex;
|
||||
|
||||
// Get uOfferIndex.
|
||||
dirNext(uBookRoot, uBookEnd, uBookDirIndex, uBookDirNode, uBookDirEntry, uOfferIndex);
|
||||
mNodes.dirNext(uBookRoot, uBookEnd, uBookDirIndex, uBookDirNode, uBookDirEntry, uOfferIndex);
|
||||
|
||||
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
|
||||
|
||||
@@ -4733,8 +4318,8 @@ TER TransactionEngine::doContractAdd(const SerializedTransaction& txn)
|
||||
Log(lsWARNING) << "doContractAdd> " << txn.getJson(0);
|
||||
|
||||
const uint32 expiration = txn.getITFieldU32(sfExpiration);
|
||||
const uint32 bondAmount = txn.getITFieldU32(sfBondAmount);
|
||||
const uint32 stampEscrow = txn.getITFieldU32(sfStampEscrow);
|
||||
// const uint32 bondAmount = txn.getITFieldU32(sfBondAmount);
|
||||
// const uint32 stampEscrow = txn.getITFieldU32(sfStampEscrow);
|
||||
STAmount rippleEscrow = txn.getITFieldAmount(sfRippleEscrow);
|
||||
std::vector<unsigned char> createCode = txn.getITFieldVL(sfCreateCode);
|
||||
std::vector<unsigned char> fundCode = txn.getITFieldVL(sfFundCode);
|
||||
|
||||
@@ -10,120 +10,11 @@
|
||||
#include "SerializedTransaction.h"
|
||||
#include "SerializedLedger.h"
|
||||
#include "LedgerEntrySet.h"
|
||||
#include "TransactionErr.h"
|
||||
|
||||
// A TransactionEngine applies serialized transactions to a ledger
|
||||
// It can also, verify signatures, verify fees, and give rejection reasons
|
||||
|
||||
enum TER // aka TransactionEngineResult
|
||||
{
|
||||
// Note: Range is stable. Exact numbers are currently unstable. Use tokens.
|
||||
|
||||
// -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit)
|
||||
// Only valid during non-consensus processing.
|
||||
// Implications:
|
||||
// - Not forwarded
|
||||
// - No fee check
|
||||
telLOCAL_ERROR = -399,
|
||||
telBAD_PATH_COUNT,
|
||||
telINSUF_FEE_P,
|
||||
|
||||
// -299 .. -200: M Malformed (bad signature)
|
||||
// Causes:
|
||||
// - Transaction corrupt.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
// - Reject
|
||||
// - Can not succeed in any imagined ledger.
|
||||
temMALFORMED = -299,
|
||||
temBAD_AMOUNT,
|
||||
temBAD_AUTH_MASTER,
|
||||
temBAD_EXPIRATION,
|
||||
temBAD_ISSUER,
|
||||
temBAD_OFFER,
|
||||
temBAD_PATH,
|
||||
temBAD_PATH_LOOP,
|
||||
temBAD_PUBLISH,
|
||||
temBAD_SET_ID,
|
||||
temCREATEXNS,
|
||||
temDST_IS_SRC,
|
||||
temDST_NEEDED,
|
||||
temINSUF_FEE_P,
|
||||
temINVALID,
|
||||
temREDUNDANT,
|
||||
temRIPPLE_EMPTY,
|
||||
temUNCERTAIN,
|
||||
temUNKNOWN,
|
||||
|
||||
// -199 .. -100: F Failure (sequence number previously used)
|
||||
// Causes:
|
||||
// - Transaction cannot succeed because of ledger state.
|
||||
// - Unexpected ledger state.
|
||||
// - C++ exception.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
// - Could succeed in an imagined ledger.
|
||||
tefFAILURE = -199,
|
||||
tefALREADY,
|
||||
tefBAD_ADD_AUTH,
|
||||
tefBAD_AUTH,
|
||||
tefBAD_CLAIM_ID,
|
||||
tefBAD_GEN_AUTH,
|
||||
tefBAD_LEDGER,
|
||||
tefCLAIMED,
|
||||
tefCREATED,
|
||||
tefEXCEPTION,
|
||||
tefGEN_IN_USE,
|
||||
tefPAST_SEQ,
|
||||
|
||||
// -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent)
|
||||
// Causes:
|
||||
// - Priror application of another, possibly non-existant, transaction could allow this transaction to succeed.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
// - Might succeed later
|
||||
// - Hold
|
||||
terRETRY = -99,
|
||||
terDIR_FULL,
|
||||
terFUNDS_SPENT,
|
||||
terINSUF_FEE_B,
|
||||
terNO_ACCOUNT,
|
||||
terNO_DST,
|
||||
terNO_LINE,
|
||||
terNO_LINE_NO_ZERO,
|
||||
terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure.
|
||||
terPRE_SEQ,
|
||||
terSET_MISSING_DST,
|
||||
terUNFUNDED,
|
||||
|
||||
// 0: S Success (success)
|
||||
// Causes:
|
||||
// - Success.
|
||||
// Implications:
|
||||
// - Applied
|
||||
// - Forwarded
|
||||
tesSUCCESS = 0,
|
||||
|
||||
// 100 .. P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account)
|
||||
// Causes:
|
||||
// - Success, but does not achieve optimal result.
|
||||
// Implications:
|
||||
// - Applied
|
||||
// - Forwarded
|
||||
// Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY.
|
||||
tepPARTIAL = 100,
|
||||
tepPATH_DRY,
|
||||
tepPATH_PARTIAL,
|
||||
};
|
||||
|
||||
#define isTemMalformed(x) ((x) >= temMALFORMED && (x) < tefFAILURE)
|
||||
#define isTefFailure(x) ((x) >= tefFAILURE && (x) < terRETRY)
|
||||
#define isTepPartial(x) ((x) >= tepPATH_PARTIAL)
|
||||
|
||||
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman);
|
||||
|
||||
enum TransactionEngineParams
|
||||
{
|
||||
tapNONE = 0x00,
|
||||
@@ -266,21 +157,6 @@ class TransactionEngine
|
||||
private:
|
||||
LedgerEntrySet mNodes;
|
||||
|
||||
TER dirAdd(
|
||||
uint64& uNodeDir, // Node of entry.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex);
|
||||
|
||||
TER dirDelete(
|
||||
const bool bKeepRoot,
|
||||
const uint64& uNodeDir, // Node item is mentioned in.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex, // Item being deleted
|
||||
const bool bStable);
|
||||
|
||||
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
|
||||
TER setAuthorized(const SerializedTransaction& txn, bool bMustSetGenerator);
|
||||
|
||||
TER takeOffers(
|
||||
|
||||
91
src/TransactionErr.cpp
Normal file
91
src/TransactionErr.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "TransactionErr.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
|
||||
{
|
||||
static struct {
|
||||
TER terCode;
|
||||
const char* cpToken;
|
||||
const char* cpHuman;
|
||||
} transResultInfoA[] = {
|
||||
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger" },
|
||||
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
|
||||
{ tefBAD_AUTH, "tefBAD_AUTH", "Transaction's public key is not authorized." },
|
||||
{ tefBAD_CLAIM_ID, "tefBAD_CLAIM_ID", "Malformed." },
|
||||
{ tefBAD_GEN_AUTH, "tefBAD_GEN_AUTH", "Not authorized to claim generator." },
|
||||
{ tefBAD_LEDGER, "tefBAD_LEDGER", "Ledger in unexpected state." },
|
||||
{ tefCLAIMED, "tefCLAIMED", "Can not claim a previously claimed account." },
|
||||
{ tefEXCEPTION, "tefEXCEPTION", "Unexpected program state." },
|
||||
{ tefCREATED, "tefCREATED", "Can't add an already created account." },
|
||||
{ tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." },
|
||||
{ tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past" },
|
||||
|
||||
{ telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: too many paths." },
|
||||
{ telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." },
|
||||
|
||||
{ temBAD_AMOUNT, "temBAD_AMOUNT", "Can only send positive amounts." },
|
||||
{ temBAD_AUTH_MASTER, "temBAD_AUTH_MASTER", "Auth for unclaimed account needs correct master key." },
|
||||
{ temBAD_EXPIRATION, "temBAD_EXPIRATION", "Malformed." },
|
||||
{ temBAD_ISSUER, "temBAD_ISSUER", "Malformed." },
|
||||
{ temBAD_OFFER, "temBAD_OFFER", "Malformed." },
|
||||
{ temBAD_PATH, "temBAD_PATH", "Malformed." },
|
||||
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed." },
|
||||
{ temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: bad publish." },
|
||||
{ temBAD_SET_ID, "temBAD_SET_ID", "Malformed." },
|
||||
{ temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." },
|
||||
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
||||
{ temDST_NEEDED, "temDST_NEEDED", "Destination not specified." },
|
||||
{ temINSUF_FEE_P, "temINSUF_FEE_P", "Fee not allowed." },
|
||||
{ temINVALID, "temINVALID", "The transaction is ill-formed" },
|
||||
{ temREDUNDANT, "temREDUNDANT", "Sends same currency to self." },
|
||||
{ temRIPPLE_EMPTY, "temRIPPLE_EMPTY", "PathSet with no paths." },
|
||||
{ temUNCERTAIN, "temUNCERTAIN", "In process of determining result. Never returned." },
|
||||
{ temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet." },
|
||||
|
||||
{ tepPATH_DRY, "tepPATH_DRY", "Path could not send partial amount." },
|
||||
{ tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." },
|
||||
|
||||
{ terDIR_FULL, "terDIR_FULL", "Can not add entry to full dir." },
|
||||
{ terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." },
|
||||
{ terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
|
||||
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." },
|
||||
{ terNO_DST, "terNO_DST", "The destination does not exist" },
|
||||
{ terNO_LINE, "terNO_LINE", "No such line." },
|
||||
{ terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." },
|
||||
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
|
||||
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction" },
|
||||
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
|
||||
{ terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." },
|
||||
|
||||
{ tesSUCCESS, "tesSUCCESS", "The transaction was applied" },
|
||||
};
|
||||
|
||||
int iIndex = NUMBER(transResultInfoA);
|
||||
|
||||
while (iIndex-- && transResultInfoA[iIndex].terCode != terCode)
|
||||
;
|
||||
|
||||
if (iIndex >= 0)
|
||||
{
|
||||
strToken = transResultInfoA[iIndex].cpToken;
|
||||
strHuman = transResultInfoA[iIndex].cpHuman;
|
||||
}
|
||||
|
||||
return iIndex >= 0;
|
||||
}
|
||||
|
||||
std::string transToken(TER terCode)
|
||||
{
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
return transResultInfo(terCode, strToken, strHuman) ? strToken : "-";
|
||||
}
|
||||
|
||||
std::string transHuman(TER terCode)
|
||||
{
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
return transResultInfo(terCode, strToken, strHuman) ? strHuman : "-";
|
||||
}
|
||||
118
src/TransactionErr.h
Normal file
118
src/TransactionErr.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef _TRANSACTION_ERR_
|
||||
#define _TRANSACTION_ERR_
|
||||
|
||||
#include <string>
|
||||
|
||||
enum TER // aka TransactionEngineResult
|
||||
{
|
||||
// Note: Range is stable. Exact numbers are currently unstable. Use tokens.
|
||||
|
||||
// -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit)
|
||||
// Only valid during non-consensus processing.
|
||||
// Implications:
|
||||
// - Not forwarded
|
||||
// - No fee check
|
||||
telLOCAL_ERROR = -399,
|
||||
telBAD_PATH_COUNT,
|
||||
telINSUF_FEE_P,
|
||||
|
||||
// -299 .. -200: M Malformed (bad signature)
|
||||
// Causes:
|
||||
// - Transaction corrupt.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
// - Reject
|
||||
// - Can not succeed in any imagined ledger.
|
||||
temMALFORMED = -299,
|
||||
temBAD_AMOUNT,
|
||||
temBAD_AUTH_MASTER,
|
||||
temBAD_EXPIRATION,
|
||||
temBAD_ISSUER,
|
||||
temBAD_OFFER,
|
||||
temBAD_PATH,
|
||||
temBAD_PATH_LOOP,
|
||||
temBAD_PUBLISH,
|
||||
temBAD_SET_ID,
|
||||
temCREATEXNS,
|
||||
temDST_IS_SRC,
|
||||
temDST_NEEDED,
|
||||
temINSUF_FEE_P,
|
||||
temINVALID,
|
||||
temREDUNDANT,
|
||||
temRIPPLE_EMPTY,
|
||||
temUNCERTAIN,
|
||||
temUNKNOWN,
|
||||
|
||||
// -199 .. -100: F Failure (sequence number previously used)
|
||||
// Causes:
|
||||
// - Transaction cannot succeed because of ledger state.
|
||||
// - Unexpected ledger state.
|
||||
// - C++ exception.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
// - Could succeed in an imagined ledger.
|
||||
tefFAILURE = -199,
|
||||
tefALREADY,
|
||||
tefBAD_ADD_AUTH,
|
||||
tefBAD_AUTH,
|
||||
tefBAD_CLAIM_ID,
|
||||
tefBAD_GEN_AUTH,
|
||||
tefBAD_LEDGER,
|
||||
tefCLAIMED,
|
||||
tefCREATED,
|
||||
tefEXCEPTION,
|
||||
tefGEN_IN_USE,
|
||||
tefPAST_SEQ,
|
||||
|
||||
// -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent)
|
||||
// Causes:
|
||||
// - Priror application of another, possibly non-existant, transaction could allow this transaction to succeed.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
// - Might succeed later
|
||||
// - Hold
|
||||
terRETRY = -99,
|
||||
terDIR_FULL,
|
||||
terFUNDS_SPENT,
|
||||
terINSUF_FEE_B,
|
||||
terNO_ACCOUNT,
|
||||
terNO_DST,
|
||||
terNO_LINE,
|
||||
terNO_LINE_NO_ZERO,
|
||||
terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure.
|
||||
terPRE_SEQ,
|
||||
terSET_MISSING_DST,
|
||||
terUNFUNDED,
|
||||
|
||||
// 0: S Success (success)
|
||||
// Causes:
|
||||
// - Success.
|
||||
// Implications:
|
||||
// - Applied
|
||||
// - Forwarded
|
||||
tesSUCCESS = 0,
|
||||
|
||||
// 100 .. P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account)
|
||||
// Causes:
|
||||
// - Success, but does not achieve optimal result.
|
||||
// Implications:
|
||||
// - Applied
|
||||
// - Forwarded
|
||||
// Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY.
|
||||
tepPARTIAL = 100,
|
||||
tepPATH_DRY,
|
||||
tepPATH_PARTIAL,
|
||||
};
|
||||
|
||||
#define isTemMalformed(x) ((x) >= temMALFORMED && (x) < tefFAILURE)
|
||||
#define isTefFailure(x) ((x) >= tefFAILURE && (x) < terRETRY)
|
||||
#define isTepPartial(x) ((x) >= tepPATH_PARTIAL)
|
||||
|
||||
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman);
|
||||
std::string transToken(TER terCode);
|
||||
std::string transHuman(TER terCode);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user