Implement optional offer cancel on offer_create.

This commit is contained in:
Arthur Britto
2013-06-15 22:10:15 -07:00
parent 7347ad1693
commit 475ce2b899
3 changed files with 156 additions and 30 deletions

View File

@@ -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)

View File

@@ -391,6 +391,9 @@ TER OfferCreateTransactor::doApply ()
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 bool bHaveCancel = mTxn.isFieldPresent (sfOfferSequence);
const uint32 uCancelSequence = mTxn.getFieldU32 (sfOfferSequence);
const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence);
const uint32 uSequence = mTxn.getSequence (); const uint32 uSequence = mTxn.getSequence ();
const uint256 uLedgerIndex = Ledger::getOfferIndex (mTxnAccountID, uSequence); const uint256 uLedgerIndex = Ledger::getOfferIndex (mTxnAccountID, uSequence);
@@ -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));

View File

@@ -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;