Implement credit_set and directory support.

This commit is contained in:
Arthur Britto
2012-05-26 02:57:00 -07:00
parent 451df81411
commit cb56ad036d
7 changed files with 471 additions and 205 deletions

View File

@@ -130,15 +130,16 @@ int RPCServer::getParamCount(const Json::Value& params)
return 1; return 1;
} }
#if 0
// now, expire, n // now, expire, n
bool RPCServer::parseBorrowRate(const std::string& sBorrowRate) bool RPCServer::parseAcceptRate(const std::string& sAcceptRate)
{ {
if (!sBorrowRate.compare("expire")) if (!sAcceptRate.compare("expire"))
0; 0;
return true; return true;
} }
#endif
bool RPCServer::extractString(std::string& param, const Json::Value& params, int index) bool RPCServer::extractString(std::string& param, const Json::Value& params, int index)
{ {
@@ -373,30 +374,16 @@ Json::Value RPCServer::doPeers(Json::Value& params)
return theApp->getConnectionPool().getPeersJson(); return theApp->getConnectionPool().getPeersJson();
} }
// credit_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<borrow_rate>] [<borrow_start>] [<borrow_expire>] // credit_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<accept_rate>]
Json::Value RPCServer::doCreditSet(Json::Value& params) Json::Value RPCServer::doCreditSet(Json::Value& params)
{ {
NewcoinAddress naSeed; NewcoinAddress naSeed;
NewcoinAddress naSrcAccountID; NewcoinAddress naSrcAccountID;
NewcoinAddress naDstAccountID; NewcoinAddress naDstAccountID;
STAmount saLimitAmount; STAmount saLimitAmount;
std::string sBorrowRate; uint32 uAcceptRate = params.size() >= 6 ? boost::lexical_cast<uint32>(params[5u].asString()) : 0;
std::string sBorrowStart;
std::string sBorrowExpire;
uint32 uBorrowRate;
uint32 uBorrowStart;
uint32 uBorrowExpire;
if (params.size() >= 6) if (params.size() < 5 || params.size() > 6)
sBorrowRate = params[6u].asString();
if (params.size() >= 7)
sBorrowStart = params[7u].asString();
if (params.size() >= 8)
sBorrowExpire = params[8u].asString();
if (params.size() < 5 || params.size() > 8)
{ {
return JSONRPCError(500, "invalid parameters"); return JSONRPCError(500, "invalid parameters");
} }
@@ -412,7 +399,7 @@ Json::Value RPCServer::doCreditSet(Json::Value& params)
{ {
return JSONRPCError(500, "destination account id needed"); return JSONRPCError(500, "destination account id needed");
} }
else if (!saLimitAmount.setValue(params[5u].asString(), params[6u].asString())) else if (!saLimitAmount.setValue(params[3u].asString(), params[4u].asString()))
{ {
return JSONRPCError(500, "bad src amount/currency"); return JSONRPCError(500, "bad src amount/currency");
} }
@@ -427,16 +414,10 @@ Json::Value RPCServer::doCreditSet(Json::Value& params)
Json::Value obj = authorize(naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, sleSrc); Json::Value obj = authorize(naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, sleSrc);
if (!obj.empty()) if (!obj.empty())
{
return obj; return obj;
}
STAmount saSrcBalance = sleSrc->getIValueFieldAmount(sfBalance); STAmount saSrcBalance = sleSrc->getIValueFieldAmount(sfBalance);
uBorrowRate = 0;
uBorrowStart = 0;
uBorrowExpire = 0;
if (saSrcBalance < theConfig.FEE_DEFAULT) if (saSrcBalance < theConfig.FEE_DEFAULT)
{ {
return JSONRPCError(500, "insufficent funds"); return JSONRPCError(500, "insufficent funds");
@@ -451,9 +432,7 @@ Json::Value RPCServer::doCreditSet(Json::Value& params)
0, // YYY No source tag 0, // YYY No source tag
naDstAccountID, naDstAccountID,
saLimitAmount, saLimitAmount,
uBorrowRate, uAcceptRate);
uBorrowStart,
uBorrowExpire);
(void) theApp->getOPs().processTransaction(trans); (void) theApp->getOPs().processTransaction(trans);
@@ -463,9 +442,7 @@ Json::Value RPCServer::doCreditSet(Json::Value& params)
obj["srcAccountID"] = naSrcAccountID.humanAccountID(); obj["srcAccountID"] = naSrcAccountID.humanAccountID();
obj["dstAccountID"] = naDstAccountID.humanAccountID(); obj["dstAccountID"] = naDstAccountID.humanAccountID();
obj["limitAmount"] = saLimitAmount.getText(); obj["limitAmount"] = saLimitAmount.getText();
obj["borrowRate"] = uBorrowRate; obj["acceptRate"] = uAcceptRate;
obj["borrowStart"] = uBorrowStart;
obj["borrowExpire"] = uBorrowExpire;
return obj; return obj;
} }

View File

