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

This commit is contained in:
jed
2012-11-26 22:14:52 -08:00
41 changed files with 2434 additions and 2180 deletions

View File

@@ -92,6 +92,7 @@ public:
bool isGeneric() const { return fieldCode == 0; }
bool isInvalid() const { return fieldCode == -1; }
bool isUseful() const { return fieldCode > 0; }
bool isKnown() const { return fieldType != STI_UNKNOWN; }
bool isBinary() const { return fieldValue < 256; }
bool isDiscardable() const { return fieldValue > 256; }

View File

@@ -23,22 +23,23 @@ enum JobType
jtINVALID = -1,
jtVALIDATION_ut = 0, // A validation from an untrusted source
jtCLIENTOP_ut = 1, // A client operation from a non-local/untrusted source
jtTRANSACTION = 2, // A transaction received from the network
jtPROPOSAL_ut = 3, // A proposal from an untrusted source
jtCLIENTOP_t = 4, // A client operation from a trusted source
jtVALIDATION_t = 5, // A validation from a trusted source
jtTRANSACTION_l = 6, // A local transaction
jtPROPOSAL_t = 7, // A proposal from a trusted source
jtADMIN = 8, // An administrative operation
jtDEATH = 9, // job of death, used internally
jtPROOFWORK = 2, // A proof of work demand from another server
jtTRANSACTION = 3, // A transaction received from the network
jtPROPOSAL_ut = 4, // A proposal from an untrusted source
jtCLIENTOP_t = 5, // A client operation from a trusted source
jtVALIDATION_t = 6, // A validation from a trusted source
jtTRANSACTION_l = 7, // A local transaction
jtPROPOSAL_t = 8, // A proposal from a trusted source
jtADMIN = 9, // An administrative operation
jtDEATH = 10, // job of death, used internally
// special types not dispatched by the job pool
jtCLIENT = 10,
jtPEER = 11,
jtDISK = 12,
jtRPC = 13,
jtACCEPTLEDGER = 14,
jtPUBLEDGER = 15,
jtCLIENT = 16,
jtPEER = 17,
jtDISK = 18,
jtRPC = 19,
jtACCEPTLEDGER = 20,
jtPUBLEDGER = 21,
};
#define NUM_JOB_TYPES 24

View File

@@ -328,8 +328,8 @@ bool LedgerEntrySet::threadTx(SLE::ref threadTo, Ledger::ref ledger,
if (!threadTo->thread(mSet.getTxID(), mSet.getLgrSeq(), prevTxID, prevLgrID))
return false;
if (prevTxID.isZero() || TransactionMetaSet::thread(mSet.getAffectedNode(threadTo->getIndex(), sfModifiedNode),
prevTxID, prevLgrID))
if (prevTxID.isZero() ||
TransactionMetaSet::thread(mSet.getAffectedNode(threadTo, sfModifiedNode), prevTxID, prevLgrID))
return true;
assert(false);
@@ -359,7 +359,7 @@ bool LedgerEntrySet::threadOwners(SLE::ref node, Ledger::ref ledger,
return false;
}
void LedgerEntrySet::calcRawMeta(Serializer& s, TER result)
void LedgerEntrySet::calcRawMeta(Serializer& s, TER result, uint32 index)
{ // calculate the raw meta data and return it. This must be called before the set is committed
// Entries modified only as a result of building the transaction metadata
@@ -402,14 +402,17 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result)
SLE::pointer origNode = mLedger->getSLE(it.first);
SLE::pointer curNode = it.second.mEntry;
if ((type == &sfModifiedNode) && (*curNode == *origNode))
continue;
uint16 nodeType = curNode ? curNode->getFieldU16(sfLedgerEntryType) : origNode->getFieldU16(sfLedgerEntryType);
mSet.setAffectedNode(it.first, *type, nodeType);
if (type == &sfDeletedNode)
{
assert(origNode);
assert(curNode);
assert(origNode && curNode);
threadOwners(origNode, mLedger, newMod); // thread transaction to owners
STObject prevs(sfPreviousFields);
@@ -432,6 +435,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result)
}
else if (type == &sfModifiedNode)
{
assert(curNode && origNode);
if (curNode->isThreadedType()) // thread transaction to node it modified
threadTx(curNode, mLedger, newMod);
@@ -455,7 +459,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result)
}
else if (type == &sfCreatedNode) // if created, thread to owner(s)
{
assert(!origNode);
assert(curNode && !origNode);
threadOwners(curNode, mLedger, newMod);
if (curNode->isThreadedType()) // always thread to self
@@ -470,6 +474,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result)
if (!news.empty())
mSet.getAffectedNode(it.first).addObject(news);
}
else assert(false);
}
// add any new modified nodes to the modification set
@@ -477,7 +482,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result)
BOOST_FOREACH(u256_sle_pair& it, newMod)
entryModify(it.second);
mSet.addRaw(s, result);
mSet.addRaw(s, result, index);
cLog(lsTRACE) << "Metadata:" << mSet.getJson(0);
}

