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

This commit is contained in:
jed
2012-10-16 16:50:42 -07:00
9 changed files with 382 additions and 111 deletions

View File

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

View File

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

View File

@@ -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");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();
},
});