Merge branch 'master' of github.com:jedmccaleb/NewCoin into api

This commit is contained in:
jed
2012-11-04 13:03:07 -08:00
13 changed files with 409 additions and 190 deletions

View File

@@ -1,16 +1,18 @@
var async = require("async");
var fs = require("fs"); var fs = require("fs");
var path = require("path"); var path = require("path");
var utils = require("./utils.js"); var utils = require("./utils.js");
// Empty a directory. // Empty a directory.
// done(err) : err = true if an error occured.
var emptyPath = function(dirPath, done) { var emptyPath = function(dirPath, done) {
fs.readdir(dirPath, function(err, files) { fs.readdir(dirPath, function(err, files) {
if (err) { if (err) {
done(err); done(err);
} }
else { else {
utils.mapOr(rmPath, files.map(function(f) { return path.join(dirPath, f); }), done); async.some(files.map(function(f) { return path.join(dirPath, f); }), rmPath, done);
} }
}); });
}; };
@@ -53,6 +55,7 @@ var resetPath = function(dirPath, mode, done) {
}; };
// Remove path recursively. // Remove path recursively.
// done(err)
var rmPath = function(dirPath, done) { var rmPath = function(dirPath, done) {
// console.log("rmPath: %s", dirPath); // console.log("rmPath: %s", dirPath);

View File

@@ -36,11 +36,12 @@ var config = require('../test/config.js');
var Request = function (remote, command) { var Request = function (remote, command) {
var self = this; var self = this;
this.message = { this.message = {
'command' : command, 'command' : command,
'id' : undefined, 'id' : undefined,
}; };
this.remote = remote; this.remote = remote;
this.requested = false;
this.on('request', function () { this.on('request', function () {
self.request_default(); self.request_default();
@@ -68,7 +69,10 @@ Request.prototype.request = function (remote) {
}; };
Request.prototype.request_default = function () { Request.prototype.request_default = function () {
this.remote.request(this); if (!this.requested) {
this.requested = true;
this.remote.request(this);
}
}; };
Request.prototype.ledger_choose = function (current) { Request.prototype.ledger_choose = function (current) {
@@ -572,6 +576,7 @@ Remote.prototype.submit = function (transaction) {
else { else {
if (!transaction.transaction.Sequence) { if (!transaction.transaction.Sequence) {
transaction.transaction.Sequence = this.account_seq(transaction.transaction.Account, 'ADVANCE'); transaction.transaction.Sequence = this.account_seq(transaction.transaction.Account, 'ADVANCE');
// console.log("Sequence: %s", transaction.transaction.Sequence);
} }
if (!transaction.transaction.Sequence) { if (!transaction.transaction.Sequence) {
@@ -581,7 +586,7 @@ Remote.prototype.submit = function (transaction) {
// Try again. // Try again.
self.submit(transaction); self.submit(transaction);
}) })
.on('error', function (message) { .on('error_account_seq_cache', function (message) {
// XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number. // XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number.
// Look in the current ledger. // Look in the current ledger.
@@ -590,7 +595,7 @@ Remote.prototype.submit = function (transaction) {
// Try again. // Try again.
self.submit(transaction); self.submit(transaction);
}) })
.on('error', function (message) { .on('error_account_seq_cache', function (message) {
// Forward errors. // Forward errors.
transaction.emit('error', message); transaction.emit('error', message);
}) })
@@ -685,6 +690,11 @@ Remote.prototype.account_seq = function (account, advance) {
seq = account_info.seq; seq = account_info.seq;
if (advance) account_info.seq += 1; if (advance) account_info.seq += 1;
// console.log("cached: %s current=%d next=%d", account, seq, account_info.seq);
}
else {
// console.log("uncached: %s", account);
} }
return seq; return seq;
@@ -701,21 +711,40 @@ Remote.prototype.set_account_seq = function (account, seq) {
// Return a request to refresh accounts[account].seq. // Return a request to refresh accounts[account].seq.
Remote.prototype.account_seq_cache = function (account, current) { Remote.prototype.account_seq_cache = function (account, current) {
var self = this; var self = this;
var request = this.request_ledger_entry('account_root'); var request;
if (!self.accounts[account]) self.accounts[account] = {};
var account_info = self.accounts[account];
request = account_info.caching_seq_request;
if (!request) {
// console.log("starting: %s", account);
request = self.request_ledger_entry('account_root')
.account_root(account)
.ledger_choose(current)
.on('success', function (message) {
delete account_info.caching_seq_request;
var seq = message.node.Sequence;
account_info.seq = seq;
// console.log("caching: %s %d", account, seq);
// If the caller also waits for 'success', they might run before this.
request.emit('success_account_seq_cache', message);
})
.on('error', function (message) {
// console.log("error: %s", account);
delete account_info.caching_seq_request;
request.emit('error_account_seq_cache', message);
});
account_info.caching_seq_request = request;
}
return request return request
.account_root(account)
.ledger_choose(current)
.on('success', function (message) {
var seq = message.node.Sequence;
if (!self.accounts[account]) self.accounts[account] = {};
self.accounts[account].seq = seq;
// If the caller also waits for 'success', they might run before this.
request.emit('success_account_seq_cache');
});
}; };
// Mark an account's root node as dirty. // Mark an account's root node as dirty.
@@ -1042,6 +1071,16 @@ Transaction.prototype.send_max = function (send_max) {
return this; return this;
} }
// --> rate: In billionths.
Transaction.prototype.transfer_rate = function (rate) {
this.transaction.TransferRate = Number(rate);
if (this.transaction.TransferRate < 1e9)
throw 'invalidTransferRate';
return this;
}
// Add flags to a transaction. // Add flags to a transaction.
// --> flags: undefined, _flag_, or [ _flags_ ] // --> flags: undefined, _flag_, or [ _flags_ ]
Transaction.prototype.set_flags = function (flags) { Transaction.prototype.set_flags = function (flags) {
@@ -1081,14 +1120,15 @@ Transaction.prototype._account_secret = function (account) {
return this.remote.config.accounts[account] ? this.remote.config.accounts[account].secret : undefined; return this.remote.config.accounts[account] ? this.remote.config.accounts[account].secret : undefined;
}; };
// .wallet_locator() // Options:
// .message_key() // .domain() NYI
// .domain() // .message_key() NYI
// .transfer_rate() // .transfer_rate()
// .publish() // .wallet_locator() NYI
Transaction.prototype.account_set = function (src) { Transaction.prototype.account_set = function (src) {
this.secret = this._account_secret(src); this.secret = this._account_secret(src);
this.transaction.TransactionType = 'AccountSet'; this.transaction.TransactionType = 'AccountSet';
this.transaction.Account = UInt160.json_rewrite(src);
return this; return this;
}; };

View File

@@ -19,24 +19,6 @@ var throwErr = function(done) {
}; };
}; };
// apply function to elements of array. Return first true value to done or undefined.
var mapOr = function(func, array, done) {
if (array.length) {
func(array[array.length-1], function(v) {
if (v) {
done(v);
}
else {
array.length -= 1;
mapOr(func, array, done);
}
});
}
else {
done();
}
};
var trace = function(comment, func) { var trace = function(comment, func) {
return function() { return function() {
console.log("%s: %s", trace, arguments.toString); console.log("%s: %s", trace, arguments.toString);
@@ -88,7 +70,6 @@ var stringToArray = function (s) {
return a; return a;
}; };
exports.mapOr = mapOr;
exports.trace = trace; exports.trace = trace;
exports.arraySet = arraySet; exports.arraySet = arraySet;
exports.hexToString = hexToString; exports.hexToString = hexToString;

View File

@@ -28,6 +28,9 @@ SField sfIndex(STI_HASH256, 258, "index");
static int initFields() static int initFields()
{ {
sfTxnSignature.notSigningField(); sfTxnSignatures.notSigningField();
sfSignature.notSigningField();
sfHighQualityIn.setMeta(SFM_CHANGE); sfHighQualityOut.setMeta(SFM_CHANGE); sfHighQualityIn.setMeta(SFM_CHANGE); sfHighQualityOut.setMeta(SFM_CHANGE);
sfLowQualityIn.setMeta(SFM_CHANGE); sfLowQualityOut.setMeta(SFM_CHANGE); sfLowQualityIn.setMeta(SFM_CHANGE); sfLowQualityOut.setMeta(SFM_CHANGE);

View File

@@ -60,16 +60,18 @@ public:
const int fieldValue; // Code number for protocol const int fieldValue; // Code number for protocol
std::string fieldName; std::string fieldName;
SF_Meta fieldMeta; SF_Meta fieldMeta;
bool signingField;
SField(int fc, SerializedTypeID tid, int fv, const char* fn) : SField(int fc, SerializedTypeID tid, int fv, const char* fn) :
fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER) fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER), signingField(true)
{ {
boost::mutex::scoped_lock sl(mapMutex); boost::mutex::scoped_lock sl(mapMutex);
codeToField[fieldCode] = this; codeToField[fieldCode] = this;
} }
SField(SerializedTypeID tid, int fv, const char *fn) : SField(SerializedTypeID tid, int fv, const char *fn) :
fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER) fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn),
fieldMeta(SFM_NEVER), signingField(true)
{ {
boost::mutex::scoped_lock sl(mapMutex); boost::mutex::scoped_lock sl(mapMutex);
codeToField[fieldCode] = this; codeToField[fieldCode] = this;
@@ -97,6 +99,11 @@ public:
bool shouldMetaDel() const { return (fieldMeta == SFM_DELETE) || (fieldMeta == SFM_ALWAYS); } bool shouldMetaDel() const { return (fieldMeta == SFM_DELETE) || (fieldMeta == SFM_ALWAYS); }
bool shouldMetaMod() const { return (fieldMeta == SFM_CHANGE) || (fieldMeta == SFM_ALWAYS); } bool shouldMetaMod() const { return (fieldMeta == SFM_CHANGE) || (fieldMeta == SFM_ALWAYS); }
void setMeta(SF_Meta m) { fieldMeta = m; } void setMeta(SF_Meta m) { fieldMeta = m; }
bool isSigningField() const { return signingField; }
void notSigningField() { signingField = false; }
bool shouldInclude(bool withSigningField) const
{ return (fieldValue < 256) && (withSigningField || signingField); }
bool operator==(const SField& f) const { return fieldCode == f.fieldCode; } bool operator==(const SField& f) const { return fieldCode == f.fieldCode; }
bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; } bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; }

View File

@@ -799,7 +799,11 @@ void RippleCalc::calcNodeRipple(
} }
// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur... // Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur...
// No account adjustments in reverse as we don't know how much is going to actually be pushed through yet. // Based on required deliverable, propagate redeem, issue, and deliver requests to the previous node.
// Inflate amount requested by required fees.
// Reedems are limited based on IOUs previous has on hand.
// Issues are limited based on credit limits and amount owed.
// No account balance adjustments as we don't know how much is going to actually be pushed through yet.
// <-- tesSUCCESS or tepPATH_DRY // <-- tesSUCCESS or tepPATH_DRY
TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality) TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality)
{ {
@@ -1135,7 +1139,9 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref psp
return terResult; return terResult;
} }
// When moving forward, we know the actual amount to push through so adjust balances. // The reverse pass has been narrowing by credit available and inflating by fees as it worked backwards.
// Now, push through the actual amount to each node and adjust balances.
//
// Perform balance adjustments between previous and current node. // Perform balance adjustments between previous and current node.
// - The previous node: specifies what to push through to current. // - The previous node: specifies what to push through to current.
// - All of previous output is consumed. // - All of previous output is consumed.
@@ -1204,46 +1210,39 @@ TER RippleCalc::calcNodeAccountFwd(
if (bPrvAccount && bNxtAccount) if (bPrvAccount && bNxtAccount)
{ {
// Next is an account, must be rippling.
if (!uIndex) if (!uIndex)
{ {
// ^ --> ACCOUNT --> account // ^ --> ACCOUNT --> account
// First node, calculate amount to send. // First node, calculate amount to ripple based on what is available.
// XXX Limit by stamp/ripple balance
const STAmount& saCurSendMaxReq = pspCur->saInReq.isNegative() // Limit by sendmax.
? pspCur->saInReq // Negative for no limit, doing a calculation. const STAmount saCurSendMaxReq = pspCur->saInReq.isNegative()
? pspCur->saInReq // Negative for no limit, doing a calculation.
: pspCur->saInReq-pspCur->saInAct; // request - done. : pspCur->saInReq-pspCur->saInAct; // request - done.
STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends. STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends.
if (saCurRedeemReq) saCurRedeemAct = saCurRedeemReq
{ // Redeem requested.
// Redeem requested. ? saCurRedeemReq.isNegative()
saCurRedeemAct = saCurRedeemReq.isNegative() ? saCurRedeemReq
? saCurRedeemReq : std::min(saCurRedeemReq, saCurSendMaxReq)
: std::min(saCurRedeemReq, saCurSendMaxReq); // No redeeming.
} : saCurRedeemReq;
else
{
// No redeeming.
saCurRedeemAct = saCurRedeemReq;
}
saCurSendMaxPass = saCurRedeemAct; saCurSendMaxPass = saCurRedeemAct;
if (saCurIssueReq && (saCurSendMaxReq.isNegative() || saCurSendMaxPass != saCurSendMaxReq)) saCurIssueAct = (saCurIssueReq // Issue wanted.
{ && (saCurSendMaxReq.isNegative() // No limit.
// Issue requested and pass does not meet max. || saCurSendMaxPass != saCurSendMaxReq)) // Not yet satisfied.
saCurIssueAct = saCurSendMaxReq.isNegative() // Issue requested and pass does not meet max.
? saCurIssueReq ? saCurSendMaxReq.isNegative()
: std::min(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); ? saCurIssueReq
} : std::min(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq)
else // No issuing.
{ : STAmount(saCurIssueReq);
// No issuing.
saCurIssueAct = STAmount(saCurIssueReq);
}
saCurSendMaxPass += saCurIssueAct; saCurSendMaxPass += saCurIssueAct;
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurSendMaxReq=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurSendMaxReq=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s")
@@ -1287,7 +1286,7 @@ TER RippleCalc::calcNodeAccountFwd(
saCurIssueAct.zero(saCurIssueReq); saCurIssueAct.zero(saCurIssueReq);
// Previous redeem part 1: redeem -> redeem // Previous redeem part 1: redeem -> redeem
if (saPrvRedeemReq != saPrvRedeemAct) // Previous wants to redeem. To next must be ok. if (saPrvRedeemReq && saCurRedeemReq) // Previous wants to redeem.
{ {
// Rate : 1.0 : quality out // Rate : 1.0 : quality out
calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax); calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax);
@@ -1302,25 +1301,23 @@ TER RippleCalc::calcNodeAccountFwd(
} }
// Previous redeem part 2: redeem -> issue. // Previous redeem part 2: redeem -> issue.
// wants to redeem and current would and can issue.
// If redeeming cur to next is done, this implies can issue.
if (saPrvRedeemReq != saPrvRedeemAct // Previous still wants to redeem. if (saPrvRedeemReq != saPrvRedeemAct // Previous still wants to redeem.
&& saCurRedeemReq == saCurRedeemAct // Current has no more to redeem to next. && saCurRedeemReq == saCurRedeemAct // Current redeeming is done can issue.
&& saCurIssueReq) && saCurIssueReq) // Current wants to issue.
{ {
// Rate : 1.0 : transfer_rate // Rate : 1.0 : transfer_rate
calcNodeRipple(QUALITY_ONE, lesActive.rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax); calcNodeRipple(QUALITY_ONE, lesActive.rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax);
} }
// Previous issue part 2 : issue -> issue // Previous issue part 2 : issue -> issue
if (saPrvIssueReq != saPrvIssueAct) // Previous wants to issue. To next must be ok. if (saPrvIssueReq != saPrvIssueAct // Previous wants to issue.
&& saCurRedeemReq == saCurRedeemAct) // Current redeeming is done can issue.
{ {
// Rate: quality in : 1.0 // Rate: quality in : 1.0
calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax); calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax);
} }
// Adjust prv --> cur balance : take all inbound // Adjust prv --> cur balance : take all inbound
// XXX Currency must be in amount.
lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false); lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false);
} }
} }
@@ -1429,7 +1426,7 @@ bool PathState::lessPriority(PathState::ref lhs, PathState::ref rhs)
return lhs->mIndex > rhs->mIndex; // Bigger is worse. return lhs->mIndex > rhs->mIndex; // Bigger is worse.
} }
// Make sure the path delivers to uAccountID: uCurrencyID from uIssuerID. // Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID.
// //
// If the unadded next node as specified by arguments would not work as is, then add the necessary nodes so it would work. // If the unadded next node as specified by arguments would not work as is, then add the necessary nodes so it would work.
// //
@@ -1651,7 +1648,40 @@ PathState::PathState(
| STPathElement::typeIssuer, | STPathElement::typeIssuer,
uSenderID, uSenderID,
uInCurrencyID, uInCurrencyID,
uInIssuerID); uSenderID);
if (tesSUCCESS == terStatus
&& !!uInCurrencyID // First was not XRC
&& uInIssuerID != uSenderID) { // Issuer was not same as sender
// May have an implied node.
// Figure out next node properties for implied node.
const uint160 uNxtCurrencyID = spSourcePath.getElementCount()
? spSourcePath.getElement(0).getCurrency()
: uOutCurrencyID;
const uint160 uNxtAccountID = spSourcePath.getElementCount()
? spSourcePath.getElement(0).getAccountID()
: !!uOutCurrencyID
? uOutIssuerID == uReceiverID
? uReceiverID
: uOutIssuerID
: ACCOUNT_XNS;
// Can't just use push implied, because it can't compensate for next account.
if (!uNxtCurrencyID // Next is XRC - will have offer next
|| uInCurrencyID != uNxtCurrencyID // Next is different current - will have offer next
|| uInIssuerID != uNxtAccountID) // Next is not implied issuer
{
// Add implied account.
terStatus = pushNode(
STPathElement::typeAccount
| STPathElement::typeCurrency
| STPathElement::typeIssuer,
uInIssuerID,
uInCurrencyID,
uInIssuerID);
}
}
BOOST_FOREACH(const STPathElement& speElement, spSourcePath) BOOST_FOREACH(const STPathElement& speElement, spSourcePath)
{ {
@@ -1659,21 +1689,35 @@ PathState::PathState(
terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID()); terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID());
} }
const PaymentNode& pnPrv = vpnNodes.back();
if (tesSUCCESS == terStatus
&& !!uOutCurrencyID // Next is not XRC
&& uOutIssuerID != uReceiverID // Out issuer is not reciever
&& (pnPrv.uCurrencyID != uOutCurrencyID // Previous will be an offer.
|| pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer.
{
// Add implied account.
terStatus = pushNode(
STPathElement::typeAccount
| STPathElement::typeCurrency
| STPathElement::typeIssuer,
uOutIssuerID,
uInCurrencyID,
uOutIssuerID);
}
if (tesSUCCESS == terStatus) if (tesSUCCESS == terStatus)
{ {
// Create receiver node. // Create receiver node.
terStatus = pushImply(uReceiverID, uOutCurrencyID, uOutIssuerID); terStatus = pushNode(
if (tesSUCCESS == terStatus) STPathElement::typeAccount // Last node is always an account.
{ | STPathElement::typeCurrency
terStatus = pushNode( | STPathElement::typeIssuer,
STPathElement::typeAccount // Last node is always an account. uReceiverID, // Receive to output
| STPathElement::typeCurrency uOutCurrencyID, // Desired currency
| STPathElement::typeIssuer, !!uOutCurrencyID ? uReceiverID : ACCOUNT_XNS);
uReceiverID, // Receive to output
uOutCurrencyID, // Desired currency
uOutIssuerID);
}
} }
if (tesSUCCESS == terStatus) if (tesSUCCESS == terStatus)
@@ -2065,8 +2109,8 @@ int iPass = 0;
assert(!!pspCur->saInPass && !!pspCur->saOutPass); assert(!!pspCur->saInPass && !!pspCur->saOutPass);
if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality.
|| !pspBest // Best is not yet set. && (!pspBest // Best is not yet set.
|| PathState::lessPriority(pspBest, pspCur)) // Current is better than set. || PathState::lessPriority(pspBest, pspCur))) // Current is better than set.
{ {
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s") cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s")
% STAmount::saFromRate(pspCur->uQuality) % STAmount::saFromRate(pspCur->uQuality)

View File

@@ -65,7 +65,7 @@ void SHAMap::getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint2
if (childHash != d->getNodeHash()) if (childHash != d->getNodeHash())
{ {
cLog(lsERROR) << "Wrong hash from cached object"; cLog(lsERROR) << "Wrong hash from cached object";
d = SHAMapTreeNode::pointer(); d.reset();
} }
else else
{ {

View File

@@ -125,22 +125,22 @@
// inner object // inner object
// OBJECT/1 is reserved for end of object // OBJECT/1 is reserved for end of object
FIELD(TemplateEntry, OBJECT, 1)
FIELD(TransactionMetaData, OBJECT, 2) FIELD(TransactionMetaData, OBJECT, 2)
FIELD(CreatedNode, OBJECT, 3) FIELD(CreatedNode, OBJECT, 3)
FIELD(DeletedNode, OBJECT, 4) FIELD(DeletedNode, OBJECT, 4)
FIELD(ModifiedNode, OBJECT, 5) FIELD(ModifiedNode, OBJECT, 5)
FIELD(PreviousFields, OBJECT, 6) FIELD(PreviousFields, OBJECT, 6)
FIELD(FinalFields, OBJECT, 7) FIELD(FinalFields, OBJECT, 7)
FIELD(TemplateEntry, OBJECT, 8)
// array of objects // array of objects
// ARRAY/1 is reserved for end of array // ARRAY/1 is reserved for end of array
FIELD(AffectedNodes, ARRAY, 1)
FIELD(SigningAccounts, ARRAY, 2) FIELD(SigningAccounts, ARRAY, 2)
FIELD(TxnSignatures, ARRAY, 3) FIELD(TxnSignatures, ARRAY, 3)
FIELD(Signatures, ARRAY, 4) FIELD(Signatures, ARRAY, 4)
FIELD(Template, ARRAY, 5) FIELD(Template, ARRAY, 5)
FIELD(Necessary, ARRAY, 6) FIELD(Necessary, ARRAY, 6)
FIELD(Sufficient, ARRAY, 7) FIELD(Sufficient, ARRAY, 7)
FIELD(AffectedNodes, ARRAY, 8)
// vim:ts=4 // vim:ts=4

View File

@@ -283,13 +283,8 @@ void STObject::add(Serializer& s, bool withSigningFields) const
BOOST_FOREACH(const SerializedType& it, mData) BOOST_FOREACH(const SerializedType& it, mData)
{ // pick out the fields and sort them { // pick out the fields and sort them
if ((it.getSType() != STI_NOTPRESENT) && it.getFName().isBinary()) if ((it.getSType() != STI_NOTPRESENT) && it.getFName().shouldInclude(withSigningFields))
{
SField::ref fName = it.getFName();
if (withSigningFields ||
((fName != sfTxnSignature) && (fName != sfTxnSignatures) && (fName != sfSignature)))
fields.insert(std::make_pair(it.getFName().fieldCode, &it)); fields.insert(std::make_pair(it.getFName().fieldCode, &it));
}
} }

View File

@@ -603,7 +603,7 @@ public:
int getElementCount() const { return mPath.size(); } int getElementCount() const { return mPath.size(); }
bool isEmpty() const { return mPath.empty(); } bool isEmpty() const { return mPath.empty(); }
const STPathElement& getElement(int offset) const { return mPath[offset]; } const STPathElement& getElement(int offset) const { return mPath[offset]; }
const STPathElement& getElemet(int offset) { return mPath[offset]; } const STPathElement& getElement(int offset) { return mPath[offset]; }
void addElement(const STPathElement &e) { mPath.push_back(e); } void addElement(const STPathElement &e) { mPath.push_back(e); }
void clear() { mPath.clear(); } void clear() { mPath.clear(); }
bool hasSeen(const uint160 &acct); bool hasSeen(const uint160 &acct);

View File

@@ -462,7 +462,11 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn, const Transac
const bool bMax = txn.isFieldPresent(sfSendMax); const bool bMax = txn.isFieldPresent(sfSendMax);
const uint160 uDstAccountID = txn.getFieldAccount160(sfDestination); const uint160 uDstAccountID = txn.getFieldAccount160(sfDestination);
const STAmount saDstAmount = txn.getFieldAmount(sfAmount); const STAmount saDstAmount = txn.getFieldAmount(sfAmount);
const STAmount saMaxAmount = bMax ? txn.getFieldAmount(sfSendMax) : saDstAmount; const STAmount saMaxAmount = bMax
? txn.getFieldAmount(sfSendMax)
: saDstAmount.isNative()
? saDstAmount
: STAmount(saDstAmount.getCurrency(), mTxnAccountID, saDstAmount.getMantissa(), saDstAmount.getExponent(), saDstAmount.isNegative());
const uint160 uSrcCurrency = saMaxAmount.getCurrency(); const uint160 uSrcCurrency = saMaxAmount.getCurrency();
const uint160 uDstCurrency = saDstAmount.getCurrency(); const uint160 uDstCurrency = saDstAmount.getCurrency();

View File

@@ -464,24 +464,23 @@ buster.testCase("Indirect ripple", {
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback); testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback);
}, },
function (callback) { function (callback) {
self.what = "Set alice's limit."; self.what = "Set credit limits.";
testutils.credit_limit(self.remote, "alice", "600/USD/mtgox", callback); testutils.credit_limits(self.remote,
{
"alice" : "600/USD/mtgox",
"bob" : "700/USD/mtgox",
},
callback);
}, },
function (callback) { function (callback) {
self.what = "Set bob's limit."; self.what = "Distribute funds.";
testutils.credit_limit(self.remote, "bob", "700/USD/mtgox", callback); testutils.payments(self.remote,
}, {
function (callback) { "mtgox" : [ "70/USD/alice", "50/USD/bob" ],
self.what = "Give alice some mtgox."; },
callback);
testutils.payment(self.remote, "mtgox", "alice", "70/USD/mtgox", callback);
},
function (callback) {
self.what = "Give bob some mtgox.";
testutils.payment(self.remote, "mtgox", "bob", "50/USD/mtgox", callback);
}, },
function (callback) { function (callback) {
self.what = "Verify alice balance with mtgox."; self.what = "Verify alice balance with mtgox.";
@@ -534,24 +533,23 @@ buster.testCase("Indirect ripple", {
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback); testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback);
}, },
function (callback) { function (callback) {
self.what = "Set alice's limit."; self.what = "Set credit limits.";
testutils.credit_limit(self.remote, "alice", "600/USD/mtgox", callback); testutils.credit_limits(self.remote,
{
"alice" : "600/USD/mtgox",
"bob" : "700/USD/mtgox",
},
callback);
}, },
function (callback) { function (callback) {
self.what = "Set bob's limit."; self.what = "Distribute funds.";
testutils.credit_limit(self.remote, "bob", "700/USD/mtgox", callback); testutils.payments(self.remote,
}, {
function (callback) { "mtgox" : [ "70/USD/alice", "50/USD/bob" ],
self.what = "Give alice some mtgox."; },
callback);
testutils.payment(self.remote, "mtgox", "alice", "70/USD/mtgox", callback);
},
function (callback) {
self.what = "Give bob some mtgox.";
testutils.payment(self.remote, "mtgox", "bob", "50/USD/mtgox", callback);
}, },
function (callback) { function (callback) {
self.what = "Alice sends via a path"; self.what = "Alice sends via a path";
@@ -593,39 +591,24 @@ buster.testCase("Indirect ripple", {
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "amazon", "mtgox"], callback); testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "amazon", "mtgox"], callback);
}, },
function (callback) { function (callback) {
self.what = "Set alice's limit with bob."; self.what = "Set credit limits.";
testutils.credit_limit(self.remote, "bob", "600/USD/alice", callback); testutils.credit_limits(self.remote,
{
"amazon" : "2000/USD/mtgox",
"bob" : [ "600/USD/alice", "1000/USD/mtgox" ],
"carol" : [ "700/USD/alice", "1000/USD/mtgox" ],
},
callback);
}, },
function (callback) { function (callback) {
self.what = "Set alice's limit with carol."; self.what = "Distribute funds.";
testutils.credit_limit(self.remote, "carol", "700/USD/alice", callback); testutils.payments(self.remote,
}, {
function (callback) { "mtgox" : [ "100/USD/bob", "100/USD/carol" ],
self.what = "Set bob's mtgox limit."; },
callback);
testutils.credit_limit(self.remote, "bob", "1000/USD/mtgox", callback);
},
function (callback) {
self.what = "Set carol's mtgox limit.";
testutils.credit_limit(self.remote, "carol", "1000/USD/mtgox", callback);
},
function (callback) {
self.what = "Set amazon's mtgox limit.";
testutils.credit_limit(self.remote, "amazon", "2000/USD/mtgox", callback);
},
function (callback) {
self.what = "Give bob some mtgox.";
testutils.payment(self.remote, "mtgox", "bob", "100/USD/mtgox", callback);
},
function (callback) {
self.what = "Give carol some mtgox.";
testutils.payment(self.remote, "mtgox", "carol", "100/USD/mtgox", callback);
}, },
function (callback) { function (callback) {
self.what = "Alice pays amazon via multiple paths"; self.what = "Alice pays amazon via multiple paths";
@@ -642,29 +625,84 @@ buster.testCase("Indirect ripple", {
.submit(); .submit();
}, },
function (callback) { function (callback) {
self.what = "Verify amazon balance with mtgox."; self.what = "Verify balances.";
testutils.verify_balance(self.remote, "amazon", "150/USD/mtgox", callback); testutils.verify_balances(self.remote,
{
"alice" : [ "-100/USD/bob", "-50/USD/carol" ],
"amazon" : "150/USD/mtgox",
"bob" : "0/USD/mtgox",
"carol" : "50/USD/mtgox",
},
callback);
},
], function (error) {
buster.refute(error, self.what);
done();
});
},
"indirect ripple with path and transfer fee" :
function (done) {
var self = this;
async.waterfall([
function (callback) {
self.what = "Create accounts.";
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "amazon", "mtgox"], callback);
}, },
function (callback) { function (callback) {
self.what = "Verify alice balance with bob."; self.what = "Set mtgox transfer rate.";
testutils.verify_balance(self.remote, "alice", "-50/USD/bob", callback); testutils.transfer_rate(self.remote, "mtgox", 1.1e9, callback);
}, },
function (callback) { function (callback) {
self.what = "Verify alice balance with carol."; self.what = "Set credit limits.";
testutils.verify_balance(self.remote, "alice", "-100/USD/carol", callback); testutils.credit_limits(self.remote,
{
"amazon" : "2000/USD/mtgox",
"bob" : [ "600/USD/alice", "1000/USD/mtgox" ],
"carol" : [ "700/USD/alice", "1000/USD/mtgox" ],
},
callback);
}, },
function (callback) { function (callback) {
self.what = "Verify bob balance with mtgox."; self.what = "Distribute funds.";
testutils.verify_balance(self.remote, "bob", "50/USD/mtgox", callback); testutils.payments(self.remote,
{
"mtgox" : [ "100/USD/bob", "100/USD/carol" ],
},
callback);
}, },
function (callback) { function (callback) {
self.what = "Verify carol balance with mtgox."; self.what = "Alice pays amazon via multiple paths";
testutils.verify_balance(self.remote, "carol", "0/USD/mtgox", callback); self.remote.transaction()
.payment("alice", "amazon", "150/USD/mtgox")
.send_max("200/USD/alice")
.path_add( [ { account: "bob" } ])
.path_add( [ { account: "carol" } ])
.on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS');
})
.submit();
},
function (callback) {
self.what = "Verify balances.";
testutils.verify_balances(self.remote,
{
"alice" : [ "-100/USD/bob", "-65.00000000000001/USD/carol" ],
"amazon" : "150/USD/mtgox",
"bob" : "0/USD/mtgox",
"carol" : "35/USD/mtgox",
},
callback);
}, },
], function (error) { ], function (error) {
buster.refute(error, self.what); buster.refute(error, self.what);
@@ -672,9 +710,6 @@ buster.testCase("Indirect ripple", {
}); });
}, },
// Direct ripple without no liqudity. // Direct ripple without no liqudity.
// Ripple without credit path. // Test with XRC at start and end.
// Ripple with one-way credit path.
// Transfer Fees
// Use multiple paths.
}); });
// vim:sw=2:sts=2:ts=8 // vim:sw=2:sts=2:ts=8

