Add trust auto clear. Fixes #28

This commit is contained in:
Arthur Britto
2013-03-31 16:15:45 -07:00
parent bd4e00e391
commit 96733c2874
5 changed files with 200 additions and 27 deletions

View File

@@ -150,8 +150,9 @@ void LedgerEntrySet::entryCreate(SLE::ref sle)
case taaMODIFY:
throw std::runtime_error("Create after modify");
case taaCREATE:
throw std::runtime_error("Create after create"); // We could make this work
throw std::runtime_error("Create after create"); // This could be made to work
case taaCACHED:
throw std::runtime_error("Create after cache");
@@ -532,7 +533,7 @@ TER LedgerEntrySet::dirCount(const uint256& uRootIndex, uint32& uCount)
// <-- 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.
// 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,
@@ -850,7 +851,7 @@ bool LedgerEntrySet::dirFirst(
sleNode = entryCache(ltDIR_NODE, uRootIndex);
uDirEntry = 0;
assert(sleNode); // We never probe for directories.
assert(sleNode); // Never probe for directories.
return LedgerEntrySet::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
}
@@ -1243,13 +1244,38 @@ TER LedgerEntrySet::trustCreate(
ownerCountAdjust(!bSetDst ? uSrcAccountID : uDstAccountID, 1, sleAccount);
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); // ONLY: Create ripple balance.
}
return terResult;
}
// Direct send w/o fees: redeeming IOUs and/or sending own IOUs.
TER LedgerEntrySet::trustDelete(SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID)
{
bool bLowNode = sleRippleState->isFieldPresent(sfLowNode); // Detect legacy dirs.
bool bHighNode = sleRippleState->isFieldPresent(sfHighNode);
uint64 uLowNode = sleRippleState->getFieldU64(sfLowNode);
uint64 uHighNode = sleRippleState->getFieldU64(sfHighNode);
TER terResult;
cLog(lsTRACE) << "trustDelete: Deleting ripple line: low";
terResult = dirDelete(false, uLowNode, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false, !bLowNode);
if (tesSUCCESS == terResult)
{
cLog(lsTRACE) << "trustDelete: Deleting ripple line: high";
terResult = dirDelete(false, uHighNode, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false, !bHighNode);
}
cLog(lsINFO) << "trustDelete: Deleting ripple line: state";
entryDelete(sleRippleState);
return terResult;
}
// Direct send w/o fees:
// - Redeeming IOUs and/or sending sender's own IOUs.
// - Create trust line of needed.
TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer)
{
uint160 uIssuerID = saAmount.getIssuer();
@@ -1306,15 +1332,47 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei
% saAmount.getFullText()
% saBalance.getFullText());
bool bDelete = false;
uint32 uFlags;
// YYY Could skip this if rippling in reverse.
if (saBefore.isPositive() // Sender balance was positive.
&& !saBalance.isPositive() // Sender is zero or negative.
&& isSetBit((uFlags = sleRippleState->getFieldU32(sfFlags)), !bSenderHigh ? lsfLowReserve : lsfHighReserve) // Sender reserve is set.
&& !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0.
&& !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0.
&& !sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) // Sender quality out is 0.
{
// Clear the reserve of the sender, possibly delete the line!
SLE::pointer sleSender = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID));
ownerCountAdjust(uSenderID, -1, sleSender);
sleRippleState->setFieldU32(sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); // Clear reserve flag.
bDelete = !saBalance // Balance is zero.
&& !isSetBit(uFlags, bSenderHigh ? lsfLowReserve : lsfHighReserve); // Receiver reserve is clear.
}
if (bSenderHigh)
saBalance.negate();
sleRippleState->setFieldAmount(sfBalance, saBalance);
// Want to reflect balance to zero even if we are deleting line.
sleRippleState->setFieldAmount(sfBalance, saBalance); // ONLY: Adjust ripple balance.
if (bDelete)
{
terResult = trustDelete(sleRippleState,
bSenderHigh ? uReceiverID : uSenderID,
!bSenderHigh ? uReceiverID : uSenderID);
}
else
{
entryModify(sleRippleState);
terResult = tesSUCCESS;
}
}
return terResult;
}
@@ -1374,6 +1432,7 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv
}
else if (saAmount.isNative())
{
// XRP send which does not check reserve and can do pure adjustment.
SLE::pointer sleSender = !!uSenderID
? entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID))
: SLE::pointer();
@@ -1398,14 +1457,14 @@ TER LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiv
}
else
{
sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount);
sleSender->setFieldAmount(sfBalance, sleSender->getFieldAmount(sfBalance) - saAmount); // Decrement XRP balance.
entryModify(sleSender);
}
}
if (tesSUCCESS == terResult && sleReceiver)
{
sleReceiver->setFieldAmount(sfBalance, sleReceiver->getFieldAmount(sfBalance) + saAmount);
sleReceiver->setFieldAmount(sfBalance, sleReceiver->getFieldAmount(sfBalance) + saAmount); // Increment XRP balance.
entryModify(sleReceiver);
}