@@ -190,18 +190,12 @@ Transaction::pointer Transaction::setCreditSet(
const NewcoinAddress& naPrivateKey, const NewcoinAddress& naPrivateKey,
const NewcoinAddress& naDstAccountID, const NewcoinAddress& naDstAccountID,
const STAmount& saLimitAmount, const STAmount& saLimitAmount,
uint32 uBorrowRate, uint32 uAcceptRate)
uint32 uBorrowStart,
uint32 uBorrowExpire)
{ {
mTransaction->setITFieldAccount(sfDestination, naDstAccountID); mTransaction->setITFieldAccount(sfDestination, naDstAccountID);
mTransaction->setITFieldAmount(sfLimitAmount, saLimitAmount); mTransaction->setITFieldAmount(sfLimitAmount, saLimitAmount);
if (uBorrowRate) if (uAcceptRate)
mTransaction->setITFieldU32(sfBorrowRate, uBorrowRate); mTransaction->setITFieldU32(sfAcceptRate, uAcceptRate);
if (uBorrowStart)
mTransaction->setITFieldU32(sfBorrowStart, uBorrowStart);
if (uBorrowExpire)
mTransaction->setITFieldU32(sfBorrowExpire, uBorrowExpire);
sign(naPrivateKey); sign(naPrivateKey);
@@ -216,15 +210,13 @@ Transaction::pointer Transaction::sharedCreditSet(
uint32 uSourceTag, uint32 uSourceTag,
const NewcoinAddress& naDstAccountID, const NewcoinAddress& naDstAccountID,
const STAmount& saLimitAmount, const STAmount& saLimitAmount,
uint32 uBorrowRate, uint32 uAcceptRate)
uint32 uBorrowStart,
uint32 uBorrowExpire)
{ {
pointer tResult = boost::make_shared<Transaction>(ttCREDIT_SET, pointer tResult = boost::make_shared<Transaction>(ttCREDIT_SET,
naPublicKey, naSourceAccount, naPublicKey, naSourceAccount,
uSeq, saFee, uSourceTag); uSeq, saFee, uSourceTag);
return tResult->setCreditSet(naPrivateKey, naDstAccountID, saLimitAmount, uBorrowRate, uBorrowStart, uBorrowExpire); return tResult->setCreditSet(naPrivateKey, naDstAccountID, saLimitAmount, uAcceptRate);
} }
// //

View File

@@ -61,9 +61,7 @@ private:
const NewcoinAddress& naPrivateKey, const NewcoinAddress& naPrivateKey,
const NewcoinAddress& naDstAccountID, const NewcoinAddress& naDstAccountID,
const STAmount& saLimitAmount, const STAmount& saLimitAmount,
uint32 uBorrowRate, uint32 uAcceptRate);
uint32 uBorrowStart,
uint32 uBorrowExpire);
Transaction::pointer setPayment( Transaction::pointer setPayment(
const NewcoinAddress& naPrivateKey, const NewcoinAddress& naPrivateKey,
@@ -119,9 +117,7 @@ public:
uint32 uSourceTag, uint32 uSourceTag,
const NewcoinAddress& naDstAccountID, const NewcoinAddress& naDstAccountID,
const STAmount& saLimitAmount, const STAmount& saLimitAmount,
uint32 uBorrowRate, uint32 uAcceptRate);
uint32 uBorrowStart,
uint32 uBorrowExpire);
// Make a payment. // Make a payment.
static Transaction::pointer sharedPayment( static Transaction::pointer sharedPayment(

View File

@@ -2,9 +2,216 @@
#include "Config.h" #include "Config.h"
#include "TransactionFormats.h" #include "TransactionFormats.h"
#include "utils.h"
#include <boost/format.hpp> #include <boost/format.hpp>
typedef SerializedLedgerEntry SLE;
#define DIR_NODE_MAX 32
// We return the uNodeDir so that on delete we can quickly know where the element is mentioned in the directory.
TransactionEngineResult TransactionEngine::dirAdd(
std::vector<AffectedAccount>& accounts,
uint64& uNodeDir,
const LedgerEntryType letKind,
const uint256& uBase,
const uint256& uLedgerIndex)
{
// Get the root.
uint256 uRootIndex = Ledger::getDirIndex(uBase, letKind);
LedgerStateParms lspRoot = lepNONE;
SLE::pointer sleRoot = mLedger->getDirRoot(lspRoot, uRootIndex);
bool bRootNew;
// Get the last node index.
if (sleRoot)
{
bRootNew = false;
uNodeDir = sleRoot->getIFieldU64(sfLastNode);
}
else
{
bRootNew = true;
uNodeDir = 1;
sleRoot = boost::make_shared<SerializedLedgerEntry>(ltDIR_ROOT);
sleRoot->setIndex(uRootIndex);
sleRoot->setIFieldU64(sfFirstNode, uNodeDir);
sleRoot->setIFieldU64(sfLastNode, uNodeDir);
accounts.push_back(std::make_pair(taaCREATE, sleRoot));
}
// Get the last node.
uint256 uNodeIndex = Ledger::getDirIndex(uBase, letKind, uNodeDir);
LedgerStateParms lspNode = lepNONE;
SLE::pointer sleNode = bRootNew ? SLE::pointer() : mLedger->getDirNode(lspNode, uNodeIndex);
if (sleNode)
{
STVector256 svIndexes;
svIndexes = sleNode->getIFieldV256(sfIndexes);
if (DIR_NODE_MAX != svIndexes.peekValue().size())
{
// Last node is not full, append.
svIndexes.peekValue().push_back(uLedgerIndex);
sleNode->setIFieldV256(sfIndexes, svIndexes);
accounts.push_back(std::make_pair(taaMODIFY, sleNode));
}
// Last node is full, add a new node.
else if (!++uNodeDir)
{
return terDIR_FULL;
}
else
{
// Record new last node.
sleNode = SLE::pointer();
sleRoot->setIFieldU64(sfLastNode, uNodeDir);
accounts.push_back(std::make_pair(taaMODIFY, sleRoot));
}
}
if (!sleNode)
{
// Add to last node, which is empty.
sleNode = boost::make_shared<SerializedLedgerEntry>(ltDIR_NODE);
sleNode->setIndex(uNodeIndex);
STVector256 svIndexes;
svIndexes.peekValue().push_back(uLedgerIndex);
sleNode->setIFieldV256(sfIndexes, svIndexes);
accounts.push_back(std::make_pair(taaCREATE, sleNode));
}
return terSUCCESS;
}
TransactionEngineResult TransactionEngine::dirDelete(
std::vector<AffectedAccount>& accounts,
const uint64& uNodeDir,
const LedgerEntryType letKind,
const uint256& uBase,
const uint256& uLedgerIndex)
{
uint64 uNodeCur = uNodeDir;
uint256 uNodeIndex = Ledger::getDirIndex(uBase, letKind, uNodeCur);
LedgerStateParms lspNode = lepNONE;
SLE::pointer sleNode = mLedger->getDirNode(lspNode, uNodeIndex);
if (!sleNode)
{
std::cerr << "dirDelete: no such node" << std::endl;
return terNODE_NOT_FOUND;
}
else
{
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
std::vector<uint256>::iterator it;
it = std::find(vuiIndexes.begin(), vuiIndexes.end(), uLedgerIndex);
if (vuiIndexes.end() == it)
{
std::cerr << "dirDelete: node not mentioned" << std::endl;
return terNODE_NOT_MENTIONED;
}
else
{
// Get root information
LedgerStateParms lspRoot = lepNONE;
SLE::pointer sleRoot = mLedger->getDirRoot(lspRoot, Ledger::getDirIndex(uBase, letKind));
if (!sleRoot)
{
std::cerr << "dirDelete: root node is missing" << std::endl;
return terNODE_NO_ROOT;
}
uint64 uFirstNodeOrig = sleRoot->getIFieldU64(sfFirstNode);
uint64 uLastNodeOrig = sleRoot->getIFieldU64(sfLastNode);
uint64 uFirstNode = uFirstNodeOrig;
uint64 uLastNode = uLastNodeOrig;
// Remove the element.
if (vuiIndexes.size() > 1)
{
*it = vuiIndexes[vuiIndexes.size()-1];
}
vuiIndexes.resize(vuiIndexes.size()-1);
sleNode->setIFieldV256(sfIndexes, svIndexes);
if (!vuiIndexes.empty() || (uFirstNode != uNodeCur && uLastNode != uNodeCur))
{
// Node is not being deleted.
accounts.push_back(std::make_pair(taaMODIFY, sleNode));
}
while (uFirstNode && svIndexes.peekValue().empty()
&& (uFirstNode == uNodeCur || uLastNode == uNodeCur))
{
// Current node is empty and first or last, delete it.
accounts.push_back(std::make_pair(taaDELETE, sleNode));
if (uFirstNode == uLastNode)
{
// Complete deletion.
uFirstNode = 0;
}
else
{
if (uFirstNode == uNodeCur)
{
// Advance first node
++uNodeCur;
++uFirstNode;
}
else
{
// Rewind last node
--uNodeCur;
--uLastNode;
}
// Get replacement node.
lspNode = lepNONE;
sleNode = mLedger->getDirNode(lspNode, Ledger::getDirIndex(uBase, letKind, uNodeCur));
svIndexes = sleNode->getIFieldV256(sfIndexes);
}
}
if (uFirstNode == uFirstNodeOrig && uLastNode == uLastNodeOrig)
{
// Dir is fine.
nothing();
}
else if (uFirstNode)
{
// Update root's pointer pointers.
sleRoot->setIFieldU64(sfFirstNode, uFirstNode);
sleRoot->setIFieldU64(sfLastNode, uLastNode);
accounts.push_back(std::make_pair(taaMODIFY, sleRoot));
}
else
{
// Delete the root.
accounts.push_back(std::make_pair(taaDELETE, sleRoot));
}
}
return terSUCCESS;
}
}
TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTransaction& txn, TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTransaction& txn,
TransactionEngineParams params) TransactionEngineParams params)
{ {
@@ -106,8 +313,8 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran
// find source account // find source account
// If we are only verifying some transactions, this would be probablistic. // If we are only verifying some transactions, this would be probablistic.
LedgerStateParms qry = lepNONE; LedgerStateParms lspRoot = lepNONE;
SerializedLedgerEntry::pointer sleSrc = mLedger->getAccountRoot(qry, srcAccountID); SLE::pointer sleSrc = mLedger->getAccountRoot(lspRoot, srcAccountID);
if (!sleSrc) if (!sleSrc)
{ {
std::cerr << str(boost::format("applyTransaction: Delay transaction: source account does not exisit: %s") % txn.getSourceAccount().humanAccountID()) << std::endl; std::cerr << str(boost::format("applyTransaction: Delay transaction: source account does not exisit: %s") % txn.getSourceAccount().humanAccountID()) << std::endl;
@@ -178,7 +385,7 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran
break; break;
case ttCREDIT_SET: case ttCREDIT_SET:
result = doCreditSet(txn, accounts); result = doCreditSet(txn, accounts, srcAccountID);
break; break;
case ttINVALID: case ttINVALID:
@@ -257,22 +464,22 @@ TransactionEngineResult TransactionEngine::doClaim(const SerializedTransaction&
} }
LedgerStateParms qry = lepNONE; LedgerStateParms qry = lepNONE;
SerializedLedgerEntry::pointer dest = accounts[0].second; SLE::pointer sleDst = accounts[0].second;
if (!dest) if (!sleDst)
{ {
// Source account does not exist. Could succeed if it was created first. // Source account does not exist. Could succeed if it was created first.
std::cerr << str(boost::format("doClaim: no such account: %s") % txn.getSourceAccount().humanAccountID()) << std::endl; std::cerr << str(boost::format("doClaim: no such account: %s") % txn.getSourceAccount().humanAccountID()) << std::endl;
return terNO_ACCOUNT; return terNO_ACCOUNT;
} }
std::cerr << str(boost::format("doClaim: %s") % dest->getFullText()) << std::endl; std::cerr << str(boost::format("doClaim: %s") % sleDst->getFullText()) << std::endl;
if (dest->getIFieldPresent(sfAuthorizedKey)) if (sleDst->getIFieldPresent(sfAuthorizedKey))
{ {
// Source account already claimed. // Source account already claimed.
std::cerr << "doClaim: source already claimed" << std::endl; std::cerr << "doClaim: source already claimed" << std::endl;
return tenCLAIMED; return terCLAIMED;
} }
// //
@@ -300,8 +507,8 @@ TransactionEngineResult TransactionEngine::doClaim(const SerializedTransaction&
uint160 hGeneratorID = naAccountPublic.getAccountID(); uint160 hGeneratorID = naAccountPublic.getAccountID();
qry = lepNONE; qry = lepNONE;
SerializedLedgerEntry::pointer gen = mLedger->getGenerator(qry, hGeneratorID); SLE::pointer sleGen = mLedger->getGenerator(qry, hGeneratorID);
if (gen) if (sleGen)
{ {
// Generator is already in use. Regular passphrases limited to one wallet. // Generator is already in use. Regular passphrases limited to one wallet.
std::cerr << "doClaim: generator already in use" << std::endl; std::cerr << "doClaim: generator already in use" << std::endl;
@@ -313,41 +520,132 @@ TransactionEngineResult TransactionEngine::doClaim(const SerializedTransaction&
// //
// Set the public key needed to use the account. // Set the public key needed to use the account.
dest->setIFieldH160(sfAuthorizedKey, hGeneratorID); sleDst->setIFieldH160(sfAuthorizedKey, hGeneratorID);
// Construct a generator map entry. // Construct a generator map entry.
gen = boost::make_shared<SerializedLedgerEntry>(ltGENERATOR_MAP); sleGen = boost::make_shared<SerializedLedgerEntry>(ltGENERATOR_MAP);
gen->setIndex(Ledger::getGeneratorIndex(hGeneratorID)); sleGen->setIndex(Ledger::getGeneratorIndex(hGeneratorID));
gen->setIFieldH160(sfGeneratorID, hGeneratorID); sleGen->setIFieldH160(sfGeneratorID, hGeneratorID);
gen->setIFieldVL(sfGenerator, vucCipher); sleGen->setIFieldVL(sfGenerator, vucCipher);
accounts.push_back(std::make_pair(taaCREATE, gen)); accounts.push_back(std::make_pair(taaCREATE, sleGen));
std::cerr << "doClaim<" << std::endl; std::cerr << "doClaim<" << std::endl;
return terSUCCESS; return terSUCCESS;
} }
TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransaction&, std::vector<AffectedAccount>&) TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransaction& txn,
std::vector<AffectedAccount>&accounts,
const uint160& uSrcAccountID)
{ {
return tenINVALID; TransactionEngineResult terResult = terSUCCESS;
std::cerr << "doCreditSet>" << std::endl;
// Check if destination makes sense.
uint160 uDstAccountID = txn.getITFieldAccount(sfDestination);
if (!uDstAccountID)
{
std::cerr << "doCreditSet: Invalid transaction: Payment destination account not specifed." << std::endl;
return tenDST_NEEDED;
}
else if (uSrcAccountID == uDstAccountID)
{
std::cerr << "doCreditSet: Invalid transaction: Source account is the same as destination." << std::endl;
return tenDST_IS_SRC;
}
LedgerStateParms qry = lepNONE;
SLE::pointer sleDst = mLedger->getAccountRoot(qry, uDstAccountID);
if (!sleDst)
{
std::cerr << "doCreditSet: Delay transaction: Destination account does not exist." << std::endl;
return terNO_DST;
}
STAmount saLimitAmount = txn.getITFieldAmount(sfLimitAmount);
uint160 uCurrency = saLimitAmount.getCurrency();
bool bSltD = uSrcAccountID < uDstAccountID;
uint32 uFlags = bSltD ? lsfLowIndexed : lsfHighIndexed;
STAmount saBalance(uCurrency);
bool bAddIndex;
qry = lepNONE;
SLE::pointer sleRippleState = mLedger->getRippleState(qry, uSrcAccountID, uDstAccountID, uCurrency);
if (sleRippleState)
{
std::cerr << "doCreditSet: Modifying ripple line." << std::endl;
bAddIndex = !(sleRippleState->getFlags() & uFlags);
sleRippleState->setIFieldAmount(bSltD ? sfLowLimit : sfHighID, saLimitAmount);
accounts.push_back(std::make_pair(taaMODIFY, sleRippleState));
if (bAddIndex)
sleRippleState->setFlag(uFlags);
}
// Line does not exist.
else if (!saLimitAmount.getValue())
{
std::cerr << "doCreditSet: Setting non-existant ripple line to 0." << std::endl;
return terNO_LINE_NO_ZERO;
}
else
{
std::cerr << "doCreditSet: Creating ripple line." << std::endl;
STAmount saZero(uCurrency);
bAddIndex = true;
sleRippleState = boost::make_shared<SerializedLedgerEntry>(ltRIPPLE_STATE);
sleRippleState->setIndex(Ledger::getRippleStateIndex(uSrcAccountID, uDstAccountID, uCurrency));
sleRippleState->setFlag(uFlags);
sleRippleState->setIFieldAmount(sfBalance, saZero); // Zero balance in currency.
sleRippleState->setIFieldAmount(bSltD ? sfLowLimit : sfHighLimit, saLimitAmount);
sleRippleState->setIFieldAmount(bSltD ? sfHighLimit : sfLowLimit, saZero);
sleRippleState->setIFieldAccount(bSltD ? sfLowID : sfHighID, uSrcAccountID);
sleRippleState->setIFieldAccount(bSltD ? sfHighID : sfLowID, uDstAccountID);
accounts.push_back(std::make_pair(taaCREATE, sleRippleState));
}
if (bAddIndex)
{
// Add entries so clients can find lines.
// - Client needs to be able to walk who account has given credit to and who has account's credit.
// - Client doesn't need to know every account who has extended credit but it owed nothing.
uint64 uSrcRef; // Ignored, ripple_state dirs never delete.
// XXX Verify extend is passing the right bits, not the zero bits.
// XXX Make dirAdd more flexiable to take vector.
terResult = dirAdd(accounts, uSrcRef, ltRIPPLE_STATE, uint160extend256(uSrcAccountID, 0), sleRippleState->getIndex());
}
std::cerr << "doCreditSet<" << std::endl;
return terResult;
} }
TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction& txn, TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction& txn,
std::vector<AffectedAccount>& accounts, std::vector<AffectedAccount>& accounts,
uint160 srcAccountID) const uint160& srcAccountID)
{ {
uint32 txFlags = txn.getFlags(); uint32 txFlags = txn.getFlags();
uint160 dstAccountID = txn.getITFieldAccount(sfDestination); uint160 uDstAccountID = txn.getITFieldAccount(sfDestination);
// Does the destination account exist? if (!uDstAccountID)
if (!dstAccountID)
{ {
std::cerr << "doPayment: Invalid transaction: Payment destination account not specifed." << std::endl; std::cerr << "doPayment: Invalid transaction: Payment destination account not specifed." << std::endl;
return tenINVALID; return tenINVALID;
} }
// XXX Only bad if no currency conversion in between through other people's offer. // XXX Only bad if no currency conversion in between through other people's offer.
else if (srcAccountID == dstAccountID) else if (srcAccountID == uDstAccountID)
{ {
std::cerr << "doPayment: Invalid transaction: Source account is the same as destination." << std::endl; std::cerr << "doPayment: Invalid transaction: Source account is the same as destination." << std::endl;
return tenINVALID; return tenINVALID;
@@ -355,24 +653,23 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
bool bCreate = !!(txFlags & tfCreateAccount); bool bCreate = !!(txFlags & tfCreateAccount);
uint160 currency; uint160 uCurrency;
if (txn.getITFieldPresent(sfCurrency)) if (txn.getITFieldPresent(sfCurrency))
{ {
currency = txn.getITFieldH160(sfCurrency); uCurrency = txn.getITFieldH160(sfCurrency);
if (!currency) if (!uCurrency)
{ {
std::cerr << "doPayment: Invalid transaction: XNC explicitly specified." << std::endl; std::cerr << "doPayment: Invalid transaction: " SYSTEM_CURRENCY_CODE " explicitly specified." << std::endl;
return tenEXPLICITXNC; return tenEXPLICITXNC;
} }
} }
LedgerStateParms qry = lepNONE; LedgerStateParms qry = lepNONE;
SLE::pointer sleDst = mLedger->getAccountRoot(qry, uDstAccountID);
SerializedLedgerEntry::pointer dest = mLedger->getAccountRoot(qry, dstAccountID); if (!sleDst)
if (!dest)
{ {
// Destination account does not exist. // Destination account does not exist.
if (bCreate && !!currency) if (bCreate && !!uCurrency)
{ {
std::cerr << "doPayment: Invalid transaction: Create account may only fund XBC." << std::endl; std::cerr << "doPayment: Invalid transaction: Create account may only fund XBC." << std::endl;
return tenCREATEXNC; return tenCREATEXNC;
@@ -380,32 +677,32 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
else if (!bCreate) else if (!bCreate)
{ {
std::cerr << "doPayment: Delay transaction: Destination account does not exist." << std::endl; std::cerr << "doPayment: Delay transaction: Destination account does not exist." << std::endl;
return terNO_TARGET; return terNO_DST;
} }
// Create the account. // Create the account.
dest = boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT); sleDst = boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT);
dest->setIndex(Ledger::getAccountRootIndex(dstAccountID)); sleDst->setIndex(Ledger::getAccountRootIndex(uDstAccountID));
dest->setIFieldAccount(sfAccount, dstAccountID); sleDst->setIFieldAccount(sfAccount, uDstAccountID);
dest->setIFieldU32(sfSequence, 1); sleDst->setIFieldU32(sfSequence, 1);
accounts.push_back(std::make_pair(taaCREATE, dest)); accounts.push_back(std::make_pair(taaCREATE, sleDst));
} }
// Destination exists. // Destination exists.
else if (bCreate) else if (bCreate)
{ {
std::cerr << "doPayment: Invalid transaction: Account already created." << std::endl; std::cerr << "doPayment: Invalid transaction: Account already created." << std::endl;
return tenCREATED; return terCREATED;
} }
else else
{ {
accounts.push_back(std::make_pair(taaMODIFY, dest)); accounts.push_back(std::make_pair(taaMODIFY, sleDst));
} }
STAmount saAmount = txn.getITFieldAmount(sfAmount); STAmount saAmount = txn.getITFieldAmount(sfAmount);
if (!currency) if (!uCurrency)
{ {
STAmount saSrcBalance = accounts[0].second->getIValueFieldAmount(sfBalance); STAmount saSrcBalance = accounts[0].second->getIValueFieldAmount(sfBalance);
@@ -430,8 +727,8 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
TransactionEngineResult TransactionEngine::doTransitSet(const SerializedTransaction& st, std::vector<AffectedAccount>&) TransactionEngineResult TransactionEngine::doTransitSet(const SerializedTransaction& st, std::vector<AffectedAccount>&)
{ {
std::cerr << "doTransitSet>" << std::endl; std::cerr << "doTransitSet>" << std::endl;
#if 0
SerializedLedgerEntry::pointer sleSrc = accounts[0].second; SLE::pointer sleSrc = accounts[0].second;
bool bTxnTransitRate = st->getIFieldPresent(sfTransitRate); bool bTxnTransitRate = st->getIFieldPresent(sfTransitRate);
bool bTxnTransitStart = st->getIFieldPresent(sfTransitStart); bool bTxnTransitStart = st->getIFieldPresent(sfTransitStart);
@@ -451,104 +748,85 @@ TransactionEngineResult TransactionEngine::doTransitSet(const SerializedTransact
uint32 uActNextTransitStart = bActNextTransitStart ? sleSrc->getIFieldU32(sfNextTransitStart) : 0; uint32 uActNextTransitStart = bActNextTransitStart ? sleSrc->getIFieldU32(sfNextTransitStart) : 0;
uint32 uActNextTransitExpire = bActNextTransitExpire ? sleSrc->getIFieldU32(sfNextTransitExpire) : 0; uint32 uActNextTransitExpire = bActNextTransitExpire ? sleSrc->getIFieldU32(sfNextTransitExpire) : 0;
// Try to overwrite next rate. //
// Update view
//
bool bBetterNextExpire = bool bNoCurrent = !bActTransitRate;
(!bTxnTransitExpire // Txn does not expire (same or extends) bool bCurrentExpired =
|| (bActNextTransitExpire && uTxnTransitExpire >= uActTransitExpire)); // Same or longer the next. bActTransitExpire // Current can expire
&& bActNextTransitStart // Have a replacement
&& uActTransitExpire <= uLedger; // Current is expired
bool bBetterNextRate = // Replace current with next if need.
!bActNextTransitRate // No next rate, txn is better. if (bNoCurrent // No current.
|| (uTxnTransitRate <= uActNextTransitRate); // Charge is the same or less. && bActNextTransitRate // Have next.
&& uActNextTransitStart <= uLedger) // Next has started.
// Must start at the same time or sooner.
bool bBetterNextStart =
// If starting sooner, must be better than current, or current must not protect range.
if (!bActNextTransitRate)
{ {
// No next. Overwrite next. // Make next current.
} uActTransitRate = uActNextTransitRate;
else if (uTxnTransitRate <= uActNextTransitRate && bBetterNextExpire) bActTransitExpire = bActNextTransitStart;
{ uActTransitExpire = uActNextTransitExpire;
// Better than
// Remove next.
uActNextTransitStart = 0;
} }
// True if no rate is active or going to be active; //
bool bNoRate = !bActTransitRate && !bActNextTransitRate; // Determine new transaction deposition.
//
bool bStartNow = bTxnTransitStart bool bBetterThanCurrent =
? uTxnTransitStart < uLedger !no current
: bActTransitRate || (
? bActTransitExpire Expires same or later than current
? Start before or same as current
: true // Current never expires. Fee same or less than current
: true; // Nothing is running. )
// True if new rate does not apply immediately. bool bBetterThanNext =
boo bRateIsLater = bTxnTransitStart !no next
? uTxnTransitStart < uLedger || (
Expires same or later than next
Start before or same as next
Fee same or less than next
)
: bActNextTransitRate && bTxnTransitExpire; bool bBetterThanBoth =
bBetterThanCurrent && bBetterThanNext
if (no_rate || rate_is_better || rate_is_later) bool bCurrentBlocks =
!bBetterThanCurrent
&& overlaps with current
bool bNextBlocks =
!bBetterThanNext
&& overlaps with next
if (bBetterThanBoth)
{ {
// Install. // Erase both and install.
if (starting)
// If not starting now, install as next.
}
else if (bCurrentBlocks || bNextBlocks)
{ {
// install now // Invalid ignore
}
else if (bBetterThanCurrent)
{
// Install over current
}
else if (bBetterThanNext)
{
// Install over next
} }
else else
{ {
// install next // Error.
} }
}
else
{
return tenTRANSIT_WORSE; return tenTRANSIT_WORSE;
}
if (no_rate && starting)
if (!bActTransitRate && !bActNextTransitRate) {
// No rate
bProtected = false;
} if (bActTransitRate && !bTx
if (bTxnTransitStart && uTxnTransitStart > uLedger)
{
// Start at a particular time.
}
else
{
// No start or start in the past, start now.
if (!bActTransitRate && !bActNextTransitRate)
{
// No current or future rate.
uDstTransitRate = uTxnTransitRate;
uDstTransitExpire = uTxnTransitExpire; // 0 for never expire.
}
else if (bActTransitRate && !bActNextTransitRate)
{
// Have a current rate and none pending.
if (!bActTransitExpire) {
// Current rate does not expire
}
}
else
{
// Have a future rate.
}
}
if (!bActTransitRate && !bActNextTransitRate)
{
}
// Set current. // Set current.
uDstTransitRate = uTxnTransitRate; uDstTransitRate = uTxnTransitRate;
@@ -560,7 +838,7 @@ TransactionEngineResult TransactionEngine::doTransitSet(const SerializedTransact
uDstNextTransitExpire = uTxnTransitExpire; // 0 for never expire. uDstNextTransitExpire = uTxnTransitExpire; // 0 for never expire.
if (txn.getITFieldPresent(sfCurrency)) if (txn.getITFieldPresent(sfCurrency))
#endif
std::cerr << "doTransitSet<" << std::endl; std::cerr << "doTransitSet<" << std::endl;
return tenINVALID; return tenINVALID;
} }

View File

@@ -18,11 +18,9 @@ enum TransactionEngineResult
tenGEN_IN_USE = -300, // Generator already in use. tenGEN_IN_USE = -300, // Generator already in use.
tenCREATEXNC, // Can not specify non XNC for Create. tenCREATEXNC, // Can not specify non XNC for Create.
tenEXPLICITXNC, // XNC is used by default, don't specify it. tenEXPLICITXNC, // XNC is used by default, don't specify it.
tenDST_NEEDED, // Destination not specified.
tenDST_IS_SRC, // Destination may not be source.
// Not possible due to ledger database: Fee claimed
tenCREATED = -200, // Can not create a previously created account.
tenCLAIMED, // Can not claim a previously claimed account.
tenTRANSIT_WORSE, // Can not override a better promise.
// Other // Other
tenFAILED = -100, // Something broke horribly tenFAILED = -100, // Something broke horribly
@@ -32,18 +30,27 @@ enum TransactionEngineResult
terSUCCESS = 0, // The transaction was applied terSUCCESS = 0, // The transaction was applied
// terFAILED_BUT_COULD_SUCEED = >0 // terFAILED_BUT_COULD_SUCCEED = >0
// Conflict with ledger database: Fee claimed
// Might succeed if not conflict is not caused by transaction ordering.
terALREADY, // The transaction was already in the ledger 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)
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
terBAD_SEQ, // This sequence number should be zero for prepaid transactions. terBAD_SEQ, // This sequence number should be zero for prepaid transactions.
terPRE_SEQ, // Missing/inapplicable prior transaction terCLAIMED, // Can not claim a previously claimed account.
terCREATED, // Can not create a previously created account.
terDIR_FULL, // Can not add entry to full dir.
terINSUF_FEE_B, // Account balance can't pay fee
terINSUF_FEE_T, // fee insufficient now (account doesn't exist, network load)
terNODE_NOT_FOUND, // Can not delete a dir node.
terNODE_NOT_MENTIONED,
terNODE_NO_ROOT,
terNO_ACCOUNT, // The source account does not exist
terNO_DST, // The destination does not exist
terNO_PATH, // No path existed or met transaction/balance requirements
terPAST_LEDGER, // The transaction expired and can't be applied terPAST_LEDGER, // The transaction expired and can't be applied
terPAST_SEQ, // This sequence number has already past
terPRE_SEQ, // Missing/inapplicable prior transaction
terUNFUNDED, // Source account had insufficient balance for transactin
terNO_LINE_NO_ZERO, // Can't zero non-existant line, destination might make it.
}; };
enum TransactionEngineParams enum TransactionEngineParams
@@ -65,17 +72,33 @@ typedef std::pair<TransactionAccountAction, SerializedLedgerEntry::pointer> Affe
class TransactionEngine class TransactionEngine
{ {
private:
TransactionEngineResult dirAdd(
std::vector<AffectedAccount>& accounts,
uint64& uNodeDir, // Node of entry.
const LedgerEntryType letKind,
const uint256& uBase,
const uint256& uLedgerIndex);
TransactionEngineResult dirDelete(
std::vector<AffectedAccount>& accounts,
const uint64& uNodeDir, // Node item is mentioned in.
const LedgerEntryType letKind,
const uint256& uBase, // Key of item.
const uint256& uLedgerIndex); // Item being deleted
protected: protected:
Ledger::pointer mLedger; Ledger::pointer mLedger;
TransactionEngineResult doCreditSet(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doCancel(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doCancel(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doClaim(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doClaim(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doCreditSet(const SerializedTransaction&, std::vector<AffectedAccount>&,
const uint160& srcAccountID);
TransactionEngineResult doDelete(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doDelete(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doInvoice(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doInvoice(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doOffer(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doOffer(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doPayment(const SerializedTransaction&, std::vector<AffectedAccount>&, TransactionEngineResult doPayment(const SerializedTransaction&, std::vector<AffectedAccount>&,
uint160 srcAccountID); const uint160& srcAccountID);
TransactionEngineResult doStore(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doStore(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doTake(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doTake(const SerializedTransaction&, std::vector<AffectedAccount>&);
TransactionEngineResult doTransitSet(const SerializedTransaction&, std::vector<AffectedAccount>&); TransactionEngineResult doTransitSet(const SerializedTransaction&, std::vector<AffectedAccount>&);

View File

@@ -18,9 +18,9 @@ TransactionFormat InnerTxnFormats[]=
{ S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 }, { S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 },
{ S_FIELD(Destination), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Destination), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(LimitAmount), STI_AMOUNT, SOE_REQUIRED, 0 }, { S_FIELD(LimitAmount), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(BorrowRate), STI_UINT32, SOE_IFFLAG, 1 }, { S_FIELD(AcceptRate), STI_UINT32, SOE_IFFLAG, 1 },
{ S_FIELD(BorrowStart), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(AcceptStart), STI_UINT32, SOE_IFFLAG, 2 },
{ S_FIELD(BorrowExpire), STI_UINT32, SOE_IFFLAG, 4 }, { S_FIELD(AcceptExpire), STI_UINT32, SOE_IFFLAG, 4 },
{ S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 }, { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 },
{ S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 },
{ sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } }

View File

@@ -40,7 +40,7 @@ void printHelp(const po::options_description& desc)
cout << " account_info <account>|<nickname>" << endl; cout << " account_info <account>|<nickname>" << endl;
cout << " account_info <seed>|<pass_phrase>|<key> [<index>]" << endl; cout << " account_info <seed>|<pass_phrase>|<key> [<index>]" << endl;
cout << " connect <ip> [<port>]" << endl; cout << " connect <ip> [<port>]" << endl;
cout << " credit_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<borrow_rate>] [<borrow_start>] [<borrow_expire>]" << endl; cout << " credit_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<account_rate>]" << endl;
cout << " ledger" << endl; cout << " ledger" << endl;
cout << " peers" << endl; cout << " peers" << endl;
cout << " send <seed> <paying_account> <account_id> <amount> [<currency>] [<send_max>] [<send_currency>]" << endl; cout << " send <seed> <paying_account> <account_id> <amount> [<currency>] [<send_max>] [<send_currency>]" << endl;