mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Implement optional offer cancel on offer_create.
This commit is contained in:
@@ -43,6 +43,7 @@ void TFInit ()
|
|||||||
<< SOElement (sfTakerPays, SOE_REQUIRED)
|
<< SOElement (sfTakerPays, SOE_REQUIRED)
|
||||||
<< SOElement (sfTakerGets, SOE_REQUIRED)
|
<< SOElement (sfTakerGets, SOE_REQUIRED)
|
||||||
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
<< SOElement (sfExpiration, SOE_OPTIONAL)
|
||||||
|
<< SOElement (sfOfferSequence, SOE_OPTIONAL)
|
||||||
;
|
;
|
||||||
|
|
||||||
DECLARE_TF (OfferCancel, ttOFFER_CANCEL)
|
DECLARE_TF (OfferCancel, ttOFFER_CANCEL)
|
||||||
|
|||||||
@@ -372,45 +372,48 @@ TER OfferCreateTransactor::takeOffers (
|
|||||||
TER OfferCreateTransactor::doApply ()
|
TER OfferCreateTransactor::doApply ()
|
||||||
{
|
{
|
||||||
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate> " << mTxn.getJson (0);
|
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate> " << mTxn.getJson (0);
|
||||||
const uint32 uTxFlags = mTxn.getFlags ();
|
const uint32 uTxFlags = mTxn.getFlags ();
|
||||||
const bool bPassive = isSetBit (uTxFlags, tfPassive);
|
const bool bPassive = isSetBit (uTxFlags, tfPassive);
|
||||||
const bool bImmediateOrCancel = isSetBit (uTxFlags, tfImmediateOrCancel);
|
const bool bImmediateOrCancel = isSetBit (uTxFlags, tfImmediateOrCancel);
|
||||||
const bool bFillOrKill = isSetBit (uTxFlags, tfFillOrKill);
|
const bool bFillOrKill = isSetBit (uTxFlags, tfFillOrKill);
|
||||||
const bool bSell = isSetBit (uTxFlags, tfSell);
|
const bool bSell = isSetBit (uTxFlags, tfSell);
|
||||||
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays);
|
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays);
|
||||||
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets);
|
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets);
|
||||||
|
|
||||||
if (!saTakerPays.isLegalNet () || !saTakerGets.isLegalNet ())
|
if (!saTakerPays.isLegalNet () || !saTakerGets.isLegalNet ())
|
||||||
return temBAD_AMOUNT;
|
return temBAD_AMOUNT;
|
||||||
|
|
||||||
WriteLog (lsTRACE, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: saTakerPays=%s saTakerGets=%s")
|
WriteLog (lsTRACE, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: saTakerPays=%s saTakerGets=%s")
|
||||||
% saTakerPays.getFullText ()
|
% saTakerPays.getFullText ()
|
||||||
% saTakerGets.getFullText ());
|
% saTakerGets.getFullText ());
|
||||||
|
|
||||||
const uint160 uPaysIssuerID = saTakerPays.getIssuer ();
|
const uint160 uPaysIssuerID = saTakerPays.getIssuer ();
|
||||||
const uint160 uGetsIssuerID = saTakerGets.getIssuer ();
|
const uint160 uGetsIssuerID = saTakerGets.getIssuer ();
|
||||||
const uint32 uExpiration = mTxn.getFieldU32 (sfExpiration);
|
const uint32 uExpiration = mTxn.getFieldU32 (sfExpiration);
|
||||||
const bool bHaveExpiration = mTxn.isFieldPresent (sfExpiration);
|
const bool bHaveExpiration = mTxn.isFieldPresent (sfExpiration);
|
||||||
const uint32 uSequence = mTxn.getSequence ();
|
const bool bHaveCancel = mTxn.isFieldPresent (sfOfferSequence);
|
||||||
|
const uint32 uCancelSequence = mTxn.getFieldU32 (sfOfferSequence);
|
||||||
|
const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence);
|
||||||
|
const uint32 uSequence = mTxn.getSequence ();
|
||||||
|
|
||||||
const uint256 uLedgerIndex = Ledger::getOfferIndex (mTxnAccountID, uSequence);
|
const uint256 uLedgerIndex = Ledger::getOfferIndex (mTxnAccountID, uSequence);
|
||||||
|
|
||||||
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate: Creating offer node: " << uLedgerIndex.ToString () << " uSequence=" << uSequence;
|
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate: Creating offer node: " << uLedgerIndex.ToString () << " uSequence=" << uSequence;
|
||||||
|
|
||||||
const uint160 uPaysCurrency = saTakerPays.getCurrency ();
|
const uint160 uPaysCurrency = saTakerPays.getCurrency ();
|
||||||
const uint160 uGetsCurrency = saTakerGets.getCurrency ();
|
const uint160 uGetsCurrency = saTakerGets.getCurrency ();
|
||||||
const uint64 uRate = STAmount::getRate (saTakerGets, saTakerPays);
|
const uint64 uRate = STAmount::getRate (saTakerGets, saTakerPays);
|
||||||
|
|
||||||
TER terResult = tesSUCCESS;
|
TER terResult = tesSUCCESS;
|
||||||
uint256 uDirectory; // Delete hints.
|
uint256 uDirectory; // Delete hints.
|
||||||
uint64 uOwnerNode;
|
uint64 uOwnerNode;
|
||||||
uint64 uBookNode;
|
uint64 uBookNode;
|
||||||
|
|
||||||
LedgerEntrySet& lesActive = mEngine->getNodes ();
|
LedgerEntrySet& lesActive = mEngine->getNodes ();
|
||||||
LedgerEntrySet lesCheckpoint = lesActive; // Checkpoint with just fees paid.
|
LedgerEntrySet lesCheckpoint = lesActive; // Checkpoint with just fees paid.
|
||||||
lesActive.bumpSeq (); // Begin ledger variance.
|
lesActive.bumpSeq (); // Begin ledger variance.
|
||||||
|
|
||||||
SLE::pointer sleCreator = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mTxnAccountID));
|
SLE::pointer sleCreator = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mTxnAccountID));
|
||||||
|
|
||||||
if (uTxFlags & tfOfferCreateMask)
|
if (uTxFlags & tfOfferCreateMask)
|
||||||
{
|
{
|
||||||
@@ -472,7 +475,35 @@ TER OfferCreateTransactor::doApply ()
|
|||||||
|
|
||||||
terResult = tecUNFUNDED_OFFER;
|
terResult = tecUNFUNDED_OFFER;
|
||||||
}
|
}
|
||||||
|
else if (bHaveCancel && (!uCancelSequence || uAccountSequenceNext - 1 <= uCancelSequence))
|
||||||
|
{
|
||||||
|
WriteLog (lsINFO, OfferCreateTransactor) << "OfferCreate: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uCancelSequence;
|
||||||
|
|
||||||
|
terResult = temBAD_SEQUENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel offer.
|
||||||
|
if (tesSUCCESS == terResult && bHaveCancel)
|
||||||
|
{
|
||||||
|
const uint256 uCancelIndex = Ledger::getOfferIndex (mTxnAccountID, uCancelSequence);
|
||||||
|
SLE::pointer sleCancel = mEngine->entryCache (ltOFFER, uCancelIndex);
|
||||||
|
|
||||||
|
if (sleCancel)
|
||||||
|
{
|
||||||
|
WriteLog (lsWARNING, OfferCancelTransactor) << "OfferCreate: uCancelSequence=" << uCancelSequence;
|
||||||
|
|
||||||
|
terResult = mEngine->getNodes ().offerDelete (sleCancel, uCancelIndex, mTxnAccountID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteLog (lsWARNING, OfferCancelTransactor) << "OfferCreate: offer not found: "
|
||||||
|
<< RippleAddress::createHumanAccountID (mTxnAccountID)
|
||||||
|
<< " : " << uCancelSequence
|
||||||
|
<< " : " << uCancelIndex.ToString ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure authorized to hold what taker will pay.
|
||||||
if (tesSUCCESS == terResult && !saTakerPays.isNative ())
|
if (tesSUCCESS == terResult && !saTakerPays.isNative ())
|
||||||
{
|
{
|
||||||
SLE::pointer sleTakerPays = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uPaysIssuerID));
|
SLE::pointer sleTakerPays = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uPaysIssuerID));
|
||||||
|
|||||||
@@ -73,6 +73,100 @@ buster.testCase("Offer tests", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"offer create then offer create with cancel in one ledger" :
|
||||||
|
function (done) {
|
||||||
|
var self = this;
|
||||||
|
var final_create;
|
||||||
|
var sequence_first;
|
||||||
|
var sequence_second;
|
||||||
|
var dones = 0;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (callback) {
|
||||||
|
self.remote.transaction()
|
||||||
|
.offer_create("root", "500", "100/USD/root")
|
||||||
|
.on('proposed', function (m) {
|
||||||
|
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS', m);
|
||||||
|
})
|
||||||
|
.on('final', function (m) {
|
||||||
|
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
|
||||||
|
|
||||||
|
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||||
|
buster.assert(final_create);
|
||||||
|
|
||||||
|
if (3 === ++dones)
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (m, callback) {
|
||||||
|
sequence_first = m.tx_json.Sequence;
|
||||||
|
|
||||||
|
// Test canceling existant offer.
|
||||||
|
self.remote.transaction()
|
||||||
|
.offer_create("root", "300", "100/USD/root", undefined, sequence_first)
|
||||||
|
.on('proposed', function (m) {
|
||||||
|
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS', m);
|
||||||
|
})
|
||||||
|
.on('final', function (m) {
|
||||||
|
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
|
||||||
|
|
||||||
|
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||||
|
buster.assert(final_create);
|
||||||
|
|
||||||
|
if (3 === ++dones)
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (m, callback) {
|
||||||
|
sequence_second = m.tx_json.Sequence;
|
||||||
|
self.what = "Verify offer canceled.";
|
||||||
|
|
||||||
|
testutils.verify_offer_not_found(self.remote, "root", sequence_first, callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Verify offer replaced.";
|
||||||
|
|
||||||
|
testutils.verify_offer(self.remote, "root", sequence_second, "300", "100/USD/root", callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
// Test canceling non-existant offer.
|
||||||
|
self.remote.transaction()
|
||||||
|
.offer_create("root", "400", "200/USD/root", undefined, sequence_first)
|
||||||
|
.on('proposed', function (m) {
|
||||||
|
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS', m);
|
||||||
|
})
|
||||||
|
.on('final', function (m) {
|
||||||
|
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
|
||||||
|
|
||||||
|
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||||
|
buster.assert(final_create);
|
||||||
|
|
||||||
|
if (3 === ++dones)
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.remote
|
||||||
|
.once('ledger_closed', function (message) {
|
||||||
|
// console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash);
|
||||||
|
final_create = message;
|
||||||
|
})
|
||||||
|
.ledger_accept();
|
||||||
|
}
|
||||||
|
], function (error) {
|
||||||
|
// console.log("result: error=%s", error);
|
||||||
|
buster.refute(error);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
"Offer create then self crossing offer, no trust lines with self" :
|
"Offer create then self crossing offer, no trust lines with self" :
|
||||||
function (done) {
|
function (done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
Reference in New Issue
Block a user