View File

@@ -124,7 +124,7 @@ public:
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault);
Json::Value getJson(int) const;
void calcRawMeta(Serializer&, TER result);
void calcRawMeta(Serializer&, TER result, uint32 index);
// iterator functions
typedef std::map<uint256, LedgerEntrySetEntry>::iterator iterator;

View File

@@ -581,6 +581,17 @@ void Peer::processReadBuffer()
}
break;
case ripple::mtPROOFOFWORK:
{
ripple::TMProofWork msg;
if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvProofWork(msg);
else
cLog(lsWARNING) << "parse error: " << type;
}
break;
default:
cLog(lsWARNING) << "Unknown Msg: " << type;
cLog(lsWARNING) << strHex(&mReadbuf[0], mReadbuf.size());
@@ -1124,6 +1135,23 @@ void Peer::recvAccount(ripple::TMAccount& packet)
{
}
void Peer::recvProofWork(ripple::TMProofWork& packet)
{
if (packet.has_result())
{ // this is a reply to a proof of work we sent
// WRITEME
return;
}
if (packet.has_target() && packet.has_challenge() && packet.has_iterations())
{ // this is a challenge
// WRITEME
return;
}
cLog(lsINFO) << "Received in valid proof of work object from peer";
}
void Peer::recvStatus(ripple::TMStatusChange& packet)
{
cLog(lsTRACE) << "Received status change from peer " << getIP();

View File

@@ -126,6 +126,7 @@ protected:
void recvStatus(ripple::TMStatusChange& packet);
void recvPropose(const boost::shared_ptr<ripple::TMProposeSet>& packet);
void recvHaveTxSet(ripple::TMHaveTransactionSet& packet);
void recvProofWork(ripple::TMProofWork& packet);
void getSessionCookie(std::string& strDst);

File diff suppressed because it is too large Load Diff

View File

@@ -101,20 +101,32 @@ public:
STAmount saOutPass; // <-- Amount actually sent.
bool bConsumed; // If true, use consumes full liquidity. False, may or may not.
PathState* setIndex(const int iIndex) {
mIndex = iIndex;
return this;
}
PathState(
const int iIndex,
const STAmount& saSend,
const STAmount& saSendMax,
const Ledger::ref lrLedger = Ledger::pointer()
) : mLedger(lrLedger), saInReq(saSendMax), saOutReq(saSend) { ; }
void setExpanded(
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID,
const STAmount& saSend,
const STAmount& saSendMax
const uint160& uSenderID
);
void setCanonical(
PathState::ref pspExpanded
);
Json::Value getJson() const;
static PathState::pointer createPathState(
const int iIndex,
static PathState::pointer createExpanded(
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
@@ -123,7 +135,22 @@ public:
const STAmount& saSendMax
)
{
return boost::make_shared<PathState>(iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax);
PathState::pointer pspNew = boost::make_shared<PathState>(saSend, saSendMax, lesSource.getLedgerRef());
pspNew->setExpanded(lesSource, spSourcePath, uReceiverID, uSenderID);
return pspNew;
}
static PathState::pointer createCanonical(
PathState::ref pspExpanded
)
{
PathState::pointer pspNew = boost::make_shared<PathState>(pspExpanded->saOutAct, pspExpanded->saInAct);
pspNew->setCanonical(pspExpanded);
return pspNew;
}
static bool lessPriority(PathState::ref lhs, PathState::ref rhs);
@@ -141,7 +168,6 @@ public:
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
PathState::pointer pathCreate(const STPath& spPath);
void pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
TER calcNode(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
TER calcNodeRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);

View File

@@ -57,6 +57,7 @@
FIELD(OfferSequence, UINT32, 25)
FIELD(FirstLedgerSequence, UINT32, 26)
FIELD(LastLedgerSequence, UINT32, 27)
FIELD(TransactionIndex, UINT32, 28)
// 64-bit integers
FIELD(IndexNext, UINT64, 1)

View File

@@ -934,7 +934,7 @@ STArray* STArray::construct(SerializerIterator& sit, SField::ref field)
throw std::runtime_error("Unknown field");
}
value.push_back(STObject(fn));
value.push_back(new STObject(fn));
value.rbegin()->set(sit, 1);
}
@@ -1234,6 +1234,9 @@ BOOST_AUTO_TEST_SUITE(SerializedObject)
BOOST_AUTO_TEST_CASE( FieldManipulation_test )
{
if (sfGeneric.isUseful())
BOOST_FAIL("sfGeneric must not be useful");
SField sfTestVL(STI_VL, 255, "TestVL");
SField sfTestH256(STI_HASH256, 255, "TestH256");
SField sfTestU32(STI_UINT32, 255, "TestU32");

View File

@@ -46,6 +46,8 @@ public:
STObject(const std::vector<SOElement::ptr>& type, SerializerIterator& sit, SField::ref name) : SerializedType(name)
{ set(sit); setType(type); }
std::auto_ptr<STObject> oClone() const { return std::auto_ptr<STObject>(new STObject(*this)); }
static std::auto_ptr<STObject> parseJson(const Json::Value& value, SField::ref name = sfGeneric, int depth = 0);
virtual ~STObject() { ; }
@@ -81,7 +83,7 @@ public:
SerializedType& back() { return mData.back(); }
const SerializedType& back() const { return mData.back(); }
int getCount() const { return mData.size(); }
int getCount() const { return mData.size(); }
bool setFlag(uint32);
bool clearFlag(uint32);
@@ -167,6 +169,10 @@ public:
bool operator!=(const STObject& o) const { return ! (*this == o); }
};
// allow ptr_* collections of STObject's
inline STObject* new_clone(const STObject& s) { return s.oClone().release(); }
inline void delete_clone(const STObject* s) { boost::checked_delete(s); }
inline STObject::iterator range_begin(STObject& x) { return x.begin(); }
inline STObject::iterator range_end(STObject &x) { return x.end(); }
namespace boost
@@ -180,12 +186,12 @@ namespace boost
class STArray : public SerializedType, private IS_INSTANCE(SerializedArray)
{
public:
typedef std::vector<STObject> vector;
typedef std::vector<STObject>::iterator iterator;
typedef std::vector<STObject>::const_iterator const_iterator;
typedef std::vector<STObject>::reverse_iterator reverse_iterator;
typedef std::vector<STObject>::const_reverse_iterator const_reverse_iterator;
typedef std::vector<STObject>::size_type size_type;
typedef boost::ptr_vector<STObject> vector;
typedef boost::ptr_vector<STObject>::iterator iterator;
typedef boost::ptr_vector<STObject>::const_iterator const_iterator;
typedef boost::ptr_vector<STObject>::reverse_iterator reverse_iterator;
typedef boost::ptr_vector<STObject>::const_reverse_iterator const_reverse_iterator;
typedef boost::ptr_vector<STObject>::size_type size_type;
protected:
@@ -210,7 +216,7 @@ public:
vector& getValue() { return value; }
// vector-like functions
void push_back(const STObject& object) { value.push_back(object); }
void push_back(const STObject& object) { value.push_back(object.oClone()); }
STObject& operator[](int j) { return value[j]; }
const STObject& operator[](int j) const { return value[j]; }
iterator begin() { return value.begin(); }

View File

@@ -19,6 +19,15 @@ DECLARE_INSTANCE(SerializedValue);
STAmount saZero(CURRENCY_ONE, ACCOUNT_ONE, 0);
STAmount saOne(CURRENCY_ONE, ACCOUNT_ONE, 1);
SerializedType& SerializedType::operator=(const SerializedType& t)
{
if ((t.fName != fName) && fName->isUseful() && t.fName->isUseful())
Log(lsWARNING) << "Caution: " << t.fName->getName() << " not replacing " << fName->getName();
if (!fName->isUseful()) fName = t.fName;
return *this;
}
void STPathSet::printDebug() {
for (int i = 0; i < value.size(); i++) {
std::cout << i << ": ";

View File

@@ -10,6 +10,15 @@
#include "Serializer.h"
#include "FieldNames.h"
#include "InstanceCounter.h"
#include "Log.h"
// CAUTION: Do not create a vector (or similar container) of any object derived from
// SerializedType. Use Boost ptr_* containers. The copy assignment operator of
// SerializedType has semantics that will cause contained types to change their names
// when an object is deleted because copy assignment is used to "slide down" the
// remaining types and this will not copy the field name. Changing the copy assignment
// operator to copy the field name breaks the use of copy assignment just to copy values,
// which is used in the transaction engine code.
enum PathFlags
{
@@ -68,8 +77,8 @@ public:
void addFieldID(Serializer& s) const { s.addFieldID(fName->fieldType, fName->fieldValue); }
SerializedType& operator=(const SerializedType& t)
{ if (!fName->fieldCode) fName = t.fName; return *this; }
SerializedType& operator=(const SerializedType& t);
bool operator==(const SerializedType& t) const
{ return (getSType() == t.getSType()) && isEquivalent(t); }
bool operator!=(const SerializedType& t) const

View File

@@ -119,7 +119,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa
{
// Transaction succeeded fully or (retries are not allowed and the transaction succeeded partially).
Serializer m;
mNodes.calcRawMeta(m, terResult);
mNodes.calcRawMeta(m, terResult, mTxnSeq++);
txnWrite();

View File

@@ -56,6 +56,7 @@ private:
protected:
Ledger::pointer mLedger;
int mTxnSeq;
uint160 mTxnAccountID;
SLE::pointer mTxnAccount;
@@ -65,8 +66,8 @@ protected:
public:
typedef boost::shared_ptr<TransactionEngine> pointer;
TransactionEngine() { ; }
TransactionEngine(Ledger::ref ledger) : mLedger(ledger) { assert(mLedger); }
TransactionEngine() : mTxnSeq(0) { ; }
TransactionEngine(Ledger::ref ledger) : mLedger(ledger), mTxnSeq(0) { assert(mLedger); }
LedgerEntrySet& getNodes() { return mNodes; }
Ledger::pointer getLedger() { return mLedger; }

View File

@@ -20,6 +20,7 @@ TransactionMetaSet::TransactionMetaSet(const uint256& txid, uint32 ledger, const
throw std::runtime_error("bad metadata");
mResult = obj->getFieldU8(sfTransactionResult);
mIndex = obj->getFieldU32(sfTransactionIndex);
mNodes = * dynamic_cast<STArray*>(&obj->getField(sfAffectedNodes));
}
@@ -79,20 +80,21 @@ std::vector<RippleAddress> TransactionMetaSet::getAffectedAccounts()
}
*/
STObject& TransactionMetaSet::getAffectedNode(const uint256& node, SField::ref type)
STObject& TransactionMetaSet::getAffectedNode(SLE::ref node, SField::ref type)
{
assert(&type);
uint256 index = node->getIndex();
BOOST_FOREACH(STObject& it, mNodes)
{
if (it.getFieldH256(sfLedgerIndex) == node)
if (it.getFieldH256(sfLedgerIndex) == index)
return it;
}
mNodes.push_back(STObject(sfModifiedNode));
mNodes.push_back(STObject(type));
STObject& obj = mNodes.back();
assert(obj.getFName() == type);
obj.setFieldH256(sfLedgerIndex, node);
obj.setFieldH256(sfLedgerIndex, index);
obj.setFieldU16(sfLedgerEntryType, node->getFieldU16(sfLedgerEntryType));
return obj;
}
@@ -153,13 +155,15 @@ STObject TransactionMetaSet::getAsObject() const
STObject metaData(sfTransactionMetaData);
assert(mResult != 255);
metaData.setFieldU8(sfTransactionResult, mResult);
metaData.setFieldU32(sfTransactionIndex, mIndex);
metaData.addObject(mNodes);
return metaData;
}
void TransactionMetaSet::addRaw(Serializer& s, TER result)
void TransactionMetaSet::addRaw(Serializer& s, TER result, uint32 index)
{
mResult = static_cast<int>(result);
mIndex = index;
assert((mResult == 0) || ((mResult > 100) && (mResult <= 255)));
mNodes.sort(compare);

View File

@@ -12,6 +12,7 @@
#include "Serializer.h"
#include "SerializedTypes.h"
#include "SerializedObject.h"
#include "SerializedLedger.h"
#include "TransactionErr.h"
class TransactionMetaSet
@@ -22,13 +23,15 @@ public:
protected:
uint256 mTransactionID;
uint32 mLedger;
uint32 mIndex;
int mResult;
STArray mNodes;
public:
TransactionMetaSet() : mLedger(0), mResult(255) { ; }
TransactionMetaSet(const uint256& txID, uint32 ledger) : mTransactionID(txID), mLedger(ledger), mResult(255) { ; }
TransactionMetaSet() : mLedger(0), mIndex(static_cast<uint32>(-1)), mResult(255) { ; }
TransactionMetaSet(const uint256& txID, uint32 ledger, uint32 index) :
mTransactionID(txID), mLedger(ledger), mIndex(static_cast<uint32>(-1)), mResult(255) { ; }
TransactionMetaSet(const uint256& txID, uint32 ledger, const std::vector<unsigned char>&);
void init(const uint256& transactionID, uint32 ledger);
@@ -39,17 +42,18 @@ public:
uint32 getLgrSeq() { return mLedger; }
int getResult() const { return mResult; }
TER getResultTER() const { return static_cast<TER>(mResult); }
uint32 getIndex() const { return mIndex; }
bool isNodeAffected(const uint256&) const;
void setAffectedNode(const uint256&, SField::ref type, uint16 nodeType);
STObject& getAffectedNode(const uint256&, SField::ref type);
STObject& getAffectedNode(SLE::ref node, SField::ref type); // create if needed
STObject& getAffectedNode(const uint256&);
const STObject& peekAffectedNode(const uint256&) const;
//std::vector<RippleAddress> getAffectedAccounts();
Json::Value getJson(int p) const { return getAsObject().getJson(p); }
void addRaw(Serializer&, TER);
void addRaw(Serializer&, TER, uint32 index);
STObject getAsObject() const;

View File

@@ -5,7 +5,7 @@ enum MessageType {
mtHELLO = 1;
mtERROR_MSG = 2;
mtPING = 3;
mtPOW = 4;
mtPROOFOFWORK = 4;
// network presence detection
mtGET_CONTACTS = 10;
@@ -33,13 +33,13 @@ enum MessageType {
}
// empty message (or just result) = request proof of work
// token, iterations, target, challenge = give proof of work challenge
// token, response = show proof of work
// token, result = result of pow attempt
// token, iterations, target, challenge = issue demand for proof of work
// token, response = give solution to proof of work
// token, result = report result of pow
message TMProofWork
{
optional string token = 1;
required string token = 1;
optional uint32 iterations = 2;
optional bytes target = 3;
optional bytes challenge = 4;

View File

@@ -22,4 +22,4 @@ var Account = function (network, account) {
exports.Account = Account;
// vim:sw=2:sts=2:ts=8
// vim:sw=2:sts=2:ts=8:et

View File

@@ -344,6 +344,10 @@ Amount.from_json = function(j) {
return (new Amount()).parse_json(j);
};
Amount.from_human = function(j) {
return (new Amount()).parse_human(j);
};
Amount.is_valid = function (j) {
return Amount.from_json(j).is_valid();
};
@@ -388,6 +392,18 @@ Amount.prototype.currency = function() {
return this._currency;
};
Amount.prototype.set_currency = function(c) {
if ('string' === typeof c) {
this._currency.parse_json(c);
}
else
{
c.copyTo(this._currency);
}
return this;
};
// Only checks the value. Not the currency and issuer.
Amount.prototype.is_valid = function() {
return !isNaN(this._value);
@@ -462,8 +478,15 @@ Amount.prototype.to_human = function (opts)
{
opts = opts || {};
var int_part = this._value.divide(consts.bi_xns_unit).toString(10);
var fraction_part = this._value.mod(consts.bi_xns_unit).toString(10);
// Default options
if ("undefined" === typeof opts.group_sep) opts.group_sep = true;
opts.group_width = opts.group_width || 3;
var denominator = this._is_native ?
consts.bi_xns_unit :
consts.bi_10.clone().pow(-this._offset);
var int_part = this._value.divide(denominator).toString(10);
var fraction_part = this._value.mod(denominator).toString(10);
int_part = int_part.replace(/^0*/, '');
fraction_part = fraction_part.replace(/0*$/, '');
@@ -472,6 +495,13 @@ Amount.prototype.to_human = function (opts)
fraction_part = fraction_part.slice(0, opts.precision);
}
if (opts.group_sep) {
if ("string" !== typeof opts.group_sep) {
opts.group_sep = ',';
}
int_part = utils.chunkString(int_part, opts.group_width, true).join(opts.group_sep);
}
var formatted = '';
formatted += int_part.length ? int_part : '0';
formatted += fraction_part.length ? '.'+fraction_part : '';
@@ -534,6 +564,67 @@ Amount.prototype.to_text_full = function() {
: this.to_text() + "/" + this._currency.to_json() + "/" + this._issuer.to_json();
};
/**
* Tries to correctly interpret an amount as entered by a user.
*
* Examples:
*
* XRP 250 => 250000000/XRP
* 25.2 XRP => 25200000/XRP
* USD 100.40 => 100.4/USD/?
* 100 => 100000000/XRP
*/
Amount.prototype.parse_human = function(j) {
// Cast to string
j = ""+j;
// Parse
var m = j.match(/^\s*([a-z]{3})?\s*(-)?(\d+)(?:\.(\d*))?\s*([a-z]{3})?\s*$/i);
if (m) {
var currency = m[1] || m[5] || "XRP",
integer = m[3] || "0",
fraction = m[4] || "",
precision = null;
currency = currency.toUpperCase();
this._value = new BigInteger(integer);
this.set_currency(currency);
// XRP have exactly six digits of precision
if (currency === 'XRP') {
fraction = fraction.slice(0, 6);
while (fraction.length < 6) {
fraction += "0";
}
this._is_native = true;
this._value = this._value.multiply(consts.bi_xns_unit).add(new BigInteger(fraction));
}
// Other currencies have arbitrary precision
else {
while (fraction[fraction.length - 1] === "0") {
fraction = fraction.slice(0, fraction.length - 1);
}
precision = fraction.length;
this._is_native = false;
var multiplier = consts.bi_10.clone().pow(precision);
this._value = this._value.multiply(multiplier).add(new BigInteger(fraction));
this._offset = -precision;
this.canonicalize();
}
this._is_negative = !!m[2];
} else {
this._value = NaN;
}
return this;
};
// Parse a XRP value from untrusted input.
// - integer = raw units
// - float = with precision 6
@@ -551,7 +642,7 @@ Amount.prototype.parse_native = function(j) {
this._value = new BigInteger(m[2]);
}
else {
// Float notation
// Float notation : values multiplied by 1,000,000.
var int_part = (new BigInteger(m[2])).multiply(consts.bi_xns_unit);
var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+consts.xns_precision-m[3].length))));
@@ -578,7 +669,8 @@ Amount.prototype.parse_native = function(j) {
return this;
};
// Parse a non-native value.
// Parse a non-native value for the json wire format.
// Requires _currency to be set!
Amount.prototype.parse_value = function(j) {
this._is_native = false;
@@ -647,9 +739,9 @@ Amount.prototype.parse_json = function(j) {
var m = j.match(/^(.+)\/(...)\/(.+)$/);
if (m) {
this.parse_value(m[1]);
this._currency = Currency.from_json(m[2]);
this._issuer = UInt160.from_json(m[3]);
this.parse_value(m[1]);
}
else {
this.parse_native(j);
@@ -663,9 +755,9 @@ Amount.prototype.parse_json = function(j) {
else if ('object' === typeof j && 'value' in j) {
// Parse the passed value to sanitize and copy it.
this.parse_value(j.value);
this._currency.parse_json(j.currency); // Never XRP.
this._issuer.parse_json(j.issuer);
this.parse_value(j.value);
}
else {
this._value = NaN;
@@ -727,4 +819,4 @@ exports.UInt160 = UInt160;
exports.config = {};
// vim:sw=2:sts=2:ts=8
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1216,4 +1216,4 @@ BigInteger.ONE = nbv(1);
exports.nbi = nbi;
exports.BigInteger = BigInteger;
// vim:sw=2:sts=2:ts=8
// vim:sw=2:sts=2:ts=8:et

View File

@@ -55,4 +55,4 @@ Network.protocol.stop = function () {
exports.Network = Network;
// vim:sw=2:sts=2:ts=8
// vim:sw=2:sts=2:ts=8:et

View File

@@ -88,4 +88,4 @@ exports.mkPath = mkPath;
exports.resetPath = resetPath;
exports.rmPath = rmPath;
// vim:sw=2:sts=2:ts=8
// vim:sw=2:sts=2:ts=8:et

View File

@@ -22,6 +22,8 @@ var Amount = require('./amount.js').Amount;
var Currency = require('./amount.js').Currency;
var UInt160 = require('./amount.js').UInt160;
var utils = require('./utils');
// Request events emitted:
// 'success' : Request successful.
// 'error' : Request failed.
@@ -37,23 +39,16 @@ var Request = function (remote, command) {
};
this.remote = remote;
this.requested = false;
this.on('request', function () {
self.request_default();
});
};
Request.prototype = new EventEmitter;
// Send the request to a remote.
Request.prototype.request = function (remote) {
this.emit('request', remote);
};
Request.prototype.request_default = function () {
if (!this.requested) {
this.requested = true;
this.remote.request(this);
this.emit('request', remote);
}
};
@@ -189,7 +184,8 @@ var Remote = function (opts, trace) {
this.trusted = opts.trusted;
this.websocket_ip = opts.websocket_ip;
this.websocket_port = opts.websocket_port;
this.local_sequence = opts.local_sequence;
this.local_sequence = opts.local_sequence; // Locally track sequence numbers
this.local_fee = opts.local_fee; // Locally set fees
this.id = 0;
this.trace = opts.trace || trace;
this._ledger_current_index = undefined;
@@ -460,12 +456,12 @@ Remote.prototype._connect_message = function (ws, json) {
unexpected = true;
}
else if ('success' === message.status) {
if (this.trace) console.log("remote: response: %s", JSON.stringify(message, undefined, 2));
if (this.trace) utils.logObject("remote: response: %s", message);
request.emit('success', message.result);
}
else if (message.error) {
if (this.trace) console.log("remote: error: %s", JSON.stringify(message, undefined, 2));
if (this.trace) utils.logObject("remote: error: %s", message);
request.emit('error', {
'error' : 'remoteError',
@@ -491,7 +487,7 @@ Remote.prototype._connect_message = function (ws, json) {
// Account subscription event
case 'account':
if (this.trace) console.log("remote: account: %s", JSON.stringify(message, undefined, 2));
if (this.trace) utils.logObject("remote: account: %s", message);
break;
default:
@@ -535,12 +531,12 @@ Remote.prototype.request = function (request) {
this.id += 1; // Advance id.
if (this.trace) console.log("remote: request: %s", JSON.stringify(request.message));
if (this.trace) utils.logObject("remote: request: %s", request.message);
this.ws.send(JSON.stringify(request.message));
}
else {
if (this.trace) console.log("remote: request: DROPPING: %s", JSON.stringify(request.message));
if (this.trace) utils.logObject("remote: request: DROPPING: %s", request.message);
}
};
@@ -592,7 +588,8 @@ Remote.prototype.request_ledger_entry = function (type) {
this.type = type;
// Transparent caching:
request.on('request', function (remote) { // Intercept default request.
this.request_default = this.request;
this.request = function () { // Intercept default request.
if (self._ledger_hash) {
// XXX Add caching.
}
@@ -610,7 +607,7 @@ Remote.prototype.request_ledger_entry = function (type) {
if (node) {
// Emulate fetch of ledger entry.
this.request.emit('success', {
self.emit('success', {
// YYY Missing lots of fields.
'node' : node,
});
@@ -634,7 +631,7 @@ Remote.prototype.request_ledger_entry = function (type) {
this.request_default();
}
}
});
};
return request;
};
@@ -1174,7 +1171,7 @@ Transaction.prototype.submit = function (callback) {
// YYY Might check paths for invalid accounts.
if (undefined === tx_json.Fee) {
if (this.remote.local_fee && undefined === tx_json.Fee) {
if ('Payment' === tx_json.TransactionType
&& tx_json.Flags & Remote.flags.Payment.CreateAccount) {
@@ -1346,7 +1343,7 @@ Transaction.prototype.set_flags = function (flags) {
}
}
if (this.tx_json.Flags & Remote.flags.Payment.CreateAccount)
if (this.remote.local_fee && (this.tx_json.Flags & Remote.flags.Payment.CreateAccount))
this.tx_json.Fee = Remote.fees.account_create.to_json();
}
@@ -1399,10 +1396,13 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi
this.secret = this._account_secret(src);
this.tx_json.TransactionType = 'OfferCreate';
this.tx_json.Account = UInt160.json_rewrite(src);
this.tx_json.Fee = Remote.fees.offer.to_json();
this.tx_json.TakerPays = Amount.json_rewrite(taker_pays);
this.tx_json.TakerGets = Amount.json_rewrite(taker_gets);
if (this.remote.local_fee) {
this.tx_json.Fee = Remote.fees.offer.to_json();
}
if (expiration)
this.tx_json.Expiration = Date === expiration.constructor
? expiration.getTime()
@@ -1489,4 +1489,4 @@ Transaction.prototype.wallet_add = function (src, amount, authorized_key, public
exports.config = {};
exports.Remote = Remote;
// vim:sw=2:sts=2:ts=8
// vim:sw=2:sts=2:ts=8:et

View File

@@ -41,4 +41,4 @@ serializer.addUInt160 = function(value) {
serializer.getSHA512Half = function() {
};
// vim:ts=4
// vim:sw=2:sts=2:ts=8:et

View File

@@ -70,10 +70,30 @@ var stringToArray = function (s) {
return a;
};
exports.trace = trace;
exports.arraySet = arraySet;
exports.hexToString = hexToString;
exports.stringToArray = stringToArray;
exports.stringToHex = stringToHex;
var chunkString = function (str, n, leftAlign) {
var ret = [];
var i=0, len=str.length;
if (leftAlign) {
i = str.length % n;
if (i) ret.push(str.slice(0, i));
}
for(; i < len; i += n) {
ret.push(str.slice(i, n+i));
}
return ret;
};
// vim:sw=2:sts=2:ts=8
var logObject = function (msg, obj) {
console.log(msg, JSON.stringify(obj, undefined, 2));
};
exports.trace = trace;
exports.arraySet = arraySet;
exports.hexToString = hexToString;
exports.stringToArray = stringToArray;
exports.stringToHex = stringToHex;
exports.logObject = logObject;
exports.chunkString = chunkString;
// vim:sw=2:sts=2:ts=8:et

7
src/js/utils.web.js Normal file
View File

@@ -0,0 +1,7 @@
exports = module.exports = require('./utils.js');
// We override this function for browsers, because they print objects nicer
// natively than JSON.stringify can.
exports.logObject = function (msg, obj) {
console.log(msg, "", obj);
};