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

This commit is contained in:
JoelKatz
2012-10-31 17:37:36 -07:00
5 changed files with 138 additions and 20 deletions

View File

@@ -232,6 +232,11 @@ var Currency = function () {
this.value = NaN;
}
// Given "USD" return the json.
Currency.json_rewrite = function(j) {
return Currency.from_json(j).to_json();
};
Currency.from_json = function (j) {
return (new Currency()).parse_json(j);
};

View File

@@ -980,6 +980,43 @@ Transaction.prototype.submit = function () {
// Set options for Transactions
//
Transaction._path_rewrite = function (path) {
var path_new = [];
for (var index in path) {
var node = path[index];
var node_new = {};
if ('account' in node)
node_new.account = UInt160.json_rewrite(node.account);
if ('issuer' in node)
node_new.issuer = UInt160.json_rewrite(node.issuer);
if ('currency' in node)
node_new.currency = Currency.json_rewrite(node.currency);
path_new.push(node_new);
}
return path_new;
}
Transaction.prototype.path_add = function (path) {
this.transaction.Paths = this.transaction.Paths || []
this.transaction.Paths.push(Transaction._path_rewrite(path));
return this;
}
// --> paths: undefined or array of path
// A path is an array of objects containing some combination of: account, currency, issuer
Transaction.prototype.paths = function (paths) {
for (var index in paths) {
this.path_add(paths[index]);
}
return this;
}
// If the secret is in the config object, it does not need to be provided.
Transaction.prototype.secret = function (secret) {
this.secret = secret;
@@ -987,7 +1024,7 @@ Transaction.prototype.secret = function (secret) {
Transaction.prototype.send_max = function (send_max) {
if (send_max)
this.transaction.SendMax = send_max.to_json();
this.transaction.SendMax = Amount.json_rewrite(send_max);
return this;
}
@@ -1105,6 +1142,13 @@ Transaction.prototype.password_set = function (src, authorized_key, generator, p
// --> src : UInt160 or String
// --> dst : UInt160 or String
// --> deliver_amount : Amount or String.
//
// Options:
// .paths()
// .path_add()
// .secret()
// .send_max()
// .set_flags()
Transaction.prototype.payment = function (src, dst, deliver_amount) {
this.secret = this._account_secret(src);
this.transaction.TransactionType = 'Payment';

View File

@@ -1421,6 +1421,8 @@ bool PathState::lessPriority(PathState::ref lhs, PathState::ref rhs)
// Make sure the path 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.
//
// Rules:
// - Currencies must be converted via an offer.
// - A node names it's output.
@@ -1449,13 +1451,14 @@ TER PathState::pushImply(
ACCOUNT_ONE, // Placeholder for offers.
uCurrencyID, // The offer's output is what is now wanted.
uIssuerID);
}
const PaymentNode& pnBck = vpnNodes.back();
// For ripple, non-stamps, ensure the issuer is on at least one side of the transaction.
if (tesSUCCESS == terResult
&& !!uCurrencyID // Not stamps.
&& (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs.
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
{
// Need to ripple through uIssuerID's account.
@@ -1514,13 +1517,19 @@ TER PathState::pushNode(
pnCur.saRevRedeem = STAmount(uCurrencyID, uAccountID);
pnCur.saRevIssue = STAmount(uCurrencyID, uAccountID);
if (!bFirst)
if (bFirst)
{
// The first node is always correct as is.
nothing();
}
else
{
// Add required intermediate nodes to deliver to current account.
terResult = pushImply(
pnCur.uAccountID, // Current account.
pnCur.uCurrencyID, // Wanted currency.
!!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer.
!!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as wanted issuer.
// Note: pnPrv may no longer be the immediately previous node.
}
@@ -1532,7 +1541,7 @@ TER PathState::pushNode(
if (bBckAccount)
{
SLE::pointer sleRippleState = mLedger->getSLE(Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));
SLE::pointer sleRippleState = lesEntries.entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));
if (!sleRippleState)
{
@@ -1541,7 +1550,7 @@ TER PathState::pushNode(
<< " and "
<< RippleAddress::createHumanAccountID(pnCur.uAccountID)
<< " for "
<< STAmount::createHumanCurrency(pnPrv.uCurrencyID)
<< STAmount::createHumanCurrency(pnCur.uCurrencyID)
<< "." ;
cLog(lsINFO) << getJson();
@@ -1555,12 +1564,12 @@ TER PathState::pushNode(
<< " and "
<< RippleAddress::createHumanAccountID(pnCur.uAccountID)
<< " for "
<< STAmount::createHumanCurrency(pnPrv.uCurrencyID)
<< STAmount::createHumanCurrency(pnCur.uCurrencyID)
<< "." ;
STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, uCurrencyID);
STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
if (!saOwed.isPositive() && *saOwed.negate() >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, uCurrencyID))
if (!saOwed.isPositive() && *saOwed.negate() >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID))
{
terResult = tepPATH_DRY;
}

View File

@@ -1112,23 +1112,24 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
data.push_back(new STPathSet(field));
STPathSet* tail = dynamic_cast<STPathSet*>(&data.back());
assert(tail);
for (Json::UInt i = 0; !object.isValidIndex(i); ++i)
for (Json::UInt i = 0; value.isValidIndex(i); ++i)
{
STPath p;
if (!object[i].isArray())
if (!value[i].isArray())
throw std::runtime_error("Path must be array");
for (Json::UInt j = 0; !object[i].isValidIndex(j); ++j)
for (Json::UInt j = 0; value[i].isValidIndex(j); ++j)
{ // each element in this path has some combination of account, currency, or issuer
Json::Value pathEl = object[i][j];
Json::Value pathEl = value[i][j];
if (!pathEl.isObject())
throw std::runtime_error("Path elements must be objects");
const Json::Value& account = pathEl["account"];
const Json::Value& currency = pathEl["currency"];
const Json::Value& issuer = pathEl["issuer"];
const Json::Value& account = pathEl["account"];
const Json::Value& currency = pathEl["currency"];
const Json::Value& issuer = pathEl["issuer"];
bool hasCurrency = false;
uint160 uAccount, uCurrency, uIssuer;
bool hasCurrency;
if (!account.isNull())
{ // human account id
if (!account.isString())
@@ -1138,7 +1139,7 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
uAccount.SetHex(strValue);
{
RippleAddress a;
if (!a.setAccountPublic(strValue))
if (!a.setAccountID(strValue))
throw std::runtime_error("Account in path element invalid");
uAccount = a.getAccountID();
}
@@ -1162,7 +1163,7 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
else
{
RippleAddress a;
if (!a.setAccountPublic(issuer.asString()))
if (!a.setAccountID(issuer.asString()))
throw std::runtime_error("path element issuer invalid");
uIssuer = a.getAccountID();
}

View File

@@ -523,6 +523,65 @@ buster.testCase("Indirect ripple", {
});
},
"indirect ripple with path" :
function (done) {
var self = this;
async.waterfall([
function (callback) {
self.what = "Create accounts.";
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback);
},
function (callback) {
self.what = "Set alice's limit.";
testutils.credit_limit(self.remote, "alice", "600/USD/mtgox", callback);
},
function (callback) {
self.what = "Set bob's limit.";
testutils.credit_limit(self.remote, "bob", "700/USD/mtgox", callback);
},
function (callback) {
self.what = "Give alice some mtgox.";
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) {
self.what = "Alice sends via a path";
self.remote.transaction()
.payment("alice", "bob", "5/USD/mtgox")
.path_add( [ { account: "mtgox" } ])
.on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS');
})
.submit();
},
function (callback) {
self.what = "Verify alice balance with mtgox.";
testutils.verify_balance(self.remote, "alice", "65/USD/mtgox", callback);
},
function (callback) {
self.what = "Verify bob balance with mtgox.";
testutils.verify_balance(self.remote, "bob", "55/USD/mtgox", callback);
},
], function (error) {
buster.refute(error, self.what);
done();
});
},
// Direct ripple without no liqudity.
// Ripple without credit path.
// Ripple with one-way credit path.