View File

@@ -129,6 +129,30 @@ var credit_limit = function (remote, src, amount, callback) {
.submit(); .submit();
}; };
var credit_limits = function (remote, balances, callback) {
assert(3 === arguments.length);
var limits = [];
for (var src in balances) {
var values_src = balances[src];
var values = 'string' === typeof values_src ? [ values_src ] : values_src;
for (var index in values) {
limits.push( { "source" : src, "amount" : values[index] } );
}
}
async.every(limits,
function (limit, callback) {
credit_limit(remote, limit.source, limit.amount,
function (mismatch) { callback(!mismatch); });
},
function (every) {
callback(!every);
});
};
var payment = function (remote, src, dst, amount, callback) { var payment = function (remote, src, dst, amount, callback) {
assert(5 === arguments.length); assert(5 === arguments.length);
@@ -147,27 +171,110 @@ var payment = function (remote, src, dst, amount, callback) {
.submit(); .submit();
}; };
var payments = function (remote, balances, callback) {
assert(3 === arguments.length);
var sends = [];
for (var src in balances) {
var values_src = balances[src];
var values = 'string' === typeof values_src ? [ values_src ] : values_src;
for (var index in values) {
var amount_json = values[index];
var amount = Amount.from_json(amount_json);
sends.push( { "source" : src, "destination" : amount.issuer.to_json(), "amount" : amount_json } );
}
}
async.every(sends,
function (send, callback) {
payment(remote, send.source, send.destination, send.amount,
function (mismatch) { callback(!mismatch); });
},
function (every) {
callback(!every);
});
};
var transfer_rate = function (remote, src, billionths, callback) {
assert(4 === arguments.length);
remote.transaction()
.account_set(src)
.transfer_rate(billionths)
.on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS');
})
.on('error', function (m) {
// console.log("error: %s", JSON.stringify(m));
callback(m);
})
.submit();
};
var verify_balance = function (remote, src, amount_json, callback) { var verify_balance = function (remote, src, amount_json, callback) {
assert(4 === arguments.length); assert(4 === arguments.length);
var amount = Amount.from_json(amount_json); var amount = Amount.from_json(amount_json);
remote.request_ripple_balance(src, amount.issuer.to_json(), amount.currency.to_json(), 'CURRENT') if (amount.is_native()) {
.once('ripple_state', function (m) { // XXX Not implemented.
// console.log("BALANCE: %s", JSON.stringify(m)); callback(false);
// console.log("account_balance: %s", m.account_balance.to_text_full()); }
// console.log("account_limit: %s", m.account_limit.to_text_full()); else {
// console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); remote.request_ripple_balance(src, amount.issuer.to_json(), amount.currency.to_json(), 'CURRENT')
// console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); .once('ripple_state', function (m) {
// console.log("BALANCE: %s", JSON.stringify(m));
// console.log("account_balance: %s", m.account_balance.to_text_full());
// console.log("account_limit: %s", m.account_limit.to_text_full());
// console.log("issuer_balance: %s", m.issuer_balance.to_text_full());
// console.log("issuer_limit: %s", m.issuer_limit.to_text_full());
callback(!m.account_balance.equals(amount)); if (!m.account_balance.equals(amount)) {
}) console.log("verify_balance: failed: %s vs %s is %s", src, amount_json, amount.to_text_full());
.request(); }
callback(!m.account_balance.equals(amount));
})
.request();
}
}; };
var verify_balances = function (remote, balances, callback) {
var tests = [];
for (var src in balances) {
var values_src = balances[src];
var values = 'string' === typeof values_src ? [ values_src ] : values_src;
for (var index in values) {
tests.push( { "source" : src, "amount" : values[index] } );
}
}
async.every(tests,
function (check, callback) {
verify_balance(remote, check.source, check.amount,
function (mismatch) { callback(!mismatch); });
},
function (every) {
callback(!every);
});
};
exports.build_setup = build_setup; exports.build_setup = build_setup;
exports.create_accounts = create_accounts; exports.create_accounts = create_accounts;
exports.credit_limit = credit_limit; exports.credit_limit = credit_limit;
exports.credit_limits = credit_limits;
exports.payment = payment; exports.payment = payment;
exports.payments = payments;
exports.build_teardown = build_teardown; exports.build_teardown = build_teardown;
exports.transfer_rate = transfer_rate;
exports.verify_balance = verify_balance; exports.verify_balance = verify_balance;
exports.verify_balances = verify_balances;
// vim:sw=2:sts=2:ts=8 // vim:sw=2:sts=2:ts=8