View File

@@ -156,6 +156,7 @@ public:
const STAmount& saSrcLimit,
const uint32 uSrcQualityIn = 0,
const uint32 uSrcQualityOut = 0);
TER trustDelete(SLE::ref sleRippleState, const uint160& uLowAccountID, const uint160& uHighAccountID);
Json::Value getJson(int) const;
void calcRawMeta(Serializer&, TER result, uint32 index);

View File

@@ -252,22 +252,7 @@ TER TrustSetTransactor::doApply()
{
// Can delete.
bool bLowNode = sleRippleState->isFieldPresent(sfLowNode); // Detect legacy dirs.
bool bHighNode = sleRippleState->isFieldPresent(sfHighNode);
uint64 uLowNode = sleRippleState->getFieldU64(sfLowNode);
uint64 uHighNode = sleRippleState->getFieldU64(sfHighNode);
cLog(lsTRACE) << "doTrustSet: Deleting ripple line: low";
terResult = mEngine->getNodes().dirDelete(false, uLowNode, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false, !bLowNode);
if (tesSUCCESS == terResult)
{
cLog(lsTRACE) << "doTrustSet: Deleting ripple line: high";
terResult = mEngine->getNodes().dirDelete(false, uHighNode, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false, !bHighNode);
}
cLog(lsINFO) << "doTrustSet: Deleting ripple line: state";
mEngine->entryDelete(sleRippleState);
terResult = mEngine->getNodes().trustDelete(sleRippleState, uLowAccountID, uHighAccountID);
}
else if (bReserveIncrease
&& mPriorBalance.getNValue() < uReserveCreate) // Reserve is not scaled by load.

View File

@@ -1274,4 +1274,132 @@ buster.testCase("Quality paths", {
});
},
});
buster.testCase("Trust auto clear", {
'setUp' : testutils.build_setup(),
// 'setUp' : testutils.build_setup({ verbose: true }),
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
'tearDown' : testutils.build_teardown(),
"trust normal clear" :
function (done) {
var self = this;
async.waterfall([
function (callback) {
self.what = "Create accounts.";
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback);
},
function (callback) {
self.what = "Set credit limits.";
// Mutual trust.
testutils.credit_limits(self.remote,
{
"alice" : "1000/USD/bob",
"bob" : "1000/USD/alice",
},
callback);
},
function (callback) {
self.what = "Verify credit limits.";
testutils.verify_limit(self.remote, "bob", "1000/USD/alice", callback);
},
function (callback) {
self.what = "Clear credit limits.";
// Mutual trust.
testutils.credit_limits(self.remote,
{
"alice" : "0/USD/bob",
"bob" : "0/USD/alice",
},
callback);
},
function (callback) {
self.what = "Verify credit limits.";
testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) {
var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error;
callback(!success);
});
},
// YYY Could verify owner counts are zero.
], function (error) {
buster.refute(error, self.what);
done();
});
},
"trust auto clear" :
function (done) {
var self = this;
async.waterfall([
function (callback) {
self.what = "Create accounts.";
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback);
},
function (callback) {
self.what = "Set credit limits.";
// Mutual trust.
testutils.credit_limits(self.remote,
{
"alice" : "1000/USD/bob",
},
callback);
},
function (callback) {
self.what = "Distribute funds.";
testutils.payments(self.remote,
{
"bob" : [ "50/USD/alice" ],
},
callback);
},
function (callback) {
self.what = "Clear credit limits.";
// Mutual trust.
testutils.credit_limits(self.remote,
{
"alice" : "0/USD/bob",
},
callback);
},
function (callback) {
self.what = "Verify credit limits.";
testutils.verify_limit(self.remote, "alice", "0/USD/bob", callback);
},
function (callback) {
self.what = "Return funds.";
testutils.payments(self.remote,
{
"alice" : [ "50/USD/bob" ],
},
callback);
},
function (callback) {
self.what = "Verify credit limit gone.";
testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) {
var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error;
callback(!success);
});
},
], function (error) {
buster.refute(error, self.what);
done();
});
},
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -226,7 +226,7 @@ var verify_limit = function (remote, src, amount, callback) {
callback();
})
.on('error', function (m) {
.once('error', function (m) {
// console.log("error: %s", JSON.stringify(m));
callback(m);