Cleanup transaction submission

This commit is contained in:
wltsmrz
2013-08-01 06:04:49 +09:00
3 changed files with 117 additions and 79 deletions

View File

@@ -126,10 +126,10 @@ function Remote(opts, trace) {
}; };
// Account objects by AccountId. // Account objects by AccountId.
this._accounts = {}; this._accounts = { };
// OrderBook objects // OrderBook objects
this._books = {}; this._books = { };
// Secrets that we know about. // Secrets that we know about.
this.secrets = { this.secrets = {
@@ -192,11 +192,11 @@ util.inherits(Remote, EventEmitter);
// Flags for ledger entries. In support of account_root(). // Flags for ledger entries. In support of account_root().
Remote.flags = { Remote.flags = {
'account_root' : { account_root : {
'PasswordSpent' : 0x00010000, PasswordSpent: 0x00010000,
'RequireDestTag' : 0x00020000, RequireDestTag: 0x00020000,
'RequireAuth' : 0x00040000, RequireAuth: 0x00040000,
'DisallowXRP' : 0x00080000, DisallowXRP: 0x00080000
} }
}; };
@@ -917,9 +917,7 @@ Remote.prototype.ledger_accept = function (callback) {
request.request(); request.request();
request.callback(callback); request.callback(callback);
} else { } else {
this.emit('error', { this.emit('error', { error : 'notStandAlone' });
'error' : 'notStandAlone'
});
} }
return this; return this;
@@ -1100,39 +1098,40 @@ Remote.prototype.set_secret = function (account, secret) {
// //
// If does not exist: emit('error', 'error' : 'remoteError', 'remote' : { 'error' : 'entryNotFound' }) // If does not exist: emit('error', 'error' : 'remoteError', 'remote' : { 'error' : 'entryNotFound' })
Remote.prototype.request_ripple_balance = function (account, issuer, currency, current, callback) { Remote.prototype.request_ripple_balance = function (account, issuer, currency, current, callback) {
var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger.
return request.ripple_state(account, issuer, currency) request.ripple_state(account, issuer, currency);
.ledger_choose(current) request.ledger_choose(current);
.on('success', function (message) { request.once(success, function(message) {
var node = message.node; var node = message.node;
var lowLimit = Amount.from_json(node.LowLimit);
var highLimit = Amount.from_json(node.HighLimit);
// The amount the low account holds of issuer.
var balance = Amount.from_json(node.Balance);
// accountHigh implies: for account: balance is negated, highLimit is the limit set by account.
var accountHigh = UInt160.from_json(account).equals(highLimit.issuer());
var lowLimit = Amount.from_json(node.LowLimit); request.emit('ripple_state', {
var highLimit = Amount.from_json(node.HighLimit); account_balance : ( accountHigh ? balance.negate() : balance.clone()).parse_issuer(account),
// The amount the low account holds of issuer. peer_balance : (!accountHigh ? balance.negate() : balance.clone()).parse_issuer(issuer),
var balance = Amount.from_json(node.Balance);
// accountHigh implies: for account: balance is negated, highLimit is the limit set by account.
var accountHigh = UInt160.from_json(account).equals(highLimit.issuer());
request.emit('ripple_state', { account_limit : ( accountHigh ? highLimit : lowLimit).clone().parse_issuer(issuer),
'account_balance' : ( accountHigh ? balance.negate() : balance.clone()).parse_issuer(account), peer_limit : (!accountHigh ? highLimit : lowLimit).clone().parse_issuer(account),
'peer_balance' : (!accountHigh ? balance.negate() : balance.clone()).parse_issuer(issuer),
'account_limit' : ( accountHigh ? highLimit : lowLimit).clone().parse_issuer(issuer), account_quality_in : ( accountHigh ? node.HighQualityIn : node.LowQualityIn),
'peer_limit' : (!accountHigh ? highLimit : lowLimit).clone().parse_issuer(account), peer_quality_in : (!accountHigh ? node.HighQualityIn : node.LowQualityIn),
'account_quality_in' : ( accountHigh ? node.HighQualityIn : node.LowQualityIn), account_quality_out : ( accountHigh ? node.HighQualityOut : node.LowQualityOut),
'peer_quality_in' : (!accountHigh ? node.HighQualityIn : node.LowQualityIn), peer_quality_out : (!accountHigh ? node.HighQualityOut : node.LowQualityOut),
});
});
'account_quality_out' : ( accountHigh ? node.HighQualityOut : node.LowQualityOut), request.callback(callback, 'ripple_state');
'peer_quality_out' : (!accountHigh ? node.HighQualityOut : node.LowQualityOut),
}); return request;
})
.callback(callback, 'ripple_state');
}; };
Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies, callback) { Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies, callback) {
var self = this;
var request = new Request(this, 'ripple_path_find'); var request = new Request(this, 'ripple_path_find');
request.message.source_account = UInt160.json_rewrite(src_account); request.message.source_account = UInt160.json_rewrite(src_account);
@@ -1140,14 +1139,16 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account,
request.message.destination_amount = Amount.json_rewrite(dst_amount); request.message.destination_amount = Amount.json_rewrite(dst_amount);
if (src_currencies) { if (src_currencies) {
request.message.source_currencies = src_currencies.map(function (ci) { request.message.source_currencies = src_currencies.map(function (ci) {
var ci_new = {}; var ci_new = { };
if ('issuer' in ci) if (ci.hasOwnProperty('issuer')) {
ci_new.issuer = UInt160.json_rewrite(ci.issuer); ci_new.issuer = UInt160.json_rewrite(ci.issuer);
}
if ('currency' in ci) if (ci.hasOwnProperty('currency')) {
ci_new.currency = Currency.json_rewrite(ci.currency); ci_new.currency = Currency.json_rewrite(ci.currency);
}
return ci_new; return ci_new;
}); });
@@ -1211,15 +1212,27 @@ Remote.prototype.transaction = function () {
return new Transaction(this); return new Transaction(this);
}; };
/**
* Calculate a transaction fee for a number of tx fee units.
*
* This takes into account the last known network and local load fees.
*
* @return {Amount} Final fee in XRP for specified number of fee units.
*/
Remote.prototype.fee_tx = function (units) {
var fee_unit = this.fee_tx_unit();
return Amount.from_json(String(Math.ceil(units * fee_unit)));
};
/** /**
* Get the current recommended transaction fee unit. * Get the current recommended transaction fee unit.
* *
* Multiply this value with the number of fee units in order to calculate the * Multiply this value with the number of fee units in order to calculate the
* recommended fee for the transaction you are trying to submit. * recommended fee for the transaction you are trying to submit.
* *
* @return {Number} Recommended amount for one fee unit. * @return {Number} Recommended amount for one fee unit as float.
*/ */
Remote.prototype.fee_tx = function () { Remote.prototype.fee_tx_unit = function () {
var fee_unit = this._fee_base / this._fee_ref; var fee_unit = this._fee_base / this._fee_ref;
// Apply load fees // Apply load fees
@@ -1236,8 +1249,17 @@ Remote.prototype.fee_tx = function () {
* *
* Returns the base reserve with load fees and safety margin applied. * Returns the base reserve with load fees and safety margin applied.
*/ */
Remote.prototype.fee_reserve_base = function () { Remote.prototype.reserve = function (owner_count) {
// XXX var reserve_base = Amount.from_json(String(this._reserve_base));
var reserve_inc = Amount.from_json(String(this._reserve_inc));
owner_count = owner_count || 0;
if (owner_count < 0) {
throw new Error('Owner count must not be negative.');
}
return reserve_base.add(reserve_inc.product_human(owner_count));
}; };
exports.Remote = Remote; exports.Remote = Remote;

View File

@@ -85,33 +85,31 @@ function Transaction(remote) {
util.inherits(Transaction, EventEmitter); util.inherits(Transaction, EventEmitter);
// XXX This needs to be determined from the network. // XXX This needs to be determined from the network.
Transaction.fees = { Transaction.fee_units = {
default : Amount.from_json('10'), default: 10,
nickname_create : Amount.from_json('1000'),
offer : Amount.from_json('10'),
}; };
Transaction.flags = { Transaction.flags = {
AccountSet : { AccountSet: {
RequireDestTag : 0x00010000, RequireDestTag: 0x00010000,
OptionalDestTag : 0x00020000, OptionalDestTag: 0x00020000,
RequireAuth : 0x00040000, RequireAuth: 0x00040000,
OptionalAuth : 0x00080000, OptionalAuth: 0x00080000,
DisallowXRP : 0x00100000, DisallowXRP: 0x00100000,
AllowXRP : 0x00200000, AllowXRP: 0x00200000
}, },
OfferCreate : { OfferCreate: {
Passive : 0x00010000, Passive: 0x00010000,
ImmediateOrCancel : 0x00020000, ImmediateOrCancel: 0x00020000,
FillOrKill : 0x00040000, FillOrKill: 0x00040000,
Sell : 0x00080000, Sell: 0x00080000
}, },
Payment : { Payment: {
NoRippleDirect : 0x00010000, NoRippleDirect: 0x00010000,
PartialPayment : 0x00020000, PartialPayment: 0x00020000,
LimitQuality : 0x00040000, LimitQuality: 0x00040000
}, },
}; };
@@ -121,12 +119,12 @@ Transaction.HASH_SIGN = 0x53545800;
Transaction.HASH_SIGN_TESTNET = 0x73747800; Transaction.HASH_SIGN_TESTNET = 0x73747800;
Transaction.prototype.consts = { Transaction.prototype.consts = {
'telLOCAL_ERROR' : -399, telLOCAL_ERROR : -399,
'temMALFORMED' : -299, temMALFORMED : -299,
'tefFAILURE' : -199, tefFAILURE : -199,
'terRETRY' : -99, terRETRY : -99,
'tesSUCCESS' : 0, tesSUCCESS : 0,
'tecCLAIMED' : 100, tecCLAIMED : 100,
}; };
Transaction.prototype.isTelLocal = function (ter) { Transaction.prototype.isTelLocal = function (ter) {
@@ -183,11 +181,11 @@ Transaction.prototype.get_fee = function() {
Transaction.prototype.complete = function () { Transaction.prototype.complete = function () {
var tx_json = this.tx_json; var tx_json = this.tx_json;
if (tx_json.Fee === void(0) && this.remote.local_fee) { if (typeof tx_json.Fee === 'undefined' && this.remote.local_fee === void(0)) {
tx_json.Fee = Transaction.fees['default'].to_json(); this.tx_json.Fee = this.remote.fee_tx(this.fee_units()).to_json();
} }
if (tx_json.SigningPubKey === void(0) && (!this.remote || this.remote.local_signing)) { if (typeof tx_json.SigningPubKey === 'undefined' && (!this.remote || this.remote.local_signing)) {
var seed = Seed.from_json(this._secret); var seed = Seed.from_json(this._secret);
var key = seed.get_key(this.tx_json.Account); var key = seed.get_key(this.tx_json.Account);
tx_json.SigningPubKey = key.to_hex_pub(); tx_json.SigningPubKey = key.to_hex_pub();
@@ -212,9 +210,10 @@ Transaction.prototype.sign = function () {
var seed = Seed.from_json(this._secret); var seed = Seed.from_json(this._secret);
var hash = this.signing_hash(); var hash = this.signing_hash();
if (this.tx_json.TxnSignature && hash === this._previous_signing_hash) { var previously_signed = this.tx_json.TxnSignature
return; && hash === this._previous_signing_hash;
}
if (previously_signed) return;
var key = seed.get_key(this.tx_json.Account); var key = seed.get_key(this.tx_json.Account);
var sig = key.sign(hash, 0); var sig = key.sign(hash, 0);
@@ -478,7 +477,7 @@ Transaction.prototype.ripple_line_set = function (src, limit, quality_in, qualit
this.tx_json.Account = UInt160.json_rewrite(src); this.tx_json.Account = UInt160.json_rewrite(src);
// Allow limit of 0 through. // Allow limit of 0 through.
if (limit !== undefined) if (limit !== void(0))
this.tx_json.LimitAmount = Amount.json_rewrite(limit); this.tx_json.LimitAmount = Amount.json_rewrite(limit);
if (quality_in) if (quality_in)
@@ -503,6 +502,21 @@ Transaction.prototype.wallet_add = function (src, amount, authorized_key, public
return this; return this;
}; };
/**
* Returns the number of fee units this transaction will cost.
*
* Each Ripple transaction based on its type and makeup costs a certain number
* of fee units. The fee units are calculated on a per-server basis based on the
* current load on both the network and the server.
*
* @see https://ripple.com/wiki/Transaction_Fee
*
* @return {Number} Number of fee units for this transaction.
*/
Transaction.prototype.fee_units = function () {
return Transaction.fee_units['default'];
};
// Submit a transaction to the network. // Submit a transaction to the network.
// XXX Don't allow a submit without knowing ledger_index. // XXX Don't allow a submit without knowing ledger_index.
// XXX Have a network canSubmit(), post events for following. // XXX Have a network canSubmit(), post events for following.
@@ -521,13 +535,13 @@ Transaction.prototype.wallet_add = function (src, amount, authorized_key, public
Transaction.prototype.submit = function (callback) { Transaction.prototype.submit = function (callback) {
var self = this; var self = this;
this.callback = (typeof callback === 'function') ? callback : function(){}; this.callback = typeof callback === 'function' ? callback : function(){};
this.once('error', function transaction_error(error, message) { this.once('error', function(error, message) {
self.callback(error, message); self.callback(error, message);
}); });
this.once('success', function transaction_success(message) { this.once('success', function(message) {
self.callback(null, message); self.callback(null, message);
}); });

View File

@@ -157,7 +157,9 @@ var create_accounts = function (remote, src, amount, accounts, callback) {
.on('proposed', function (m) { .on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m)); // console.log("proposed: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS'); if (m.result != 'tesSUCCESS') {
callback(new Error("Transaction did not succeed."));
} else callback(null);
}) })
.on('error', function (m) { .on('error', function (m) {
// console.log("error: %s", JSON.stringify(m)); // console.log("error: %s", JSON.stringify(m));