mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
275
js/remote.js
275
js/remote.js
@@ -5,11 +5,13 @@
|
||||
//
|
||||
// YYY Will later provide a network access which use multiple instances of this.
|
||||
// YYY A better model might be to allow requesting a target state: keep connected or not.
|
||||
// XXX Make subscribe target state.
|
||||
// XXX Auto subscribe on connect.
|
||||
//
|
||||
|
||||
// Node
|
||||
var util = require('util');
|
||||
var events = require('events');
|
||||
var util = require('util');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
// npm
|
||||
var WebSocket = require('ws');
|
||||
@@ -17,9 +19,9 @@ var WebSocket = require('ws');
|
||||
var amount = require('./amount.js');
|
||||
var Amount = amount.Amount;
|
||||
|
||||
// Events emmitted:
|
||||
// 'success'
|
||||
// 'error'
|
||||
// Request events emmitted:
|
||||
// 'success' : Request successful.
|
||||
// 'error' : Request failed.
|
||||
// 'remoteError'
|
||||
// 'remoteUnexpected'
|
||||
// 'remoteDisconnected'
|
||||
@@ -33,11 +35,11 @@ var Request = function (remote, command) {
|
||||
this.on('request', this.request_default);
|
||||
};
|
||||
|
||||
Request.prototype = new events.EventEmitter;
|
||||
Request.prototype = new EventEmitter;
|
||||
|
||||
// Return this. node EventEmitter's on doesn't return this.
|
||||
Request.prototype.on = function (e, c) {
|
||||
events.EventEmitter.prototype.on.call(this, e, c);
|
||||
EventEmitter.prototype.on.call(this, e, c);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -120,7 +122,7 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) {
|
||||
};
|
||||
};
|
||||
|
||||
Remote.prototype = new events.EventEmitter;
|
||||
Remote.prototype = new EventEmitter;
|
||||
|
||||
var remoteConfig = function (config, server, trace) {
|
||||
var serverConfig = config.servers[server];
|
||||
@@ -128,6 +130,14 @@ var remoteConfig = function (config, server, trace) {
|
||||
return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, config, trace);
|
||||
};
|
||||
|
||||
var isTemMalformed = function (engine_result_code) {
|
||||
return (engine_result_code >= -299 && engine_result_code < 199);
|
||||
};
|
||||
|
||||
var isTefFailure = function (engine_result_code) {
|
||||
return (engine_result_code >= -299 && engine_result_code < 199);
|
||||
};
|
||||
|
||||
var flags = {
|
||||
'OfferCreate' : {
|
||||
'Passive' : 0x00010000,
|
||||
@@ -217,27 +227,28 @@ Remote.prototype.connect_helper = function () {
|
||||
else {
|
||||
switch (message.type) {
|
||||
case 'response':
|
||||
{
|
||||
request = ws.response[message.id];
|
||||
{
|
||||
request = ws.response[message.id];
|
||||
|
||||
if (!request) {
|
||||
unexpected = true;
|
||||
}
|
||||
else if ('success' === message.result) {
|
||||
if (self.trace) console.log("message: %s", json);
|
||||
if (!request) {
|
||||
unexpected = true;
|
||||
}
|
||||
else if ('success' === message.result) {
|
||||
if (self.trace) console.log("message: %s", json);
|
||||
|
||||
request.emit('success', message);
|
||||
}
|
||||
else if (message.error) {
|
||||
if (self.trace) console.log("message: %s", json);
|
||||
request.emit('success', message);
|
||||
}
|
||||
else if (message.error) {
|
||||
if (self.trace) console.log("message: %s", json);
|
||||
|
||||
request.emit('error', {
|
||||
'error' : 'remoteError',
|
||||
'error_message' : 'Remote reported an error.',
|
||||
'remote' : message,
|
||||
});
|
||||
request.emit('error', {
|
||||
'error' : 'remoteError',
|
||||
'error_message' : 'Remote reported an error.',
|
||||
'remote' : message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ledgerClosed':
|
||||
// XXX If not trusted, need to verify we consider ledger closed.
|
||||
@@ -247,8 +258,8 @@ Remote.prototype.connect_helper = function () {
|
||||
|
||||
self.ledger_closed = message.ledger_closed;
|
||||
self.ledger_current_index = message.ledger_closed_index + 1;
|
||||
|
||||
self.emit('ledger_closed');
|
||||
|
||||
self.emit('ledger_closed', self.ledger_closed, self.ledger_closed_index);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -319,7 +330,7 @@ Remote.prototype.disconnect = function (done) {
|
||||
var self = this;
|
||||
var ws = this.ws;
|
||||
|
||||
if (self.trace) console.log("remote: disconnect");
|
||||
if (this.trace) console.log("remote: disconnect");
|
||||
|
||||
ws.onclose = function () {
|
||||
if (self.trace) console.log("remote: onclose: %s", ws.readyState);
|
||||
@@ -333,8 +344,6 @@ Remote.prototype.disconnect = function (done) {
|
||||
// Send a request.
|
||||
// <-> request: what to send, consumed.
|
||||
Remote.prototype.request = function (request) {
|
||||
var self = this;
|
||||
|
||||
this.ws.response[request.message.id = this.id] = request;
|
||||
|
||||
this.id += 1; // Advance id.
|
||||
@@ -356,7 +365,6 @@ Remote.prototype.request_ledger_current = function () {
|
||||
return new Request(this, 'ledger_current');
|
||||
};
|
||||
|
||||
// <-> request:
|
||||
// --> ledger : optional
|
||||
// --> ledger_index : optional
|
||||
Remote.prototype.request_ledger_entry = function (type) {
|
||||
@@ -415,6 +423,19 @@ Remote.prototype.request_ledger_entry = function (type) {
|
||||
return request;
|
||||
};
|
||||
|
||||
// --> ledger_closed : optional
|
||||
Remote.prototype.request_transaction_entry = function (hash, ledger_closed) {
|
||||
assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol.
|
||||
|
||||
var request = new Request(this, 'transaction_entry');
|
||||
|
||||
request.message.transaction = hash;
|
||||
if (ledger_closed)
|
||||
request.message.ledger_closed = ledger_closed;
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
// Submit a transaction.
|
||||
Remote.prototype.submit = function (transaction) {
|
||||
var self = this;
|
||||
@@ -479,15 +500,17 @@ Remote.prototype.server_subscribe = function () {
|
||||
|
||||
var request = new Request(this, 'server_subscribe');
|
||||
|
||||
request.on('success', function (message) {
|
||||
self.ledger_current_index = message.ledger_current_index;
|
||||
self.ledger_closed = message.ledger_closed;
|
||||
self.stand_alone = message.stand_alone;
|
||||
request.
|
||||
on('success', function (message) {
|
||||
self.ledger_closed = message.ledger_closed;
|
||||
self.ledger_current_index = message.ledger_current_index;
|
||||
self.stand_alone = !!message.stand_alone;
|
||||
|
||||
self.emit('subscribed');
|
||||
self.emit('subscribed');
|
||||
|
||||
self.emit('ledger_closed');
|
||||
});
|
||||
self.emit('ledger_closed', self.ledger_closed, self.ledger_current_index-1);
|
||||
})
|
||||
.request();
|
||||
|
||||
// XXX Could give error events, maybe even time out.
|
||||
|
||||
@@ -497,14 +520,14 @@ Remote.prototype.server_subscribe = function () {
|
||||
// Ask the remote to accept the current ledger.
|
||||
// - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_closed' events.
|
||||
Remote.prototype.ledger_accept = function () {
|
||||
if (this.stand_alone)
|
||||
if (this.stand_alone || undefined === this.stand_alone)
|
||||
{
|
||||
var request = new Request(this, 'ledger_accept');
|
||||
|
||||
request.request();
|
||||
}
|
||||
else {
|
||||
self.emit('error', {
|
||||
this.emit('error', {
|
||||
'error' : 'notStandAlone'
|
||||
});
|
||||
}
|
||||
@@ -565,29 +588,139 @@ Remote.prototype.transaction = function () {
|
||||
//
|
||||
// Transactions
|
||||
//
|
||||
// Transaction events:
|
||||
// 'success' : Transaction submitted without error.
|
||||
// 'error' : Error submitting transaction.
|
||||
// 'proposed: Advisory proposed status transaction.
|
||||
// - A client should expect 0 to multiple results.
|
||||
// - Might not get back. The remote might just forward the transaction.
|
||||
// - A success could be reverted in final.
|
||||
// - local error: other remotes might like it.
|
||||
// - malformed error: local server thought it was malformed.
|
||||
// - The client should only trust this when talking to a trusted server.
|
||||
// 'final' : Final status of transaction.
|
||||
// - Only expect a final from honest clients after a tesSUCCESS or ter*.
|
||||
// 'state' : Follow the state of a transaction.
|
||||
// 'clientSubmitted' - Sent to remote
|
||||
// |- 'remoteError' - Remote rejected transaction.
|
||||
// \- 'clientProposed' - Remote provisionally accepted transaction.
|
||||
// |- 'clientMissing' - Transaction has not appeared in ledger as expected.
|
||||
// | |- 'clientLost' - No longer monitoring missing transaction.
|
||||
// |/
|
||||
// |- 'tesSUCCESS' - Transaction in ledger as expected.
|
||||
// |- 'ter...' - Transaction failed.
|
||||
// |- 'tep...' - Transaction partially succeeded.
|
||||
//
|
||||
// Notes:
|
||||
// - All transactions including locally errors and malformed errors may be
|
||||
// forwarded.
|
||||
// - A malicous server can:
|
||||
// - give any proposed result.
|
||||
// - it may declare something correct as incorrect or something correct as incorrect.
|
||||
// - it may not communicate with the rest of the network.
|
||||
// - may or may not forward.
|
||||
//
|
||||
|
||||
var SUBMIT_MISSING = 4; // Report missing.
|
||||
var SUBMIT_LOST = 8; // Give up tracking.
|
||||
|
||||
// A class to implement transactions.
|
||||
// - Collects parameters
|
||||
// - Allow event listeners to be attached to determine the outcome.
|
||||
var Transaction = function (remote) {
|
||||
this.prototype = events.EventEmitter; // XXX Node specific.
|
||||
var self = this;
|
||||
|
||||
this.prototype = EventEmitter; // XXX Node specific.
|
||||
|
||||
this.remote = remote;
|
||||
this.secret = undefined;
|
||||
this.transaction = {}; // Transaction data.
|
||||
this.transaction = { // Transaction data.
|
||||
'Flags' : 0, // XXX Would be nice if server did not require this.
|
||||
};
|
||||
this.hash = undefined;
|
||||
this.submit_index = undefined; // ledger_current_index was this when transaction was submited.
|
||||
this.state = undefined; // Under construction.
|
||||
|
||||
this.on('success', function (message) {
|
||||
if (message.engine_result) {
|
||||
self.hash = message.transaction.hash;
|
||||
|
||||
self.set_state('clientProposed');
|
||||
|
||||
self.emit('proposed', {
|
||||
'result' : message.engine_result,
|
||||
'result_code' : message.engine_result_code,
|
||||
'result_message' : message.engine_result_message,
|
||||
'rejected' : self.isRejected(message.engine_result_code), // If server is honest, don't expect a final if rejected.
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.on('error', function (message) {
|
||||
// Might want to give more detailed information.
|
||||
self.set_state('remoteError');
|
||||
});
|
||||
};
|
||||
|
||||
Transaction.prototype = new events.EventEmitter;
|
||||
Transaction.prototype = new EventEmitter;
|
||||
|
||||
// Return this. node EventEmitter's on doesn't return this.
|
||||
Transaction.prototype.on = function (e, c) {
|
||||
events.EventEmitter.prototype.on.call(this, e, c);
|
||||
EventEmitter.prototype.on.call(this, e, c);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.consts = {
|
||||
'telLOCAL_ERROR' : -399,
|
||||
'temMALFORMED' : -299,
|
||||
'tefFAILURE' : -199,
|
||||
'terRETRY' : -99,
|
||||
'tesSUCCESS' : 0,
|
||||
'tepPARTIAL' : 100,
|
||||
};
|
||||
|
||||
Transaction.prototype.isTelLocal = function (ter) {
|
||||
return ter >= this.consts.telLOCAL_ERROR && ter < this.consts.temMALFORMED;
|
||||
};
|
||||
|
||||
Transaction.prototype.isTemMalformed = function (ter) {
|
||||
return ter >= this.consts.temMALFORMED && ter < this.consts.tefFAILURE;
|
||||
};
|
||||
|
||||
Transaction.prototype.isTefFailure = function (ter) {
|
||||
return ter >= this.consts.tefFAILURE && ter < this.consts.terRETRY;
|
||||
};
|
||||
|
||||
Transaction.prototype.isTerRetry = function (ter) {
|
||||
return ter >= this.consts.terRETRY && ter < this.consts.tesSUCCESS;
|
||||
};
|
||||
|
||||
Transaction.prototype.isTepSuccess = function (ter) {
|
||||
return ter >= this.consts.tesSUCCESS;
|
||||
};
|
||||
|
||||
Transaction.prototype.isTepPartial = function (ter) {
|
||||
return ter >= this.consts.tepPATH_PARTIAL;
|
||||
};
|
||||
|
||||
Transaction.prototype.isRejected = function (ter) {
|
||||
return this.isTelLocal(ter) || this.isTemMalformed(ter) || this.isTefFailure(ter);
|
||||
};
|
||||
|
||||
Transaction.prototype.set_state = function (state) {
|
||||
if (this.state !== state) {
|
||||
this.state = state;
|
||||
this.emit('state', state);
|
||||
}
|
||||
};
|
||||
|
||||
// Submit a transaction to the network.
|
||||
// XXX Don't allow a submit without knowing ledger_closed_index.
|
||||
// XXX Have a network canSubmit(), post events for following.
|
||||
// XXX Also give broader status for tracking through network disconnects.
|
||||
Transaction.prototype.submit = function () {
|
||||
var self = this;
|
||||
var transaction = this.transaction;
|
||||
|
||||
if (undefined === transaction.Fee) {
|
||||
@@ -601,6 +734,50 @@ Transaction.prototype.submit = function () {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.listeners('final').length) {
|
||||
// There are listeners for 'final' arrange to emit it.
|
||||
|
||||
this.submit_index = this.remote.ledger_current_index;
|
||||
|
||||
var on_ledger_closed = function (ledger_closed, ledger_closed_index) {
|
||||
var stop = false;
|
||||
|
||||
// XXX make sure self.hash is available.
|
||||
self.remote.request_transaction_entry(self.hash, ledger_closed)
|
||||
.on('success', function (message) {
|
||||
// XXX Fake results for now.
|
||||
if (!message.metadata.result)
|
||||
message.metadata.result = 'tesSUCCESS';
|
||||
|
||||
self.set_state(message.metadata.result); // XXX Untested.
|
||||
self.emit('final', message);
|
||||
})
|
||||
.on('error', function (message) {
|
||||
if ('remoteError' === message.error
|
||||
&& 'transactionNotFound' === message.remote.error) {
|
||||
if (self.submit_index + SUBMIT_LOST < ledger_closed_index) {
|
||||
self.set_state('clientLost'); // Gave up.
|
||||
stop = true;
|
||||
}
|
||||
else if (self.submit_index + SUBMIT_MISSING < ledger_closed_index) {
|
||||
self.set_state('clientMissing'); // We don't know what happened to transaction, still might find.
|
||||
}
|
||||
}
|
||||
// XXX Could log other unexpectedness.
|
||||
})
|
||||
.request();
|
||||
|
||||
if (stop) {
|
||||
self.removeListener('ledger_closed', on_ledger_closed);
|
||||
self.emit('final', message);
|
||||
}
|
||||
};
|
||||
|
||||
this.remote.on('ledger_closed', on_ledger_closed);
|
||||
}
|
||||
|
||||
this.set_state('clientSubmitted');
|
||||
|
||||
this.remote.submit(this);
|
||||
|
||||
return this;
|
||||
@@ -628,7 +805,7 @@ Transaction.prototype.flags = function (flags) {
|
||||
if (flags) {
|
||||
var transaction_flags = exports.flags[this.transaction.TransactionType];
|
||||
|
||||
if (undefined == this.transaction.Flags)
|
||||
if (undefined == this.transaction.Flags) // We plan to not define this field on new Transaction.
|
||||
this.transaction.Flags = 0;
|
||||
|
||||
var flag_set = 'object' === typeof flags ? flags : [ flags ];
|
||||
@@ -655,12 +832,18 @@ Transaction.prototype.flags = function (flags) {
|
||||
//
|
||||
// Transactions
|
||||
//
|
||||
// remote.transaction() // Build a transaction object.
|
||||
// Construction:
|
||||
// remote.transaction() // Build a transaction object.
|
||||
// .offer_create(...) // Set major parameters.
|
||||
// .flags() // Set optional parameters.
|
||||
// .on() // Register for events.
|
||||
// .submit(); // Send to network.
|
||||
//
|
||||
// Events:
|
||||
// 'success' // Transaction was successfully submitted: hash, proposed TER
|
||||
// 'error' // Error submitting transaction.
|
||||
// 'closed' // Result from closed ledger: TER
|
||||
//
|
||||
|
||||
// Allow config account defaults to be used.
|
||||
Transaction.prototype.account_default = function (account) {
|
||||
|
||||
@@ -926,7 +926,19 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
|
||||
{
|
||||
case STI_UINT8:
|
||||
if (value.isString())
|
||||
{
|
||||
#if 0
|
||||
if (field == sfTransactionResult)
|
||||
{
|
||||
TER terCode;
|
||||
if (FUNCTION_THAT_DOESNT_EXIST(value.asString(), terCode))
|
||||
value = static_cast<int>(terCode);
|
||||
else
|
||||
data.push_back(new STUInt8(field, lexical_cast_st<unsigned char>(value.asString())));
|
||||
}
|
||||
data.push_back(new STUInt8(field, lexical_cast_st<unsigned char>(value.asString())));
|
||||
#endif
|
||||
}
|
||||
else if (value.isInt())
|
||||
{
|
||||
if (value.asInt() < 0 || value.asInt() > 255)
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "NewcoinAddress.h"
|
||||
#include "utils.h"
|
||||
#include "NewcoinAddress.h"
|
||||
#include "TransactionErr.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
STAmount saZero(CURRENCY_ONE, ACCOUNT_ONE, 0);
|
||||
STAmount saOne(CURRENCY_ONE, ACCOUNT_ONE, 1);
|
||||
@@ -61,11 +64,25 @@ STUInt8* STUInt8::construct(SerializerIterator& u, SField::ref name)
|
||||
|
||||
std::string STUInt8::getText() const
|
||||
{
|
||||
if (getFName() == sfTransactionResult)
|
||||
{
|
||||
std::string token, human;
|
||||
if (transResultInfo(static_cast<TER>(value), token, human))
|
||||
return human;
|
||||
}
|
||||
return boost::lexical_cast<std::string>(value);
|
||||
}
|
||||
|
||||
Json::Value STUInt8::getJson(int) const
|
||||
{
|
||||
if (getFName() == sfTransactionResult)
|
||||
{
|
||||
std::string token, human;
|
||||
if (transResultInfo(static_cast<TER>(value), token, human))
|
||||
return token;
|
||||
else
|
||||
cLog(lsWARNING) << "Unknown result code in metadata: " << value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -339,7 +356,7 @@ STPathSet* STPathSet::construct(SerializerIterator& s, SField::ref name)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
Log(lsINFO) << "STPathSet: Empty path.";
|
||||
cLog(lsINFO) << "STPathSet: Empty path.";
|
||||
|
||||
throw std::runtime_error("empty path");
|
||||
}
|
||||
@@ -354,7 +371,7 @@ STPathSet* STPathSet::construct(SerializerIterator& s, SField::ref name)
|
||||
}
|
||||
else if (iType & ~STPathElement::typeValidBits)
|
||||
{
|
||||
Log(lsINFO) << "STPathSet: Bad path element: " << iType;
|
||||
cLog(lsINFO) << "STPathSet: Bad path element: " << iType;
|
||||
|
||||
throw std::runtime_error("bad path element");
|
||||
}
|
||||
|
||||
@@ -253,7 +253,13 @@ TER TransactionEngine::doCreditSet(const SerializedTransaction& txn)
|
||||
|
||||
// Check if destination makes sense.
|
||||
|
||||
if (!uDstAccountID)
|
||||
if (saLimitAmount.isNegative())
|
||||
{
|
||||
Log(lsINFO) << "doCreditSet: Malformed transaction: Negatived credit limit.";
|
||||
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
else if (!uDstAccountID)
|
||||
{
|
||||
Log(lsINFO) << "doCreditSet: Malformed transaction: Destination account not specifed.";
|
||||
|
||||
|
||||
@@ -8,57 +8,57 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
|
||||
const char* cpToken;
|
||||
const char* cpHuman;
|
||||
} transResultInfoA[] = {
|
||||
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger" },
|
||||
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
|
||||
{ tefBAD_AUTH, "tefBAD_AUTH", "Transaction's public key is not authorized." },
|
||||
{ tefBAD_CLAIM_ID, "tefBAD_CLAIM_ID", "Malformed." },
|
||||
{ tefBAD_GEN_AUTH, "tefBAD_GEN_AUTH", "Not authorized to claim generator." },
|
||||
{ tefBAD_LEDGER, "tefBAD_LEDGER", "Ledger in unexpected state." },
|
||||
{ tefCLAIMED, "tefCLAIMED", "Can not claim a previously claimed account." },
|
||||
{ tefEXCEPTION, "tefEXCEPTION", "Unexpected program state." },
|
||||
{ tefCREATED, "tefCREATED", "Can't add an already created account." },
|
||||
{ tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." },
|
||||
{ tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past" },
|
||||
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger." },
|
||||
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
|
||||
{ tefBAD_AUTH, "tefBAD_AUTH", "Transaction's public key is not authorized." },
|
||||
{ tefBAD_CLAIM_ID, "tefBAD_CLAIM_ID", "Malformed." },
|
||||
{ tefBAD_GEN_AUTH, "tefBAD_GEN_AUTH", "Not authorized to claim generator." },
|
||||
{ tefBAD_LEDGER, "tefBAD_LEDGER", "Ledger in unexpected state." },
|
||||
{ tefCLAIMED, "tefCLAIMED", "Can not claim a previously claimed account." },
|
||||
{ tefEXCEPTION, "tefEXCEPTION", "Unexpected program state." },
|
||||
{ tefCREATED, "tefCREATED", "Can't add an already created account." },
|
||||
{ tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." },
|
||||
{ tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past" },
|
||||
|
||||
{ telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: too many paths." },
|
||||
{ telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." },
|
||||
{ telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: too many paths." },
|
||||
{ telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." },
|
||||
|
||||
{ temBAD_AMOUNT, "temBAD_AMOUNT", "Can only send positive amounts." },
|
||||
{ temBAD_AMOUNT, "temBAD_AMOUNT", "Can only send positive amounts." },
|
||||
{ temBAD_AUTH_MASTER, "temBAD_AUTH_MASTER", "Auth for unclaimed account needs correct master key." },
|
||||
{ temBAD_EXPIRATION, "temBAD_EXPIRATION", "Malformed." },
|
||||
{ temBAD_ISSUER, "temBAD_ISSUER", "Malformed." },
|
||||
{ temBAD_OFFER, "temBAD_OFFER", "Malformed." },
|
||||
{ temBAD_PATH, "temBAD_PATH", "Malformed." },
|
||||
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed." },
|
||||
{ temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: bad publish." },
|
||||
{ temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: transfer rate must be >= 1.0" },
|
||||
{ temBAD_SET_ID, "temBAD_SET_ID", "Malformed." },
|
||||
{ temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." },
|
||||
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
||||
{ temDST_NEEDED, "temDST_NEEDED", "Destination not specified." },
|
||||
{ temINSUF_FEE_P, "temINSUF_FEE_P", "Fee not allowed." },
|
||||
{ temINVALID, "temINVALID", "The transaction is ill-formed" },
|
||||
{ temREDUNDANT, "temREDUNDANT", "Sends same currency to self." },
|
||||
{ temRIPPLE_EMPTY, "temRIPPLE_EMPTY", "PathSet with no paths." },
|
||||
{ temBAD_EXPIRATION, "temBAD_EXPIRATION", "Malformed." },
|
||||
{ temBAD_ISSUER, "temBAD_ISSUER", "Malformed." },
|
||||
{ temBAD_OFFER, "temBAD_OFFER", "Malformed." },
|
||||
{ temBAD_PATH, "temBAD_PATH", "Malformed." },
|
||||
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed." },
|
||||
{ temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: Bad publish." },
|
||||
{ temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" },
|
||||
{ temBAD_SET_ID, "temBAD_SET_ID", "Malformed." },
|
||||
{ temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." },
|
||||
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
|
||||
{ temDST_NEEDED, "temDST_NEEDED", "Destination not specified." },
|
||||
{ temINSUF_FEE_P, "temINSUF_FEE_P", "Fee not allowed." },
|
||||
{ temINVALID, "temINVALID", "The transaction is ill-formed." },
|
||||
{ temREDUNDANT, "temREDUNDANT", "Sends same currency to self." },
|
||||
{ temRIPPLE_EMPTY, "temRIPPLE_EMPTY", "PathSet with no paths." },
|
||||
{ temUNCERTAIN, "temUNCERTAIN", "In process of determining result. Never returned." },
|
||||
{ temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet." },
|
||||
|
||||
{ tepPATH_DRY, "tepPATH_DRY", "Path could not send partial amount." },
|
||||
{ tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." },
|
||||
{ tepPATH_DRY, "tepPATH_DRY", "Path could not send partial amount." },
|
||||
{ tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." },
|
||||
|
||||
{ terDIR_FULL, "terDIR_FULL", "Can not add entry to full dir." },
|
||||
{ terDIR_FULL, "terDIR_FULL", "Can not add entry to full dir." },
|
||||
{ terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." },
|
||||
{ terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
|
||||
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." },
|
||||
{ terNO_DST, "terNO_DST", "The destination does not exist" },
|
||||
{ terNO_LINE, "terNO_LINE", "No such line." },
|
||||
{ terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
|
||||
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." },
|
||||
{ terNO_DST, "terNO_DST", "The destination does not exist." },
|
||||
{ terNO_LINE, "terNO_LINE", "No such line." },
|
||||
{ terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." },
|
||||
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
|
||||
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction" },
|
||||
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
|
||||
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
|
||||
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." },
|
||||
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
|
||||
{ terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." },
|
||||
|
||||
{ tesSUCCESS, "tesSUCCESS", "The transaction was applied" },
|
||||
{ tesSUCCESS, "tesSUCCESS", "The transaction was applied." },
|
||||
};
|
||||
|
||||
int iIndex = NUMBER(transResultInfoA);
|
||||
|
||||
@@ -69,7 +69,7 @@ enum TER // aka TransactionEngineResult
|
||||
|
||||
// -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent)
|
||||
// Causes:
|
||||
// - Priror application of another, possibly non-existant, transaction could allow this transaction to succeed.
|
||||
// - Prior application of another, possibly non-existant, another transaction could allow this transaction to succeed.
|
||||
// Implications:
|
||||
// - Not applied
|
||||
// - Not forwarded
|
||||
@@ -103,9 +103,10 @@ enum TER // aka TransactionEngineResult
|
||||
// - Applied
|
||||
// - Forwarded
|
||||
// Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY.
|
||||
// CAUTION: The numerical values for these results are part of the binary formats
|
||||
tepPARTIAL = 100,
|
||||
tepPATH_DRY,
|
||||
tepPATH_PARTIAL,
|
||||
tepPATH_DRY = 101,
|
||||
tepPATH_PARTIAL = 102,
|
||||
};
|
||||
|
||||
#define isTelLocal(x) ((x) >= telLOCAL_ERROR && (x) < temMALFORMED)
|
||||
|
||||
@@ -100,14 +100,8 @@ STObject TransactionMetaSet::getAsObject() const
|
||||
|
||||
void TransactionMetaSet::addRaw(Serializer& s, TER result)
|
||||
{
|
||||
mResult = 255;
|
||||
|
||||
if (result == tesSUCCESS)
|
||||
mResult = 0;
|
||||
else if (result == tepPATH_DRY)
|
||||
mResult = 1;
|
||||
else if (result == tepPATH_PARTIAL)
|
||||
mResult = 2;
|
||||
mResult = static_cast<int>(result);
|
||||
assert((mResult == 0) || ((mResult > 100) && (mResult <= 255)));
|
||||
|
||||
mNodes.sort(compare);
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
void doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvRequest);
|
||||
void doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest);
|
||||
void doSubmit(Json::Value& jvResult, const Json::Value& jvRequest);
|
||||
void doTransactionEntry(Json::Value& jvResult, const Json::Value& jvRequest);
|
||||
|
||||
// Streaming Commands
|
||||
void doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest);
|
||||
@@ -161,7 +162,7 @@ public:
|
||||
{
|
||||
Json::FastWriter jfwWriter;
|
||||
|
||||
cLog(lsDEBUG) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'";
|
||||
// cLog(lsDEBUG) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'";
|
||||
|
||||
send(cpClient, jfwWriter.write(jvObj));
|
||||
}
|
||||
@@ -311,6 +312,7 @@ Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest)
|
||||
{ "ledger_current", &WSConnection::doLedgerCurrent },
|
||||
{ "ledger_entry", &WSConnection::doLedgerEntry },
|
||||
{ "submit", &WSConnection::doSubmit },
|
||||
{ "transaction_entry", &WSConnection::doTransactionEntry },
|
||||
|
||||
// Streaming commands:
|
||||
{ "account_info_subscribe", &WSConnection::doAccountInfoSubscribe },
|
||||
@@ -984,6 +986,52 @@ void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest)
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doTransactionEntry(Json::Value& jvResult, const Json::Value& jvRequest)
|
||||
{
|
||||
if (!jvRequest.isMember("transaction"))
|
||||
{
|
||||
jvResult["error"] = "fieldNotFoundTransaction";
|
||||
}
|
||||
if (!jvRequest.isMember("ledger_closed"))
|
||||
{
|
||||
jvResult["error"] = "notYetImplemented"; // XXX We don't support any transaction yet.
|
||||
}
|
||||
else
|
||||
{
|
||||
uint256 uTransID;
|
||||
// XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure.
|
||||
uTransID.SetHex(jvRequest["transaction"].asString());
|
||||
|
||||
uint256 uLedgerID;
|
||||
// XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure.
|
||||
uLedgerID.SetHex(jvRequest["ledger_closed"].asString());
|
||||
|
||||
Ledger::pointer lpLedger = theApp->getMasterLedger().getLedgerByHash(uLedgerID);
|
||||
|
||||
if (!lpLedger) {
|
||||
jvResult["error"] = "ledgerNotFound";
|
||||
}
|
||||
else
|
||||
{
|
||||
Transaction::pointer tpTrans;
|
||||
TransactionMetaSet::pointer tmTrans;
|
||||
|
||||
if (!lpLedger-> getTransaction(uTransID, tpTrans, tmTrans))
|
||||
{
|
||||
jvResult["error"] = "transactionNotFound";
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["transaction"] = tpTrans->getJson(0);
|
||||
jvResult["metadata"] = tmTrans->getJson(0);
|
||||
// 'accounts'
|
||||
// 'engine_...'
|
||||
// 'ledger_...'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WSConnection::doTransactionSubcribe(Json::Value& jvResult, const Json::Value& jvRequest)
|
||||
{
|
||||
if (!mNetwork.subTransaction(this))
|
||||
|
||||
@@ -63,7 +63,8 @@ buster.testCase("Remote functions", {
|
||||
console.log(m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
},
|
||||
|
||||
'request_ledger_closed' :
|
||||
@@ -78,7 +79,8 @@ buster.testCase("Remote functions", {
|
||||
console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
},
|
||||
|
||||
'manual account_root success' :
|
||||
@@ -100,13 +102,15 @@ buster.testCase("Remote functions", {
|
||||
console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
})
|
||||
.on('error', function(m) {
|
||||
console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
},
|
||||
|
||||
'account_root remote malformedAddress' :
|
||||
@@ -129,13 +133,15 @@ buster.testCase("Remote functions", {
|
||||
buster.assert.equals(m.error, 'remoteError');
|
||||
buster.assert.equals(m.remote.error, 'malformedAddress');
|
||||
done();
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
})
|
||||
.on('error', function(m) {
|
||||
console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
},
|
||||
|
||||
'account_root entryNotFound' :
|
||||
@@ -158,7 +164,8 @@ buster.testCase("Remote functions", {
|
||||
buster.assert.equals(m.error, 'remoteError');
|
||||
buster.assert.equals(m.remote.error, 'entryNotFound');
|
||||
done();
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
})
|
||||
.on('error', function(m) {
|
||||
console.log("error: %s", m);
|
||||
@@ -187,13 +194,15 @@ buster.testCase("Remote functions", {
|
||||
console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
}).
|
||||
request();
|
||||
})
|
||||
.on('error', function(m) {
|
||||
console.log(m);
|
||||
|
||||
buster.assert(false);
|
||||
}).request();
|
||||
})
|
||||
.request();
|
||||
},
|
||||
|
||||
'create account' :
|
||||
@@ -211,7 +220,8 @@ buster.testCase("Remote functions", {
|
||||
console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
}).submit();
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user