mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 14:05:51 +00:00
Merge branch 'master' into autoclient
Conflicts: src/cpp/ripple/AutoSocket.h
This commit is contained in:
6
grunt.js
6
grunt.js
@@ -32,7 +32,11 @@ module.exports = function(grunt) {
|
||||
"src/js/sjcl/core/convenience.js",
|
||||
"src/js/sjcl/core/bn.js",
|
||||
"src/js/sjcl/core/ecc.js",
|
||||
"src/js/sjcl/core/srp.js"
|
||||
"src/js/sjcl/core/srp.js",
|
||||
"src/js/sjcl-custom/sjcl-secp256k1.js",
|
||||
"src/js/sjcl-custom/sjcl-ripemd160.js",
|
||||
"src/js/sjcl-custom/sjcl-extramath.js",
|
||||
"src/js/sjcl-custom/sjcl-ecdsa-der.js"
|
||||
],
|
||||
dest: 'build/sjcl.js'
|
||||
}
|
||||
|
||||
15
package.json
15
package.json
@@ -1,10 +1,17 @@
|
||||
{
|
||||
"name": "ripple",
|
||||
"name": "ripple-lib",
|
||||
"version": "0.7.0",
|
||||
"description": "Open-source peer-to-peer payment network",
|
||||
"description": "Ripple JavaScript client library",
|
||||
|
||||
"files": [
|
||||
"src/js/*.js",
|
||||
"build/sjcl.js"
|
||||
],
|
||||
"main": "src/js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"async": "~0.1.22",
|
||||
"ws": "~0.4.22",
|
||||
@@ -15,13 +22,15 @@
|
||||
"buster": "~0.6.2",
|
||||
"grunt-webpack": "~0.4.0"
|
||||
},
|
||||
|
||||
"scripts": {
|
||||
"test": "buster test"
|
||||
},
|
||||
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jedmccaleb/NewCoin.git"
|
||||
},
|
||||
"readmeFilename": "README"
|
||||
|
||||
"readmeFilename": "README.md"
|
||||
}
|
||||
|
||||
@@ -219,7 +219,8 @@
|
||||
# [cluster_nodes]:
|
||||
# To extend full trust to other nodes, place their node public keys here.
|
||||
# Generally, you should only do this for nodes under common administration.
|
||||
# Node public keys start with an 'n'.
|
||||
# Node public keys start with an 'n'. To give a node a name for identification
|
||||
# place a space after the public key and then the name.
|
||||
#
|
||||
# [ledger_history]:
|
||||
# The number of past ledgers to acquire on server startup and the minimum to
|
||||
|
||||
@@ -142,7 +142,7 @@ STAmount::STAmount(SField::ref n, const Json::Value& v)
|
||||
std::vector<std::string> elements;
|
||||
boost::split(elements, val, boost::is_any_of("\t\n\r ,/"));
|
||||
|
||||
if ((elements.size() < 0) || (elements.size() > 3))
|
||||
if (elements.size() > 3)
|
||||
throw std::runtime_error("invalid amount string");
|
||||
|
||||
value = elements[0];
|
||||
@@ -1214,6 +1214,40 @@ std::string STAmount::getFullText() const
|
||||
}
|
||||
}
|
||||
|
||||
STAmount STAmount::getRound() const
|
||||
{
|
||||
if (mIsNative)
|
||||
return *this;
|
||||
|
||||
uint64 valueDigits = mValue % 1000000000ull;
|
||||
if (valueDigits == 1)
|
||||
return STAmount(mCurrency, mIssuer, mValue - 1, mOffset, mIsNegative);
|
||||
else if (valueDigits == 999999999ull)
|
||||
return STAmount(mCurrency, mIssuer, mValue + 1, mOffset, mIsNegative);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void STAmount::roundSelf()
|
||||
{
|
||||
if (mIsNative)
|
||||
return;
|
||||
|
||||
uint64 valueDigits = mValue % 1000000000ull;
|
||||
if (valueDigits == 1)
|
||||
{
|
||||
mValue -= 1;
|
||||
if (mValue < cMinValue)
|
||||
canonicalize();
|
||||
}
|
||||
else if (valueDigits == 999999999ull)
|
||||
{
|
||||
mValue += 1;
|
||||
if (mValue > cMaxValue)
|
||||
canonicalize();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::string STAmount::getExtendedText() const
|
||||
{
|
||||
@@ -1460,7 +1494,7 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test )
|
||||
BOOST_TEST_MESSAGE("Amount CC Complete");
|
||||
}
|
||||
|
||||
static void roundTest(int n, int d, int m)
|
||||
static bool roundTest(int n, int d, int m)
|
||||
{ // check STAmount rounding
|
||||
STAmount num(CURRENCY_ONE, ACCOUNT_ONE, n);
|
||||
STAmount den(CURRENCY_ONE, ACCOUNT_ONE, d);
|
||||
@@ -1469,18 +1503,19 @@ static void roundTest(int n, int d, int m)
|
||||
STAmount res = STAmount::multiply(quot, mul, CURRENCY_ONE, ACCOUNT_ONE);
|
||||
if (res.isNative())
|
||||
BOOST_FAIL("Product is native");
|
||||
|
||||
cLog(lsDEBUG) << n << " / " << d << " = " << quot.getText();
|
||||
res.roundSelf();
|
||||
|
||||
STAmount cmp(CURRENCY_ONE, ACCOUNT_ONE, (n * m) / d);
|
||||
if (cmp.isNative())
|
||||
BOOST_FAIL("Comparison amount is native");
|
||||
|
||||
if (res == cmp)
|
||||
return;
|
||||
return true;
|
||||
cmp.throwComparable(res);
|
||||
cLog(lsINFO) << "(" << num.getText() << "/" << den.getText() << ") X " << mul.getText() << " = "
|
||||
cLog(lsWARNING) << "(" << num.getText() << "/" << den.getText() << ") X " << mul.getText() << " = "
|
||||
<< res.getText() << " not " << cmp.getText();
|
||||
BOOST_FAIL("Round fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mulTest(int a, int b)
|
||||
|
||||
@@ -50,7 +50,7 @@ Application::Application() :
|
||||
getRand(mNonce256.begin(), mNonce256.size());
|
||||
getRand(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
|
||||
mJobQueue.setThreadCount();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(20));
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(10));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,15 @@ public:
|
||||
boost::asio::async_write(PlainSocket(), buffers, handler);
|
||||
}
|
||||
|
||||
template <typename Allocator, typename Handler>
|
||||
void async_write(boost::asio::basic_streambuf<Allocator>& buffers, Handler handler)
|
||||
{
|
||||
if (isSecure())
|
||||
boost::asio::async_write(*mSocket, buffers, handler);
|
||||
else
|
||||
boost::asio::async_write(PlainSocket(), buffers, handler);
|
||||
}
|
||||
|
||||
template <typename Buf, typename Condition, typename Handler>
|
||||
void async_read(const Buf& buffers, Condition cond, Handler handler)
|
||||
{
|
||||
|
||||
@@ -55,15 +55,16 @@ const char *LedgerDBInit[] = {
|
||||
);",
|
||||
"CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);",
|
||||
|
||||
"CREATE TABLE LedgerValidations ( \
|
||||
"CREATE TABLE Validations ( \
|
||||
LedgerHash CHARACTER(64), \
|
||||
NodePubKey CHARACTER(56), \
|
||||
Flags BIGINT UNSIGNED, \
|
||||
SignTime BIGINT UNSIGNED, \
|
||||
Signature BLOB \
|
||||
RawData BLOB \
|
||||
);",
|
||||
"CREATE INDEX ValidationByHash ON \
|
||||
LedgerValidations(LedgerHash);",
|
||||
"CREATE INDEX ValidationsByHash ON \
|
||||
Validations(LedgerHash);",
|
||||
"CREATE INDEX ValidationsByTime ON \
|
||||
Validations(SignTime);",
|
||||
|
||||
"END TRANSACTION;"
|
||||
};
|
||||
|
||||
@@ -166,15 +166,9 @@ void HashedObjectStore::bulkWrite()
|
||||
HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
{
|
||||
|
||||
HashedObject::pointer obj;
|
||||
{
|
||||
obj = mCache.fetch(hash);
|
||||
if (obj)
|
||||
{
|
||||
// cLog(lsTRACE) << "HOS: " << hash << " fetch: incache";
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
HashedObject::pointer obj = mCache.fetch(hash);
|
||||
if (obj)
|
||||
return obj;
|
||||
|
||||
if (mNegativeCache.isPresent(hash))
|
||||
return obj;
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
|
||||
KeyCache(const std::string& name, int size = 0, int age = 120) : mName(name), mTargetSize(size), mTargetAge(age)
|
||||
{
|
||||
assert((mTargetSize >= 0) && (mTargetAge > 2));
|
||||
assert((size >= 0) && (age > 2));
|
||||
}
|
||||
|
||||
void getSize()
|
||||
|
||||
@@ -113,14 +113,31 @@ Ledger::Ledger(const std::string& rawLedger, bool hasPrefix) :
|
||||
zeroFees();
|
||||
}
|
||||
|
||||
void Ledger::setImmutable()
|
||||
{
|
||||
if (!mImmutable)
|
||||
{
|
||||
updateHash();
|
||||
mImmutable = true;
|
||||
if (mTransactionMap)
|
||||
mTransactionMap->setImmutable();
|
||||
if (mAccountStateMap)
|
||||
mAccountStateMap->setImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
void Ledger::updateHash()
|
||||
{
|
||||
if (!mImmutable)
|
||||
{
|
||||
if (mTransactionMap) mTransHash = mTransactionMap->getHash();
|
||||
else mTransHash.zero();
|
||||
if (mAccountStateMap) mAccountHash = mAccountStateMap->getHash();
|
||||
else mAccountHash.zero();
|
||||
if (mTransactionMap)
|
||||
mTransHash = mTransactionMap->getHash();
|
||||
else
|
||||
mTransHash.zero();
|
||||
if (mAccountStateMap)
|
||||
mAccountHash = mAccountStateMap->getHash();
|
||||
else
|
||||
mAccountHash.zero();
|
||||
}
|
||||
|
||||
Serializer s(118);
|
||||
@@ -170,18 +187,17 @@ void Ledger::setAccepted(uint32 closeTime, int closeResolution, bool correctClos
|
||||
mCloseTime = correctCloseTime ? (closeTime - (closeTime % closeResolution)) : closeTime;
|
||||
mCloseResolution = closeResolution;
|
||||
mCloseFlags = correctCloseTime ? 0 : sLCF_NoConsensusTime;
|
||||
updateHash();
|
||||
mAccepted = true;
|
||||
mImmutable = true;
|
||||
setImmutable();
|
||||
}
|
||||
|
||||
void Ledger::setAccepted()
|
||||
{ // used when we acquired the ledger
|
||||
// FIXME assert(mClosed && (mCloseTime != 0) && (mCloseResolution != 0));
|
||||
mCloseTime -= mCloseTime % mCloseResolution;
|
||||
updateHash();
|
||||
if ((mCloseFlags & sLCF_NoConsensusTime) == 0)
|
||||
mCloseTime -= mCloseTime % mCloseResolution;
|
||||
mAccepted = true;
|
||||
mImmutable = true;
|
||||
setImmutable();
|
||||
}
|
||||
|
||||
AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID)
|
||||
@@ -504,10 +520,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
|
||||
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
|
||||
|
||||
if (!db->executeSQL(sql) || !db->startIterRows())
|
||||
{
|
||||
cLog(lsDEBUG) << "No ledger for query: " << sql;
|
||||
return Ledger::pointer();
|
||||
}
|
||||
|
||||
db->getStr("LedgerHash", hash);
|
||||
ledgerHash.SetHex(hash, true);
|
||||
@@ -530,6 +543,8 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
|
||||
Ledger::pointer ret = boost::make_shared<Ledger>(prevHash, transHash, accountHash, totCoins,
|
||||
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq);
|
||||
ret->setClosed();
|
||||
if (theApp->getOPs().haveLedger(ledgerSeq))
|
||||
ret->setAccepted();
|
||||
if (ret->getHash() != ledgerHash)
|
||||
{
|
||||
if (sLog(lsERROR))
|
||||
@@ -550,7 +565,7 @@ uint256 Ledger::getHashByIndex(uint32 ledgerIndex)
|
||||
{
|
||||
uint256 ret;
|
||||
|
||||
std::string sql="SELECT LedgerHash FROM Ledgers WHERE LedgerSeq='";
|
||||
std::string sql="SELECT LedgerHash FROM Ledgers INDEXED BY SeqLedger WHERE LedgerSeq='";
|
||||
sql.append(boost::lexical_cast<std::string>(ledgerIndex));
|
||||
sql.append("';");
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
void setClosed() { mClosed = true; }
|
||||
void setAccepted(uint32 closeTime, int closeResolution, bool correctCloseTime);
|
||||
void setAccepted();
|
||||
void setImmutable() { updateHash(); mImmutable = true; }
|
||||
void setImmutable();
|
||||
bool isClosed() { return mClosed; }
|
||||
bool isAccepted() { return mAccepted; }
|
||||
bool isImmutable() { return mImmutable; }
|
||||
|
||||
@@ -160,6 +160,7 @@ bool LedgerAcquire::tryLocal()
|
||||
{
|
||||
cLog(lsDEBUG) << "Had everything locally";
|
||||
mComplete = true;
|
||||
mLedger->setClosed();
|
||||
}
|
||||
|
||||
return mComplete;
|
||||
@@ -244,11 +245,9 @@ void LedgerAcquire::done()
|
||||
|
||||
if (isComplete() && !isFailed() && mLedger)
|
||||
{
|
||||
mLedger->setClosed();
|
||||
if (mAccept)
|
||||
{
|
||||
mLedger->setClosed();
|
||||
mLedger->setAccepted();
|
||||
}
|
||||
theApp->getLedgerMaster().storeLedger(mLedger);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -30,7 +30,7 @@ void LedgerHistory::addLedger(Ledger::pointer ledger)
|
||||
|
||||
void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus)
|
||||
{
|
||||
assert(ledger && ledger->isAccepted());
|
||||
assert(ledger && ledger->isAccepted() && ledger->isImmutable());
|
||||
uint256 h(ledger->getHash());
|
||||
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
|
||||
mLedgersByHash.canonicalize(h, ledger, true);
|
||||
@@ -70,6 +70,7 @@ Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index)
|
||||
assert(ret->getLedgerSeq() == index);
|
||||
|
||||
sl.lock();
|
||||
assert(ret->isImmutable());
|
||||
mLedgersByHash.canonicalize(ret->getHash(), ret);
|
||||
mLedgersByIndex[ret->getLedgerSeq()] = ret->getHash();
|
||||
return (ret->getLedgerSeq() == index) ? ret : Ledger::pointer();
|
||||
@@ -80,13 +81,15 @@ Ledger::pointer LedgerHistory::getLedgerByHash(const uint256& hash)
|
||||
Ledger::pointer ret = mLedgersByHash.fetch(hash);
|
||||
if (ret)
|
||||
{
|
||||
assert(ret->getHash() == hash);
|
||||
assert(ret->isImmutable());
|
||||
assert(ret->getHash() == hash); // FIXME: We seem to be getting these
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = Ledger::loadByHash(hash);
|
||||
if (!ret)
|
||||
return ret;
|
||||
assert(ret->isImmutable());
|
||||
assert(ret->getHash() == hash);
|
||||
mLedgersByHash.canonicalize(ret->getHash(), ret);
|
||||
assert(ret->getHash() == hash);
|
||||
|
||||
@@ -133,6 +133,12 @@ bool LedgerMaster::haveLedgerRange(uint32 from, uint32 to)
|
||||
return (prevMissing == RangeSet::RangeSetAbsent) || (prevMissing < from);
|
||||
}
|
||||
|
||||
bool LedgerMaster::haveLedger(uint32 seq)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return mCompleteLedgers.hasValue(seq);
|
||||
}
|
||||
|
||||
void LedgerMaster::asyncAccept(Ledger::pointer ledger)
|
||||
{
|
||||
uint32 seq = ledger->getLedgerSeq();
|
||||
|
||||
@@ -126,6 +126,7 @@ public:
|
||||
void fixMismatch(Ledger::ref ledger);
|
||||
|
||||
bool haveLedgerRange(uint32 from, uint32 to);
|
||||
bool haveLedger(uint32 seq);
|
||||
|
||||
void resumeAcquiring();
|
||||
|
||||
|
||||
@@ -113,6 +113,11 @@ bool NetworkOPs::haveLedgerRange(uint32 from, uint32 to)
|
||||
return mLedgerMaster->haveLedgerRange(from, to);
|
||||
}
|
||||
|
||||
bool NetworkOPs::haveLedger(uint32 seq)
|
||||
{
|
||||
return mLedgerMaster->haveLedger(seq);
|
||||
}
|
||||
|
||||
bool NetworkOPs::addWantedHash(const uint256& h)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mWantedHashLock);
|
||||
|
||||
@@ -150,6 +150,7 @@ public:
|
||||
|
||||
// Do we have this inclusive range of ledgers in our database
|
||||
bool haveLedgerRange(uint32 from, uint32 to);
|
||||
bool haveLedger(uint32 seq);
|
||||
|
||||
SerializedValidation::ref getLastValidation() { return mLastValidation; }
|
||||
void setLastValidation(SerializedValidation::ref v) { mLastValidation = v; }
|
||||
|
||||
@@ -686,7 +686,7 @@ void Peer::recvHello(ripple::TMHello& packet)
|
||||
<< "Peer speaks version " <<
|
||||
(packet.protoversion() >> 16) << "." << (packet.protoversion() & 0xFF);
|
||||
mHello = packet;
|
||||
if (theApp->getUNL().nodeInCluster(mNodePublic))
|
||||
if (theApp->getUNL().nodeInCluster(mNodePublic, mNodeName))
|
||||
{
|
||||
mCluster = true;
|
||||
mLoad.setPrivileged();
|
||||
@@ -1785,7 +1785,11 @@ Json::Value Peer::getJson()
|
||||
if (mInbound)
|
||||
ret["inbound"] = true;
|
||||
if (mCluster)
|
||||
{
|
||||
ret["cluster"] = true;
|
||||
if (!mNodeName.empty())
|
||||
ret["name"] = mNodeName;
|
||||
}
|
||||
if (mHello.has_fullversion())
|
||||
ret["version"] = mHello.fullversion();
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ private:
|
||||
bool mActive;
|
||||
bool mCluster; // Node in our cluster
|
||||
RippleAddress mNodePublic; // Node public key of peer.
|
||||
std::string mNodeName;
|
||||
ipPort mIpPort;
|
||||
ipPort mIpPortConnect;
|
||||
uint256 mCookieHash;
|
||||
|
||||
@@ -296,6 +296,11 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit)
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
if (jvRequest.isMember("debug_signing")) {
|
||||
jvResult["tx_unsigned"] = strHex(stpTrans->getSerializer().peekData());
|
||||
jvResult["tx_signing_hash"] = stpTrans->getSigningHash().ToString();
|
||||
}
|
||||
|
||||
// FIXME: For performance, transactions should not be signed in this code path.
|
||||
stpTrans->sign(naAccountPrivate);
|
||||
|
||||
|
||||
@@ -387,6 +387,9 @@ public:
|
||||
static bool issuerFromString(uint160& uDstIssuer, const std::string& sIssuer);
|
||||
|
||||
Json::Value getJson(int) const;
|
||||
|
||||
STAmount getRound() const;
|
||||
void roundSelf();
|
||||
};
|
||||
|
||||
extern STAmount saZero;
|
||||
|
||||
@@ -31,30 +31,39 @@ public:
|
||||
typedef boost::weak_ptr<data_type> weak_data_ptr;
|
||||
typedef boost::shared_ptr<data_type> data_ptr;
|
||||
|
||||
typedef bool (*visitor_func)(const c_Key&, c_Data&);
|
||||
|
||||
protected:
|
||||
|
||||
typedef std::pair<time_t, data_ptr> cache_entry;
|
||||
class cache_entry
|
||||
{
|
||||
public:
|
||||
time_t last_use;
|
||||
data_ptr ptr;
|
||||
weak_data_ptr weak_ptr;
|
||||
|
||||
cache_entry(time_t l, const data_ptr& d) : last_use(l), ptr(d), weak_ptr(d) { ; }
|
||||
bool isCached() { return !!ptr; }
|
||||
bool isExpired() { return weak_ptr.expired(); }
|
||||
data_ptr lock() { return weak_ptr.lock(); }
|
||||
void touch() { last_use = time(NULL); }
|
||||
};
|
||||
|
||||
typedef std::pair<key_type, cache_entry> cache_pair;
|
||||
typedef boost::unordered_map<key_type, cache_entry> cache_type;
|
||||
typedef typename cache_type::iterator cache_iterator;
|
||||
typedef boost::unordered_map<key_type, weak_data_ptr> map_type;
|
||||
typedef typename map_type::iterator map_iterator;
|
||||
|
||||
mutable boost::recursive_mutex mLock;
|
||||
|
||||
std::string mName; // Used for logging
|
||||
unsigned int mTargetSize; // Desired number of cache entries (0 = ignore)
|
||||
int mTargetAge; // Desired maximum cache age
|
||||
int mCacheCount; // Number of items cached
|
||||
|
||||
cache_type mCache; // Hold strong reference to recent objects
|
||||
map_type mMap; // Track stored objects
|
||||
time_t mLastSweep;
|
||||
|
||||
public:
|
||||
TaggedCache(const char *name, int size, int age)
|
||||
: mName(name), mTargetSize(size), mTargetAge(age), mLastSweep(time(NULL)) { ; }
|
||||
: mName(name), mTargetSize(size), mTargetAge(age), mCacheCount(0), mLastSweep(time(NULL)) { ; }
|
||||
|
||||
int getTargetSize() const;
|
||||
int getTargetAge() const;
|
||||
@@ -66,8 +75,6 @@ public:
|
||||
void setTargetSize(int size);
|
||||
void setTargetAge(int age);
|
||||
void sweep();
|
||||
void visitAll(visitor_func); // Visits all tracked objects, removes selected objects
|
||||
void visitCached(visitor_func); // Visits all cached objects, uncaches selected objects
|
||||
|
||||
bool touch(const key_type& key);
|
||||
bool del(const key_type& key, bool valid);
|
||||
@@ -108,13 +115,13 @@ template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::setTa
|
||||
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getCacheSize()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return mCache.size();
|
||||
return mCacheCount;
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getTrackSize()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return mMap.size();
|
||||
return mCache.size();
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep()
|
||||
@@ -123,133 +130,107 @@ template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep
|
||||
|
||||
time_t mLastSweep = time(NULL);
|
||||
time_t target = mLastSweep - mTargetAge;
|
||||
int cacheRemovals = 0, mapRemovals = 0, cc = 0;
|
||||
|
||||
// Pass 1, remove old objects from cache
|
||||
int cacheRemovals = 0;
|
||||
if ((mTargetSize == 0) || (mCache.size() > mTargetSize))
|
||||
if ((mTargetSize != 0) && (mCache.size() > mTargetSize))
|
||||
{
|
||||
if (mTargetSize != 0)
|
||||
{
|
||||
target = mLastSweep - (mTargetAge * mTargetSize / mCache.size());
|
||||
if (target > (mLastSweep - 2))
|
||||
target = mLastSweep - 2;
|
||||
target = mLastSweep - (mTargetAge * mTargetSize / mCache.size());
|
||||
if (target > (mLastSweep - 2))
|
||||
target = mLastSweep - 2;
|
||||
Log(lsINFO, TaggedCachePartition) << mName << " is growing fast " <<
|
||||
mCache.size() << " of " << mTargetSize <<
|
||||
" aging at " << (mLastSweep - target) << " of " << mTargetAge;
|
||||
}
|
||||
|
||||
Log(lsINFO, TaggedCachePartition) << mName << " is growing fast " <<
|
||||
mCache.size() << " of " << mTargetSize <<
|
||||
" aging at " << (mLastSweep - target) << " of " << mTargetAge;
|
||||
}
|
||||
else
|
||||
target = mLastSweep - mTargetAge;
|
||||
|
||||
cache_iterator cit = mCache.begin();
|
||||
while (cit != mCache.end())
|
||||
{
|
||||
if (cit->second.first < target)
|
||||
cache_iterator cit = mCache.begin();
|
||||
while (cit != mCache.end())
|
||||
{
|
||||
if (!cit->second.ptr)
|
||||
{ // weak
|
||||
if (cit->second.weak_ptr.expired())
|
||||
{
|
||||
++cacheRemovals;
|
||||
++mapRemovals;
|
||||
mCache.erase(cit++);
|
||||
}
|
||||
else
|
||||
++cit;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2, remove dead objects from map
|
||||
int mapRemovals = 0;
|
||||
map_iterator mit = mMap.begin();
|
||||
while (mit != mMap.end())
|
||||
{
|
||||
if (mit->second.expired())
|
||||
{
|
||||
++mapRemovals;
|
||||
mMap.erase(mit++);
|
||||
}
|
||||
else
|
||||
++mit;
|
||||
{ // strong
|
||||
if (cit->second.last_use < target)
|
||||
{ // expired
|
||||
--mCacheCount;
|
||||
++cacheRemovals;
|
||||
cit->second.ptr.reset();
|
||||
if (cit->second.weak_ptr.expired())
|
||||
{
|
||||
++mapRemovals;
|
||||
mCache.erase(cit++);
|
||||
}
|
||||
else
|
||||
++cit;
|
||||
}
|
||||
else
|
||||
{
|
||||
++cc;
|
||||
++cit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(cc == mCacheCount);
|
||||
if (TaggedCachePartition.doLog(lsTRACE) && (mapRemovals || cacheRemovals))
|
||||
Log(lsTRACE, TaggedCachePartition) << mName << ": cache = " << mCache.size() << "-" << cacheRemovals <<
|
||||
", map = " << mMap.size() << "-" << mapRemovals;
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::visitAll(visitor_func func)
|
||||
{ // Visits all tracked objects, removes selected objects
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
map_iterator mit = mMap.begin();
|
||||
while (mit != mMap.end())
|
||||
{
|
||||
data_ptr cachedData = mit->second.lock();
|
||||
if (!cachedData)
|
||||
mMap.erase(mit++); // dead reference found
|
||||
else if (func(mit->first, mit->second))
|
||||
{
|
||||
mCache.erase(mit->first);
|
||||
mMap.erase(mit++);
|
||||
}
|
||||
else
|
||||
++mit;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::visitCached(visitor_func func)
|
||||
{ // Visits all cached objects, uncaches selected objects
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
cache_iterator cit = mCache.begin();
|
||||
while (cit != mCache.end())
|
||||
{
|
||||
if (func(cit->first, cit->second.second))
|
||||
mCache.erase(cit++);
|
||||
else
|
||||
++cit;
|
||||
}
|
||||
", map-=" << mapRemovals;
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> bool TaggedCache<c_Key, c_Data>::touch(const key_type& key)
|
||||
{ // If present, make current in cache
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
// Is the object in the map?
|
||||
map_iterator mit = mMap.find(key);
|
||||
if (mit == mMap.end())
|
||||
return false;
|
||||
if (mit->second.expired())
|
||||
{ // in map, but expired
|
||||
mMap.erase(mit);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the object in the cache?
|
||||
cache_iterator cit = mCache.find(key);
|
||||
if (cit != mCache.end())
|
||||
{ // in both map and cache
|
||||
cit->second.first = time(NULL);
|
||||
if (cit == mCache.end()) // Don't have the object
|
||||
return false;
|
||||
cache_entry& entry = cit->second;
|
||||
|
||||
if (entry.isCached())
|
||||
{
|
||||
entry.touch();
|
||||
return true;
|
||||
}
|
||||
|
||||
// In map but not cache, put in cache
|
||||
mCache.insert(cache_pair(key, cache_entry(time(NULL), data_ptr(cit->second.second))));
|
||||
return true;
|
||||
entry.ptr = entry.lock();
|
||||
if (entry.isCached())
|
||||
{ // We just put the object back in cache
|
||||
++mCacheCount;
|
||||
entry.touch();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Object fell out
|
||||
mCache.erase(cit);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> bool TaggedCache<c_Key, c_Data>::del(const key_type& key, bool valid)
|
||||
{ // Remove from cache, if !valid, remove from map too. Returns true if removed from cache
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
if (!valid)
|
||||
{ // remove from map too
|
||||
map_iterator mit = mMap.find(key);
|
||||
if (mit == mMap.end()) // not in map, cannot be in cache
|
||||
return false;
|
||||
mMap.erase(mit);
|
||||
}
|
||||
|
||||
cache_iterator cit = mCache.find(key);
|
||||
if (cit == mCache.end())
|
||||
return false;
|
||||
mCache.erase(cit);
|
||||
cache_entry& entry = cit->second;
|
||||
|
||||
bool ret = false;
|
||||
if (entry.isCached())
|
||||
{
|
||||
--mCacheCount;
|
||||
entry.ptr.reset();
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!valid || entry.isExpired())
|
||||
mCache.erase(cit);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -259,40 +240,50 @@ bool TaggedCache<c_Key, c_Data>::canonicalize(const key_type& key, boost::shared
|
||||
// Return values: true=we had the data already
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
map_iterator mit = mMap.find(key);
|
||||
if (mit == mMap.end())
|
||||
{ // not in map
|
||||
cache_iterator cit = mCache.find(key);
|
||||
if (cit == mCache.end())
|
||||
{
|
||||
mCache.insert(cache_pair(key, cache_entry(time(NULL), data)));
|
||||
mMap.insert(std::make_pair(key, data));
|
||||
++mCacheCount;
|
||||
return false;
|
||||
}
|
||||
cache_entry& entry = cit->second;
|
||||
entry.touch();
|
||||
|
||||
data_ptr cachedData = mit->second.lock();
|
||||
if (!cachedData)
|
||||
{ // in map, but expired. Update in map, insert in cache
|
||||
mit->second = data;
|
||||
mCache.insert(cache_pair(key, cache_entry(time(NULL), data)));
|
||||
if (entry.isCached())
|
||||
{
|
||||
if (replace)
|
||||
{
|
||||
entry.ptr = data;
|
||||
entry.weak_ptr = data;
|
||||
}
|
||||
else
|
||||
data = entry.ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// in map and cache, canonicalize
|
||||
if (replace)
|
||||
mit->second = data;
|
||||
else
|
||||
data = cachedData;
|
||||
|
||||
// Valid in map, is it in cache?
|
||||
cache_iterator cit = mCache.find(key);
|
||||
if (cit != mCache.end())
|
||||
data_ptr cachedData = entry.lock();
|
||||
if (cachedData)
|
||||
{
|
||||
cit->second.first = time(NULL); // Yes, refesh
|
||||
if (replace)
|
||||
cit->second.second = data;
|
||||
{
|
||||
entry.ptr = data;
|
||||
entry.weak_ptr = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.ptr = cachedData;
|
||||
data = cachedData;
|
||||
}
|
||||
++mCacheCount;
|
||||
return true;
|
||||
}
|
||||
else // no, add to cache
|
||||
mCache.insert(cache_pair(key, cache_entry(time(NULL), data)));
|
||||
|
||||
return true;
|
||||
entry.ptr = data;
|
||||
entry.weak_ptr = data;
|
||||
++mCacheCount;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data>
|
||||
@@ -300,29 +291,23 @@ boost::shared_ptr<c_Data> TaggedCache<c_Key, c_Data>::fetch(const key_type& key)
|
||||
{ // fetch us a shared pointer to the stored data object
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
// Is it in the cache?
|
||||
cache_iterator cit = mCache.find(key);
|
||||
if (cit != mCache.end())
|
||||
if (cit == mCache.end())
|
||||
return data_ptr();
|
||||
cache_entry& entry = cit->second;
|
||||
entry.touch();
|
||||
|
||||
if (entry.isCached())
|
||||
return entry.ptr;
|
||||
|
||||
entry.ptr = entry.lock();
|
||||
if (entry.isCached())
|
||||
{
|
||||
cit->second.first = time(NULL); // Yes, refresh
|
||||
return cit->second.second;
|
||||
++mCacheCount;
|
||||
return entry.ptr;
|
||||
}
|
||||
|
||||
// Is it in the map?
|
||||
map_iterator mit = mMap.find(key);
|
||||
if (mit == mMap.end())
|
||||
return data_ptr(); // No, we're done
|
||||
|
||||
data_ptr cachedData = mit->second.lock();
|
||||
if (!cachedData)
|
||||
{ // in map, but expired. Sorry, we don't have it
|
||||
mMap.erase(mit);
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
// Put it back in the cache
|
||||
mCache.insert(cache_pair(key, cache_entry(time(NULL), cachedData)));
|
||||
return cachedData;
|
||||
mCache.erase(cit);
|
||||
return data_ptr();
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data>
|
||||
|
||||
@@ -64,15 +64,6 @@ void UniqueNodeList::start()
|
||||
// Load information about when we last updated.
|
||||
bool UniqueNodeList::miscLoad()
|
||||
{
|
||||
BOOST_FOREACH(const std::string& node, theConfig.CLUSTER_NODES)
|
||||
{
|
||||
RippleAddress a = RippleAddress::createNodePublic(node);
|
||||
if (a.isValid())
|
||||
sClusterNodes.insert(a);
|
||||
else
|
||||
cLog(lsWARNING) << "Entry in cluster list invalid: '" << node << "'";
|
||||
}
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database *db=theApp->getWalletDB()->getDB();
|
||||
|
||||
@@ -105,13 +96,18 @@ bool UniqueNodeList::miscSave()
|
||||
|
||||
void UniqueNodeList::trustedLoad()
|
||||
{
|
||||
BOOST_FOREACH(const std::string& node, theConfig.CLUSTER_NODES)
|
||||
boost::regex rNode("\\`\\s*(\\S+)[\\s]*(.*)\\'");
|
||||
BOOST_FOREACH(const std::string& c, theConfig.CLUSTER_NODES)
|
||||
{
|
||||
RippleAddress a = RippleAddress::createNodePublic(node);
|
||||
if (a.isValid())
|
||||
sClusterNodes.insert(a);
|
||||
boost::smatch match;
|
||||
if (boost::regex_match(c, match, rNode))
|
||||
{
|
||||
RippleAddress a = RippleAddress::createNodePublic(match[1]);
|
||||
if (a.isValid())
|
||||
sClusterNodes.insert(std::make_pair(a, match[2]));
|
||||
}
|
||||
else
|
||||
cLog(lsWARNING) << "Entry in cluster list invalid: '" << node << "'";
|
||||
cLog(lsWARNING) << "Entry in cluster list invalid: '" << c << "'";
|
||||
}
|
||||
|
||||
Database* db=theApp->getWalletDB()->getDB();
|
||||
@@ -1731,7 +1727,17 @@ bool UniqueNodeList::nodeInUNL(const RippleAddress& naNodePublic)
|
||||
bool UniqueNodeList::nodeInCluster(const RippleAddress& naNodePublic)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mUNLLock);
|
||||
return sClusterNodes.count(naNodePublic) != 0;
|
||||
return sClusterNodes.end() != sClusterNodes.find(naNodePublic);
|
||||
}
|
||||
|
||||
bool UniqueNodeList::nodeInCluster(const RippleAddress& naNodePublic, std::string& name)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mUNLLock);
|
||||
std::map<RippleAddress, std::string>::iterator it = sClusterNodes.find(naNodePublic);
|
||||
if (it == sClusterNodes.end())
|
||||
return false;
|
||||
name = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
std::vector<int> viReferrals;
|
||||
} scoreNode;
|
||||
|
||||
std::set<RippleAddress> sClusterNodes;
|
||||
std::map<RippleAddress, std::string> sClusterNodes;
|
||||
|
||||
typedef boost::unordered_map<std::string,int> strIndex;
|
||||
typedef std::pair<std::string,int> ipPort;
|
||||
@@ -155,6 +155,7 @@ public:
|
||||
|
||||
bool nodeInUNL(const RippleAddress& naNodePublic);
|
||||
bool nodeInCluster(const RippleAddress& naNodePublic);
|
||||
bool nodeInCluster(const RippleAddress& naNodePublic, std::string& name);
|
||||
|
||||
void nodeBootstrap();
|
||||
bool nodeLoad(boost::filesystem::path pConfig);
|
||||
|
||||
@@ -300,8 +300,8 @@ void ValidationCollection::condWrite()
|
||||
void ValidationCollection::doWrite()
|
||||
{
|
||||
LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK));
|
||||
static boost::format insVal("INSERT INTO LedgerValidations "
|
||||
"(LedgerHash,NodePubKey,Flags,SignTime,Signature) VALUES ('%s','%s','%u','%u',%s);");
|
||||
static boost::format insVal("INSERT INTO Validations "
|
||||
"(LedgerHash,NodePubKey,SignTime,RawData) VALUES ('%s','%s','%u',%s);");
|
||||
|
||||
boost::mutex::scoped_lock sl(mValidationLock);
|
||||
assert(mWriting);
|
||||
@@ -314,11 +314,16 @@ void ValidationCollection::doWrite()
|
||||
Database *db = theApp->getLedgerDB()->getDB();
|
||||
ScopedLock dbl(theApp->getLedgerDB()->getDBLock());
|
||||
|
||||
Serializer s(1024);
|
||||
db->executeSQL("BEGIN TRANSACTION;");
|
||||
BOOST_FOREACH(const SerializedValidation::pointer& it, vector)
|
||||
{
|
||||
s.erase();
|
||||
it->add(s);
|
||||
db->executeSQL(boost::str(insVal % it->getLedgerHash().GetHex()
|
||||
% it->getSignerPublic().humanNodePublic() % it->getFlags() % it->getSignTime()
|
||||
% sqlEscape(it->getSignature())));
|
||||
% it->getSignerPublic().humanNodePublic() % it->getSignTime()
|
||||
% sqlEscape(s.peekData())));
|
||||
}
|
||||
db->executeSQL("END TRANSACTION;");
|
||||
}
|
||||
sl.lock();
|
||||
|
||||
3
src/js/README.md
Normal file
3
src/js/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Ripple JavaScript library
|
||||
|
||||
This library lets you connect to a ripple server via websockets.
|
||||
438
src/js/amount.js
438
src/js/amount.js
@@ -3,27 +3,18 @@
|
||||
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var bn = sjcl.bn;
|
||||
var utils = require('./utils.js');
|
||||
var jsbn = require('./jsbn.js');
|
||||
var utils = require('./utils');
|
||||
var jsbn = require('./jsbn');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
var nbi = jsbn.nbi;
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
|
||||
var alphabets = {
|
||||
'ripple' : "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
|
||||
'tipple' : "RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz",
|
||||
'bitcoin' : "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
};
|
||||
var UInt160 = require('./uint160').UInt160,
|
||||
Seed = require('./seed').Seed,
|
||||
Currency = require('./currency').Currency;
|
||||
|
||||
var consts = exports.consts = {
|
||||
'address_xns' : "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
|
||||
'address_one' : "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
'currency_xns' : 0,
|
||||
'currency_one' : 1,
|
||||
'uint160_xns' : utils.hexToString("0000000000000000000000000000000000000000"),
|
||||
'uint160_one' : utils.hexToString("0000000000000000000000000000000000000001"),
|
||||
'hex_xns' : "0000000000000000000000000000000000000000",
|
||||
'hex_one' : "0000000000000000000000000000000000000001",
|
||||
'xns_precision' : 6,
|
||||
|
||||
// BigInteger values prefixed with bi_.
|
||||
@@ -42,421 +33,8 @@ var consts = exports.consts = {
|
||||
|
||||
'cMinOffset' : -96,
|
||||
'cMaxOffset' : 80,
|
||||
|
||||
'VER_NONE' : 1,
|
||||
'VER_NODE_PUBLIC' : 28,
|
||||
'VER_NODE_PRIVATE' : 32,
|
||||
'VER_ACCOUNT_ID' : 0,
|
||||
'VER_ACCOUNT_PUBLIC' : 35,
|
||||
'VER_ACCOUNT_PRIVATE' : 34,
|
||||
'VER_FAMILY_GENERATOR' : 41,
|
||||
'VER_FAMILY_SEED' : 33,
|
||||
};
|
||||
|
||||
// --> input: big-endian array of bytes.
|
||||
// <-- string at least as long as input.
|
||||
var encode_base = function (input, alphabet) {
|
||||
var alphabet = alphabets[alphabet || 'ripple'];
|
||||
var bi_base = new BigInteger(String(alphabet.length));
|
||||
var bi_q = nbi();
|
||||
var bi_r = nbi();
|
||||
var bi_value = new BigInteger(input);
|
||||
var buffer = [];
|
||||
|
||||
while (bi_value.compareTo(BigInteger.ZERO) > 0)
|
||||
{
|
||||
bi_value.divRemTo(bi_base, bi_q, bi_r);
|
||||
bi_q.copyTo(bi_value);
|
||||
|
||||
buffer.push(alphabet[bi_r.intValue()]);
|
||||
}
|
||||
|
||||
var i;
|
||||
|
||||
for (i = 0; i != input.length && !input[i]; i += 1) {
|
||||
buffer.push(alphabet[0]);
|
||||
}
|
||||
|
||||
return buffer.reverse().join("");
|
||||
};
|
||||
|
||||
// --> input: String
|
||||
// <-- array of bytes or undefined.
|
||||
var decode_base = function (input, alphabet) {
|
||||
var alphabet = alphabets[alphabet || 'ripple'];
|
||||
var bi_base = new BigInteger(String(alphabet.length));
|
||||
var bi_value = nbi();
|
||||
var i;
|
||||
|
||||
for (i = 0; i != input.length && input[i] === alphabet[0]; i += 1)
|
||||
;
|
||||
|
||||
for (; i != input.length; i += 1) {
|
||||
var v = alphabet.indexOf(input[i]);
|
||||
|
||||
if (v < 0)
|
||||
return undefined;
|
||||
|
||||
var r = nbi();
|
||||
|
||||
r.fromInt(v);
|
||||
|
||||
bi_value = bi_value.multiply(bi_base).add(r);
|
||||
}
|
||||
|
||||
// toByteArray:
|
||||
// - Returns leading zeros!
|
||||
// - Returns signed bytes!
|
||||
var bytes = bi_value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0});
|
||||
var extra = 0;
|
||||
|
||||
while (extra != bytes.length && !bytes[extra])
|
||||
extra += 1;
|
||||
|
||||
if (extra)
|
||||
bytes = bytes.slice(extra);
|
||||
|
||||
var zeros = 0;
|
||||
|
||||
while (zeros !== input.length && input[zeros] === alphabet[0])
|
||||
zeros += 1;
|
||||
|
||||
return [].concat(utils.arraySet(zeros, 0), bytes);
|
||||
};
|
||||
|
||||
var sha256 = function (bytes) {
|
||||
return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
||||
};
|
||||
|
||||
var sha256hash = function (bytes) {
|
||||
return sha256(sha256(bytes));
|
||||
};
|
||||
|
||||
// --> input: Array
|
||||
// <-- String
|
||||
var encode_base_check = function (version, input, alphabet) {
|
||||
var buffer = [].concat(version, input);
|
||||
var check = sha256(sha256(buffer)).slice(0, 4);
|
||||
|
||||
return encode_base([].concat(buffer, check), alphabet);
|
||||
}
|
||||
|
||||
// --> input : String
|
||||
// <-- NaN || BigInteger
|
||||
var decode_base_check = function (version, input, alphabet) {
|
||||
var buffer = decode_base(input, alphabet);
|
||||
|
||||
if (!buffer || buffer[0] !== version || buffer.length < 5)
|
||||
return NaN;
|
||||
|
||||
var computed = sha256hash(buffer.slice(0, -4)).slice(0, 4);
|
||||
var checksum = buffer.slice(-4);
|
||||
var i;
|
||||
|
||||
for (i = 0; i != 4; i += 1)
|
||||
if (computed[i] !== checksum[i])
|
||||
return NaN;
|
||||
|
||||
return new BigInteger(buffer.slice(1, -4));
|
||||
}
|
||||
|
||||
//
|
||||
// Seed support
|
||||
//
|
||||
|
||||
var Seed = function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
};
|
||||
|
||||
Seed.json_rewrite = function (j) {
|
||||
return Seed.from_json(j).to_json();
|
||||
};
|
||||
|
||||
// Return a new Seed from j.
|
||||
Seed.from_json = function (j) {
|
||||
return 'string' === typeof j
|
||||
? (new Seed()).parse_json(j)
|
||||
: j.clone();
|
||||
};
|
||||
|
||||
Seed.is_valid = function (j) {
|
||||
return Seed.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
Seed.prototype.clone = function () {
|
||||
return this.copyTo(new Seed());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
Seed.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
Seed.prototype.equals = function (d) {
|
||||
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
|
||||
};
|
||||
|
||||
Seed.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
};
|
||||
|
||||
// value = NaN on error.
|
||||
// One day this will support rfc1751 too.
|
||||
Seed.prototype.parse_json = function (j) {
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "s") {
|
||||
this._value = decode_base_check(consts.VER_FAMILY_SEED, j);
|
||||
}
|
||||
else if (16 === j.length) {
|
||||
this._value = new BigInteger(utils.stringToArray(j), 128);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Convert from internal form.
|
||||
Seed.prototype.to_json = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return NaN;
|
||||
|
||||
var bytes = this._value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0});
|
||||
var target = 20;
|
||||
|
||||
// XXX Make sure only trim off leading zeros.
|
||||
var array = bytes.length < target
|
||||
? bytes.length
|
||||
? [].concat(utils.arraySet(target - bytes.length, 0), bytes)
|
||||
: utils.arraySet(target, 0)
|
||||
: bytes.slice(target - bytes.length);
|
||||
var output = encode_base_check(consts.VER_FAMILY_SEED, array);
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
//
|
||||
// UInt160 support
|
||||
//
|
||||
|
||||
var UInt160 = function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
};
|
||||
|
||||
UInt160.json_rewrite = function (j) {
|
||||
return UInt160.from_json(j).to_json();
|
||||
};
|
||||
|
||||
// Return a new UInt160 from j.
|
||||
UInt160.from_generic = function (j) {
|
||||
return 'string' === typeof j
|
||||
? (new UInt160()).parse_generic(j)
|
||||
: j.clone();
|
||||
};
|
||||
|
||||
// Return a new UInt160 from j.
|
||||
UInt160.from_json = function (j) {
|
||||
if ('string' === typeof j) {
|
||||
return (new UInt160()).parse_json(j);
|
||||
} else if (j instanceof UInt160) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return new UInt160();
|
||||
}
|
||||
};
|
||||
|
||||
UInt160.is_valid = function (j) {
|
||||
return UInt160.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
UInt160.prototype.clone = function () {
|
||||
return this.copyTo(new UInt160());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
UInt160.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
UInt160.prototype.equals = function (d) {
|
||||
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
|
||||
};
|
||||
|
||||
UInt160.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
};
|
||||
|
||||
// value = NaN on error.
|
||||
UInt160.prototype.parse_generic = function (j) {
|
||||
// Canonicalize and validate
|
||||
if (exports.config.accounts && j in exports.config.accounts)
|
||||
j = exports.config.accounts[j].account;
|
||||
|
||||
switch (j) {
|
||||
case undefined:
|
||||
case "0":
|
||||
case consts.address_xns:
|
||||
case consts.uint160_xns:
|
||||
case consts.hex_xns:
|
||||
this._value = nbi();
|
||||
break;
|
||||
|
||||
case "1":
|
||||
case consts.address_one:
|
||||
case consts.uint160_one:
|
||||
case consts.hex_one:
|
||||
this._value = new BigInteger([1]);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "r") {
|
||||
this._value = decode_base_check(consts.VER_ACCOUNT_ID, j);
|
||||
}
|
||||
else if (20 === j.length) {
|
||||
this._value = new BigInteger(utils.stringToArray(j), 256);
|
||||
}
|
||||
else if (40 === j.length) {
|
||||
// XXX Check char set!
|
||||
this._value = new BigInteger(j, 16);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// value = NaN on error.
|
||||
UInt160.prototype.parse_json = function (j) {
|
||||
// Canonicalize and validate
|
||||
if (exports.config.accounts && j in exports.config.accounts)
|
||||
j = exports.config.accounts[j].account;
|
||||
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "r") {
|
||||
this._value = decode_base_check(consts.VER_ACCOUNT_ID, j);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Convert from internal form.
|
||||
// XXX Json form should allow 0 and 1, C++ doesn't currently allow it.
|
||||
UInt160.prototype.to_json = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return NaN;
|
||||
|
||||
var bytes = this._value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0});
|
||||
var target = 20;
|
||||
|
||||
// XXX Make sure only trim off leading zeros.
|
||||
var array = bytes.length < target
|
||||
? bytes.length
|
||||
? [].concat(utils.arraySet(target - bytes.length, 0), bytes)
|
||||
: utils.arraySet(target, 0)
|
||||
: bytes.slice(target - bytes.length);
|
||||
var output = encode_base_check(consts.VER_ACCOUNT_ID, array);
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
//
|
||||
// Currency support
|
||||
//
|
||||
|
||||
// XXX Internal form should be UInt160.
|
||||
var Currency = function () {
|
||||
// Internal form: 0 = XRP. 3 letter-code.
|
||||
// XXX Internal should be 0 or hex with three letter annotation when valid.
|
||||
|
||||
// Json form:
|
||||
// '', 'XRP', '0': 0
|
||||
// 3-letter code: ...
|
||||
// XXX Should support hex, C++ doesn't currently allow it.
|
||||
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
// Given "USD" return the json.
|
||||
Currency.json_rewrite = function (j) {
|
||||
return Currency.from_json(j).to_json();
|
||||
};
|
||||
|
||||
Currency.from_json = function (j) {
|
||||
return 'string' === typeof j
|
||||
? (new Currency()).parse_json(j)
|
||||
: j.clone();
|
||||
};
|
||||
|
||||
Currency.is_valid = function (j) {
|
||||
return currency.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
Currency.prototype.clone = function() {
|
||||
return this.copyTo(new Currency());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
Currency.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
Currency.prototype.equals = function (d) {
|
||||
return ('string' !== typeof this._value && isNaN(this._value))
|
||||
|| ('string' !== typeof d._value && isNaN(d._value)) ? false : this._value === d._value;
|
||||
};
|
||||
|
||||
// this._value = NaN on error.
|
||||
Currency.prototype.parse_json = function (j) {
|
||||
if ("" === j || "0" === j || "XRP" === j) {
|
||||
this._value = 0;
|
||||
}
|
||||
else if ('string' != typeof j || 3 !== j.length) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else {
|
||||
this._value = j;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Currency.prototype.is_native = function () {
|
||||
return !isNaN(this._value) && !this._value;
|
||||
};
|
||||
|
||||
Currency.prototype.is_valid = function () {
|
||||
return !isNaN(this._value);
|
||||
};
|
||||
|
||||
Currency.prototype.to_json = function () {
|
||||
return this._value ? this._value : "XRP";
|
||||
};
|
||||
|
||||
Currency.prototype.to_human = function () {
|
||||
return this._value ? this._value : "XRP";
|
||||
};
|
||||
|
||||
//
|
||||
// Amount class in the style of Java's BigInteger class
|
||||
@@ -1305,10 +883,10 @@ Amount.prototype.not_equals_why = function (d) {
|
||||
};
|
||||
|
||||
exports.Amount = Amount;
|
||||
|
||||
// DEPRECATED: Include the corresponding files instead.
|
||||
exports.Currency = Currency;
|
||||
exports.Seed = Seed;
|
||||
exports.UInt160 = UInt160;
|
||||
|
||||
exports.config = {};
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
136
src/js/base.js
Normal file
136
src/js/base.js
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var utils = require('./utils');
|
||||
var jsbn = require('./jsbn');
|
||||
var extend = require('extend');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
var nbi = jsbn.nbi;
|
||||
|
||||
var Base = {};
|
||||
|
||||
var alphabets = Base.alphabets = {
|
||||
'ripple' : "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
|
||||
'tipple' : "RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz",
|
||||
'bitcoin' : "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
};
|
||||
|
||||
extend(Base, {
|
||||
'VER_NONE' : 1,
|
||||
'VER_NODE_PUBLIC' : 28,
|
||||
'VER_NODE_PRIVATE' : 32,
|
||||
'VER_ACCOUNT_ID' : 0,
|
||||
'VER_ACCOUNT_PUBLIC' : 35,
|
||||
'VER_ACCOUNT_PRIVATE' : 34,
|
||||
'VER_FAMILY_GENERATOR' : 41,
|
||||
'VER_FAMILY_SEED' : 33
|
||||
});
|
||||
|
||||
var sha256 = function (bytes) {
|
||||
return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
||||
};
|
||||
|
||||
var sha256hash = function (bytes) {
|
||||
return sha256(sha256(bytes));
|
||||
};
|
||||
|
||||
// --> input: big-endian array of bytes.
|
||||
// <-- string at least as long as input.
|
||||
Base.encode = function (input, alpha) {
|
||||
var alphabet = alphabets[alpha || 'ripple'];
|
||||
var bi_base = new BigInteger(String(alphabet.length));
|
||||
var bi_q = nbi();
|
||||
var bi_r = nbi();
|
||||
var bi_value = new BigInteger(input);
|
||||
var buffer = [];
|
||||
|
||||
while (bi_value.compareTo(BigInteger.ZERO) > 0)
|
||||
{
|
||||
bi_value.divRemTo(bi_base, bi_q, bi_r);
|
||||
bi_q.copyTo(bi_value);
|
||||
|
||||
buffer.push(alphabet[bi_r.intValue()]);
|
||||
}
|
||||
|
||||
var i;
|
||||
|
||||
for (i = 0; i != input.length && !input[i]; i += 1) {
|
||||
buffer.push(alphabet[0]);
|
||||
}
|
||||
|
||||
return buffer.reverse().join("");
|
||||
};
|
||||
|
||||
// --> input: String
|
||||
// <-- array of bytes or undefined.
|
||||
Base.decode = function (input, alpha) {
|
||||
var alphabet = alphabets[alpha || 'ripple'];
|
||||
var bi_base = new BigInteger(String(alphabet.length));
|
||||
var bi_value = nbi();
|
||||
var i;
|
||||
|
||||
for (i = 0; i != input.length && input[i] === alphabet[0]; i += 1)
|
||||
;
|
||||
|
||||
for (; i != input.length; i += 1) {
|
||||
var v = alphabet.indexOf(input[i]);
|
||||
|
||||
if (v < 0)
|
||||
return undefined;
|
||||
|
||||
var r = nbi();
|
||||
|
||||
r.fromInt(v);
|
||||
|
||||
bi_value = bi_value.multiply(bi_base).add(r);
|
||||
}
|
||||
|
||||
// toByteArray:
|
||||
// - Returns leading zeros!
|
||||
// - Returns signed bytes!
|
||||
var bytes = bi_value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0; });
|
||||
var extra = 0;
|
||||
|
||||
while (extra != bytes.length && !bytes[extra])
|
||||
extra += 1;
|
||||
|
||||
if (extra)
|
||||
bytes = bytes.slice(extra);
|
||||
|
||||
var zeros = 0;
|
||||
|
||||
while (zeros !== input.length && input[zeros] === alphabet[0])
|
||||
zeros += 1;
|
||||
|
||||
return [].concat(utils.arraySet(zeros, 0), bytes);
|
||||
};
|
||||
|
||||
// --> input: Array
|
||||
// <-- String
|
||||
Base.encode_check = function (version, input, alphabet) {
|
||||
var buffer = [].concat(version, input);
|
||||
var check = sha256(sha256(buffer)).slice(0, 4);
|
||||
|
||||
return Base.encode([].concat(buffer, check), alphabet);
|
||||
}
|
||||
|
||||
// --> input : String
|
||||
// <-- NaN || BigInteger
|
||||
Base.decode_check = function (version, input, alphabet) {
|
||||
var buffer = Base.decode(input, alphabet);
|
||||
|
||||
if (!buffer || buffer[0] !== version || buffer.length < 5)
|
||||
return NaN;
|
||||
|
||||
var computed = sha256hash(buffer.slice(0, -4)).slice(0, 4);
|
||||
var checksum = buffer.slice(-4);
|
||||
var i;
|
||||
|
||||
for (i = 0; i != 4; i += 1)
|
||||
if (computed[i] !== checksum[i])
|
||||
return NaN;
|
||||
|
||||
return new BigInteger(buffer.slice(1, -4));
|
||||
}
|
||||
|
||||
exports.Base = Base;
|
||||
3
src/js/config.js
Normal file
3
src/js/config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// This object serves as a singleton to store config options
|
||||
|
||||
module.exports = {};
|
||||
81
src/js/currency.js
Normal file
81
src/js/currency.js
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
//
|
||||
// Currency support
|
||||
//
|
||||
|
||||
// XXX Internal form should be UInt160.
|
||||
var Currency = function () {
|
||||
// Internal form: 0 = XRP. 3 letter-code.
|
||||
// XXX Internal should be 0 or hex with three letter annotation when valid.
|
||||
|
||||
// Json form:
|
||||
// '', 'XRP', '0': 0
|
||||
// 3-letter code: ...
|
||||
// XXX Should support hex, C++ doesn't currently allow it.
|
||||
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
// Given "USD" return the json.
|
||||
Currency.json_rewrite = function (j) {
|
||||
return Currency.from_json(j).to_json();
|
||||
};
|
||||
|
||||
Currency.from_json = function (j) {
|
||||
return 'string' === typeof j
|
||||
? (new Currency()).parse_json(j)
|
||||
: j.clone();
|
||||
};
|
||||
|
||||
Currency.is_valid = function (j) {
|
||||
return Currency.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
Currency.prototype.clone = function() {
|
||||
return this.copyTo(new Currency());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
Currency.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
Currency.prototype.equals = function (d) {
|
||||
return ('string' !== typeof this._value && isNaN(this._value))
|
||||
|| ('string' !== typeof d._value && isNaN(d._value)) ? false : this._value === d._value;
|
||||
};
|
||||
|
||||
// this._value = NaN on error.
|
||||
Currency.prototype.parse_json = function (j) {
|
||||
if ("" === j || "0" === j || "XRP" === j) {
|
||||
this._value = 0;
|
||||
}
|
||||
else if ('string' != typeof j || 3 !== j.length) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else {
|
||||
this._value = j;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Currency.prototype.is_native = function () {
|
||||
return !isNaN(this._value) && !this._value;
|
||||
};
|
||||
|
||||
Currency.prototype.is_valid = function () {
|
||||
return !isNaN(this._value);
|
||||
};
|
||||
|
||||
Currency.prototype.to_json = function () {
|
||||
return this._value ? this._value : "XRP";
|
||||
};
|
||||
|
||||
Currency.prototype.to_human = function () {
|
||||
return this._value ? this._value : "XRP";
|
||||
};
|
||||
|
||||
exports.Currency = Currency;
|
||||
@@ -22,6 +22,7 @@ var UInt160 = require('./amount').UInt160;
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
|
||||
// Request events emitted:
|
||||
// 'success' : Request successful.
|
||||
@@ -205,7 +206,7 @@ var Remote = function (opts, trace) {
|
||||
this._testnet = undefined;
|
||||
this._transaction_subs = 0;
|
||||
this.online_target = false;
|
||||
this.online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing'
|
||||
this._online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing'
|
||||
this.state = 'offline'; // 'online', 'offline'
|
||||
this.retry_timer = undefined;
|
||||
this.retry = undefined;
|
||||
@@ -245,7 +246,7 @@ var Remote = function (opts, trace) {
|
||||
this.on('newListener', function (type, listener) {
|
||||
if ('transaction' === type)
|
||||
{
|
||||
if (!self._transaction_subs)
|
||||
if (!self._transaction_subs && 'open' === self._online_state)
|
||||
{
|
||||
self.request_subscribe([ 'transactions' ])
|
||||
.request();
|
||||
@@ -260,7 +261,7 @@ var Remote = function (opts, trace) {
|
||||
{
|
||||
self._transaction_subs -= 1;
|
||||
|
||||
if (!self._transaction_subs)
|
||||
if (!self._transaction_subs && 'open' === self._online_state)
|
||||
{
|
||||
self.request_unsubscribe([ 'transactions' ])
|
||||
.request();
|
||||
@@ -272,12 +273,12 @@ var Remote = function (opts, trace) {
|
||||
Remote.prototype = new EventEmitter;
|
||||
|
||||
Remote.from_config = function (obj, trace) {
|
||||
var serverConfig = 'string' === typeof obj ? exports.config.servers[obj] : obj;
|
||||
var serverConfig = 'string' === typeof obj ? config.servers[obj] : obj;
|
||||
|
||||
var remote = new Remote(serverConfig, trace);
|
||||
|
||||
for (var account in exports.config.accounts) {
|
||||
var accountInfo = exports.config.accounts[account];
|
||||
for (var account in config.accounts) {
|
||||
var accountInfo = config.accounts[account];
|
||||
if ("object" === typeof accountInfo) {
|
||||
if (accountInfo.secret) {
|
||||
// Index by nickname ...
|
||||
@@ -327,12 +328,12 @@ Remote.prototype._set_state = function (state) {
|
||||
|
||||
switch (state) {
|
||||
case 'online':
|
||||
this.online_state = 'open';
|
||||
this._online_state = 'open';
|
||||
this.emit('connected');
|
||||
break;
|
||||
|
||||
case 'offline':
|
||||
this.online_state = 'closed';
|
||||
this._online_state = 'closed';
|
||||
this.emit('disconnected');
|
||||
break;
|
||||
}
|
||||
@@ -353,7 +354,7 @@ Remote.prototype.connect = function (online) {
|
||||
this.online_target = target;
|
||||
|
||||
// If we were in a stable state, go dynamic.
|
||||
switch (this.online_state) {
|
||||
switch (this._online_state) {
|
||||
case 'open':
|
||||
if (!target) this._connect_stop();
|
||||
break;
|
||||
@@ -390,9 +391,9 @@ Remote.prototype._connect_retry = function () {
|
||||
// Do not continue trying to connect.
|
||||
this._set_state('offline');
|
||||
}
|
||||
else if ('connecting' !== this.online_state) {
|
||||
else if ('connecting' !== this._online_state) {
|
||||
// New to connecting state.
|
||||
this.online_state = 'connecting';
|
||||
this._online_state = 'connecting';
|
||||
this.retry = 0;
|
||||
|
||||
this._set_state('offline'); // Report newly offline.
|
||||
@@ -850,6 +851,17 @@ Remote.prototype.request_wallet_accounts = function (seed) {
|
||||
return request;
|
||||
};
|
||||
|
||||
Remote.prototype.request_sign = function (secret, tx_json) {
|
||||
utils.assert(this.trusted); // Don't send secrets.
|
||||
|
||||
var request = new Request(this, 'sign');
|
||||
|
||||
request.message.secret = secret;
|
||||
request.message.tx_json = tx_json;
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
// Submit a transaction.
|
||||
Remote.prototype.submit = function (transaction) {
|
||||
var self = this;
|
||||
@@ -915,7 +927,12 @@ Remote.prototype.submit = function (transaction) {
|
||||
Remote.prototype._server_subscribe = function () {
|
||||
var self = this;
|
||||
|
||||
this.request_subscribe([ 'ledger', 'server' ])
|
||||
var feeds = [ 'ledger', 'server' ];
|
||||
|
||||
if (this._transaction_subs)
|
||||
feeds.push('transactions');
|
||||
|
||||
this.request_subscribe(feeds)
|
||||
.on('success', function (message) {
|
||||
self._stand_alone = !!message.stand_alone;
|
||||
self._testnet = !!message.testnet;
|
||||
@@ -1128,7 +1145,9 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account,
|
||||
request.message.source_account = UInt160.json_rewrite(src_account);
|
||||
request.message.destination_account = UInt160.json_rewrite(dst_account);
|
||||
request.message.destination_amount = Amount.json_rewrite(dst_amount);
|
||||
request.message.source_currencies = source_currencies.map(function (ci) {
|
||||
|
||||
if (source_currencies) {
|
||||
request.message.source_currencies = source_currencies.map(function (ci) {
|
||||
var ci_new = {};
|
||||
|
||||
if ('issuer' in ci)
|
||||
@@ -1139,6 +1158,7 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account,
|
||||
|
||||
return ci_new;
|
||||
});
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
@@ -1186,7 +1206,6 @@ Remote.prototype.transaction = function () {
|
||||
return new Transaction(this);
|
||||
};
|
||||
|
||||
exports.config = {};
|
||||
exports.Remote = Remote;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
129
src/js/seed.js
Normal file
129
src/js/seed.js
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Seed support
|
||||
//
|
||||
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var utils = require('./utils');
|
||||
var jsbn = require('./jsbn');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
|
||||
var Base = require('./base').Base,
|
||||
UInt256 = require('./uint256').UInt256;
|
||||
|
||||
var Seed = function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
};
|
||||
|
||||
Seed.json_rewrite = function (j) {
|
||||
return Seed.from_json(j).to_json();
|
||||
};
|
||||
|
||||
// Return a new Seed from j.
|
||||
Seed.from_json = function (j) {
|
||||
return (j instanceof Seed)
|
||||
? j.clone()
|
||||
: (new Seed()).parse_json(j);
|
||||
};
|
||||
|
||||
Seed.is_valid = function (j) {
|
||||
return Seed.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
Seed.prototype.clone = function () {
|
||||
return this.copyTo(new Seed());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
Seed.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
Seed.prototype.equals = function (d) {
|
||||
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
|
||||
};
|
||||
|
||||
Seed.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
};
|
||||
|
||||
// value = NaN on error.
|
||||
// One day this will support rfc1751 too.
|
||||
Seed.prototype.parse_json = function (j) {
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "s") {
|
||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
||||
}
|
||||
else if (16 === j.length) {
|
||||
this._value = new BigInteger(utils.stringToArray(j), 128);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Convert from internal form.
|
||||
Seed.prototype.to_json = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return NaN;
|
||||
|
||||
var bytes = this._value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0; });
|
||||
var target = 20;
|
||||
|
||||
// XXX Make sure only trim off leading zeros.
|
||||
var array = bytes.length < target
|
||||
? bytes.length
|
||||
? [].concat(utils.arraySet(target - bytes.length, 0), bytes)
|
||||
: utils.arraySet(target, 0)
|
||||
: bytes.slice(target - bytes.length);
|
||||
var output = Base.encode_check(Base.VER_FAMILY_SEED, array);
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
function append_int(a, i) {
|
||||
return [].concat(a, i >> 24, (i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff);
|
||||
}
|
||||
|
||||
function firstHalfOfSHA512(bytes) {
|
||||
return sjcl.bitArray.bitSlice(
|
||||
sjcl.hash.sha512.hash(sjcl.codec.bytes.toBits(bytes)),
|
||||
0, 256
|
||||
);
|
||||
}
|
||||
|
||||
function SHA256_RIPEMD160(bits) {
|
||||
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
|
||||
}
|
||||
|
||||
Seed.prototype.generate_private = function (account_id) {
|
||||
// XXX If account_id is given, should loop over keys until we find the right one
|
||||
|
||||
var seq = 0;
|
||||
|
||||
var private_gen, public_gen, i = 0;
|
||||
do {
|
||||
private_gen = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(this.seed, i)));
|
||||
i++;
|
||||
} while (!sjcl.ecc.curves.c256.r.greaterEquals(private_gen));
|
||||
|
||||
public_gen = sjcl.ecc.curves.c256.G.mult(private_gen);
|
||||
|
||||
var sec;
|
||||
i = 0;
|
||||
do {
|
||||
sec = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(append_int(public_gen.toBytesCompressed(), seq), i)));
|
||||
i++;
|
||||
} while (!sjcl.ecc.curves.c256.r.greaterEquals(sec));
|
||||
|
||||
return UInt256.from_bn(sec);
|
||||
};
|
||||
|
||||
exports.Seed = Seed;
|
||||
144
src/js/serializedobject.js
Normal file
144
src/js/serializedobject.js
Normal file
@@ -0,0 +1,144 @@
|
||||
var binformat = require('./binformat'),
|
||||
sjcl = require('../../build/sjcl'),
|
||||
extend = require('extend'),
|
||||
stypes = require('./serializedtypes');
|
||||
|
||||
var UInt256 = require('./uint256').UInt256;
|
||||
|
||||
var SerializedObject = function () {
|
||||
this.buffer = [];
|
||||
this.pointer = 0;
|
||||
};
|
||||
|
||||
SerializedObject.from_json = function (obj) {
|
||||
var typedef;
|
||||
var so = new SerializedObject();
|
||||
|
||||
// Create a copy of the object so we don't modify it
|
||||
obj = extend({}, obj);
|
||||
|
||||
if ("number" === typeof obj.TransactionType) {
|
||||
obj.TransactionType = SerializedObject.lookup_type_tx(obj.TransactionType);
|
||||
|
||||
if (!obj.TransactionType) {
|
||||
throw new Error("Transaction type ID is invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
if ("string" === typeof obj.TransactionType) {
|
||||
typedef = binformat.tx[obj.TransactionType].slice();
|
||||
|
||||
obj.TransactionType = typedef.shift();
|
||||
} else if ("undefined" !== typeof obj.LedgerEntryType) {
|
||||
// XXX: TODO
|
||||
throw new Error("Ledger entry binary format not yet implemented.");
|
||||
} else throw new Error("Object to be serialized must contain either " +
|
||||
"TransactionType or LedgerEntryType.");
|
||||
|
||||
so.serialize(typedef, obj);
|
||||
|
||||
return so;
|
||||
};
|
||||
|
||||
SerializedObject.prototype.append = function (bytes) {
|
||||
this.buffer = this.buffer.concat(bytes);
|
||||
this.pointer += bytes.length;
|
||||
};
|
||||
|
||||
SerializedObject.prototype.to_bits = function ()
|
||||
{
|
||||
return sjcl.codec.bytes.toBits(this.buffer);
|
||||
};
|
||||
|
||||
SerializedObject.prototype.to_hex = function () {
|
||||
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
};
|
||||
|
||||
SerializedObject.prototype.serialize = function (typedef, obj)
|
||||
{
|
||||
// Ensure canonical order
|
||||
typedef = SerializedObject._sort_typedef(typedef.slice());
|
||||
|
||||
// Serialize fields
|
||||
for (var i = 0, l = typedef.length; i < l; i++) {
|
||||
var spec = typedef[i];
|
||||
this.serialize_field(spec, obj);
|
||||
}
|
||||
};
|
||||
|
||||
SerializedObject.prototype.signing_hash = function (prefix)
|
||||
{
|
||||
var sign_buffer = new SerializedObject();
|
||||
stypes.Int32.serialize(sign_buffer, prefix);
|
||||
sign_buffer.append(this.buffer);
|
||||
return sign_buffer.hash_sha512_half();
|
||||
};
|
||||
|
||||
SerializedObject.prototype.hash_sha512_half = function ()
|
||||
{
|
||||
var bits = sjcl.codec.bytes.toBits(this.buffer),
|
||||
hash = sjcl.bitArray.bitSlice(sjcl.hash.sha512.hash(bits), 0, 256);
|
||||
|
||||
return UInt256.from_hex(sjcl.codec.hex.fromBits(hash));
|
||||
};
|
||||
|
||||
SerializedObject.prototype.serialize_field = function (spec, obj)
|
||||
{
|
||||
spec = spec.slice();
|
||||
|
||||
var name = spec.shift(),
|
||||
presence = spec.shift(),
|
||||
field_id = spec.shift(),
|
||||
Type = spec.shift();
|
||||
|
||||
if ("undefined" !== typeof obj[name]) {
|
||||
console.log(name, Type.id, field_id);
|
||||
this.append(SerializedObject.get_field_header(Type.id, field_id));
|
||||
|
||||
try {
|
||||
Type.serialize(this, obj[name]);
|
||||
} catch (e) {
|
||||
// Add field name to message and rethrow
|
||||
e.message = "Error serializing '"+name+"': "+e.message;
|
||||
throw e;
|
||||
}
|
||||
} else if (presence === binformat.REQUIRED) {
|
||||
throw new Error('Missing required field '+name);
|
||||
}
|
||||
};
|
||||
|
||||
SerializedObject.get_field_header = function (type_id, field_id)
|
||||
{
|
||||
var buffer = [0];
|
||||
if (type_id > 0xf) buffer.push(type_id & 0xff);
|
||||
else buffer[0] += (type_id & 0xf) << 4;
|
||||
|
||||
if (field_id > 0xf) buffer.push(field_id & 0xff);
|
||||
else buffer[0] += field_id & 0xf;
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
function sort_field_compare(a, b) {
|
||||
// Sort by type id first, then by field id
|
||||
return a[3].id !== b[3].id ?
|
||||
a[3].id - b[3].id :
|
||||
a[2] - b[2];
|
||||
};
|
||||
SerializedObject._sort_typedef = function (typedef) {
|
||||
return typedef.sort(sort_field_compare);
|
||||
};
|
||||
|
||||
SerializedObject.lookup_type_tx = function (id) {
|
||||
for (var i in binformat.tx) {
|
||||
if (!binformat.tx.hasOwnProperty(i)) continue;
|
||||
|
||||
if (binformat.tx[i][0] === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.SerializedObject = SerializedObject;
|
||||
@@ -1,10 +1,55 @@
|
||||
var SerializedType = function () {
|
||||
/**
|
||||
* Type definitions for binary format.
|
||||
*
|
||||
* This file should not be included directly. Instead, find the format you're
|
||||
* trying to parse or serialize in binformat.js and pass that to
|
||||
* SerializedObject.parse() or SerializedObject.serialize().
|
||||
*/
|
||||
|
||||
var extend = require('extend'),
|
||||
utils = require('./utils'),
|
||||
sjcl = require('../../build/sjcl');
|
||||
|
||||
var amount = require('./amount'),
|
||||
UInt160 = amount.UInt160,
|
||||
Amount = amount.Amount;
|
||||
|
||||
// Shortcuts
|
||||
var hex = sjcl.codec.hex,
|
||||
bytes = sjcl.codec.bytes;
|
||||
|
||||
var SerializedType = function (methods) {
|
||||
extend(this, methods);
|
||||
};
|
||||
|
||||
SerializedType.prototype.serialize_hex = function (so, hexData) {
|
||||
var byteData = bytes.fromBits(hex.toBits(hexData));
|
||||
this.serialize_varint(so, byteData.length);
|
||||
so.append(byteData);
|
||||
};
|
||||
|
||||
SerializedType.prototype.serialize_varint = function (so, val) {
|
||||
if (val < 0) {
|
||||
throw new Error("Variable integers are unsigned.");
|
||||
}
|
||||
if (val <= 192) {
|
||||
so.append([val]);
|
||||
} else if (val <= 12,480) {
|
||||
val -= 193;
|
||||
so.append([193 + (val >>> 8), val & 0xff]);
|
||||
} else if (val <= 918744) {
|
||||
val -= 12481;
|
||||
so.append([
|
||||
241 + (val >>> 16),
|
||||
val >>> 8 & 0xff,
|
||||
val & 0xff
|
||||
]);
|
||||
} else throw new Error("Variable integer overflow.");
|
||||
};
|
||||
|
||||
exports.Int8 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
return so.append([val & 0xff]);
|
||||
so.append([val & 0xff]);
|
||||
},
|
||||
parse: function (so) {
|
||||
return so.read(1)[0];
|
||||
@@ -13,7 +58,10 @@ exports.Int8 = new SerializedType({
|
||||
|
||||
exports.Int16 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
so.append([
|
||||
val >>> 8 & 0xff,
|
||||
val & 0xff
|
||||
]);
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
@@ -22,7 +70,12 @@ exports.Int16 = new SerializedType({
|
||||
|
||||
exports.Int32 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
so.append([
|
||||
val >>> 24 & 0xff,
|
||||
val >>> 16 & 0xff,
|
||||
val >>> 8 & 0xff,
|
||||
val & 0xff
|
||||
]);
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
@@ -67,7 +120,62 @@ exports.Hash160 = new SerializedType({
|
||||
|
||||
exports.Amount = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
var amount = Amount.from_json(val);
|
||||
if (!amount.is_valid()) {
|
||||
throw new Error("Not a valid Amount object.");
|
||||
}
|
||||
|
||||
// Amount (64-bit integer)
|
||||
if (amount.is_native()) {
|
||||
var valueHex = amount._value.toString(16);
|
||||
|
||||
// Enforce correct length (64 bits)
|
||||
if (valueHex.length > 16) {
|
||||
throw new Error('Value out of bounds');
|
||||
}
|
||||
while (valueHex.length < 16) {
|
||||
valueHex = "0" + valueHex;
|
||||
}
|
||||
|
||||
var valueBytes = bytes.fromBits(hex.toBits(valueHex));
|
||||
// Clear most significant two bits - these bits should already be 0 if
|
||||
// Amount enforces the range correctly, but we'll clear them anyway just
|
||||
// so this code can make certain guarantees about the encoded value.
|
||||
valueBytes[0] &= 0x3f;
|
||||
if (!amount.is_negative()) valueBytes[0] |= 0x40;
|
||||
|
||||
so.append(valueBytes);
|
||||
} else {
|
||||
// XXX
|
||||
throw new Error("Non-native amounts not implemented!");
|
||||
}
|
||||
|
||||
if (!amount.is_native()) {
|
||||
// Currency (160-bit hash)
|
||||
var currency = amount.currency().to_json();
|
||||
if ("string" === typeof currency && currency.length === 3) {
|
||||
var currencyCode = currency.toUpperCase(),
|
||||
currencyData = utils.arraySet(20, 0);
|
||||
|
||||
if (!/^[A-Z]{3}$/.test(currencyCode)) {
|
||||
throw new Error('Invalid currency code');
|
||||
}
|
||||
|
||||
currencyData[12] = currencyCode.charCodeAt(0) & 0xff;
|
||||
currencyData[13] = currencyCode.charCodeAt(1) & 0xff;
|
||||
currencyData[14] = currencyCode.charCodeAt(2) & 0xff;
|
||||
|
||||
var currencyBits = bytes.toBits(currencyData),
|
||||
currencyHash = sjcl.hash.ripemd160.hash(currencyBits);
|
||||
|
||||
so.append(bytes.fromBits(currencyHash));
|
||||
} else {
|
||||
throw new Error('Tried to serialize invalid/unimplemented currency type.');
|
||||
}
|
||||
|
||||
// Issuer (160-bit hash)
|
||||
// XXX
|
||||
}
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
@@ -76,7 +184,8 @@ exports.Amount = new SerializedType({
|
||||
|
||||
exports.VariableLength = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
if ("string" === typeof val) this.serialize_hex(so, val);
|
||||
else throw new Error("Unknown datatype.");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
@@ -85,7 +194,8 @@ exports.VariableLength = new SerializedType({
|
||||
|
||||
exports.Account = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
var account = UInt160.from_json(val);
|
||||
this.serialize_hex(so, account.to_hex());
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
//
|
||||
|
||||
var serializer = {};
|
||||
|
||||
serializer.addUInt16 = function(value) {
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
addUInt16(value.charCodeAt(0));
|
||||
break;
|
||||
|
||||
case 'integer':
|
||||
for (i = 16/8; i; i -=1) {
|
||||
raw.push(value & 255);
|
||||
value >>= 8;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw 'UNEXPECTED_TYPE';
|
||||
}
|
||||
};
|
||||
|
||||
serializer.addUInt160 = function(value) {
|
||||
switch (typeof value) {
|
||||
case 'array':
|
||||
raw.concat(value);
|
||||
break;
|
||||
|
||||
case 'integer':
|
||||
for (i = 160/8; i; i -=1) {
|
||||
raw.push(value & 255);
|
||||
value >>= 8;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw 'UNEXPECTED_TYPE';
|
||||
}
|
||||
};
|
||||
|
||||
serializer.getSHA512Half = function() {
|
||||
};
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
28
src/js/sjcl-custom/sjcl-ecdsa-der.js
Normal file
28
src/js/sjcl-custom/sjcl-ecdsa-der.js
Normal file
@@ -0,0 +1,28 @@
|
||||
sjcl.ecc.ecdsa.secretKey.prototype.signDER = function(hash, paranoia) {
|
||||
return this.encodeDER(this.sign(hash, paranoia));
|
||||
};
|
||||
|
||||
sjcl.ecc.ecdsa.secretKey.prototype.encodeDER = function(rs) {
|
||||
var w = sjcl.bitArray,
|
||||
R = this._curve.r,
|
||||
l = R.bitLength(),
|
||||
r = sjcl.bn.fromBits(w.bitSlice(rs,0,l)).toBits(),
|
||||
s = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l)).toBits();
|
||||
|
||||
var rb = sjcl.codec.bytes.fromBits(r),
|
||||
sb = sjcl.codec.bytes.fromBits(s);
|
||||
|
||||
var buffer = [].concat(
|
||||
0x30,
|
||||
4 + rb.length + sb.length,
|
||||
0x02,
|
||||
rb.length,
|
||||
rb,
|
||||
0x02,
|
||||
sb.length,
|
||||
sb
|
||||
);
|
||||
|
||||
return sjcl.codec.bytes.toBits(buffer);
|
||||
};
|
||||
|
||||
61
src/js/sjcl-custom/sjcl-extramath.js
Executable file
61
src/js/sjcl-custom/sjcl-extramath.js
Executable file
@@ -0,0 +1,61 @@
|
||||
sjcl.bn.ZERO = new sjcl.bn(0);
|
||||
|
||||
/** [ this / that , this % that ] */
|
||||
sjcl.bn.prototype.divRem = function (that) {
|
||||
if (typeof(that) !== "object") { that = new this._class(that); }
|
||||
var thisa = this.abs(), thata = that.abs(), quot = new this._class(0),
|
||||
ci = 0;
|
||||
if (!thisa.greaterEquals(thata)) {
|
||||
this.initWith(0);
|
||||
return this;
|
||||
} else if (thisa.equals(thata)) {
|
||||
this.initWith(sign);
|
||||
return this;
|
||||
}
|
||||
|
||||
for (; thisa.greaterEquals(thata); ci++) {
|
||||
thata.doubleM();
|
||||
}
|
||||
for (; ci > 0; ci--) {
|
||||
quot.doubleM();
|
||||
thata.halveM();
|
||||
if (thisa.greaterEquals(thata)) {
|
||||
quot.addM(1);
|
||||
thisa.subM(that).normalize();
|
||||
}
|
||||
}
|
||||
return [quot, thisa];
|
||||
};
|
||||
|
||||
/** this /= that (rounded to nearest int) */
|
||||
sjcl.bn.prototype.divRound = function (that) {
|
||||
var dr = this.divRem(that), quot = dr[0], rem = dr[1];
|
||||
|
||||
if (rem.doubleM().greaterEquals(that)) {
|
||||
quot.addM(1);
|
||||
}
|
||||
|
||||
return quot;
|
||||
};
|
||||
|
||||
/** this /= that (rounded down) */
|
||||
sjcl.bn.prototype.div = function (that) {
|
||||
var dr = this.divRem(that);
|
||||
return dr[0];
|
||||
};
|
||||
|
||||
sjcl.bn.prototype.sign = function () {
|
||||
return this.greaterEquals(sjcl.bn.ZERO) ? 1 : -1;
|
||||
};
|
||||
|
||||
/** -this */
|
||||
sjcl.bn.prototype.neg = function () {
|
||||
return sjcl.bn.ZERO.sub(this);
|
||||
};
|
||||
|
||||
/** |this| */
|
||||
sjcl.bn.prototype.abs = function () {
|
||||
if (this.sign() === -1) {
|
||||
return this.neg();
|
||||
} else return this;
|
||||
};
|
||||
207
src/js/sjcl-custom/sjcl-ripemd160.js
Executable file
207
src/js/sjcl-custom/sjcl-ripemd160.js
Executable file
@@ -0,0 +1,207 @@
|
||||
/** @fileOverview Javascript RIPEMD-160 implementation.
|
||||
*
|
||||
* @author Artem S Vybornov <vybornov@gmail.com>
|
||||
*/
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Context for a RIPEMD-160 operation in progress.
|
||||
* @constructor
|
||||
* @class RIPEMD, 160 bits.
|
||||
*/
|
||||
sjcl.hash.ripemd160 = function (hash) {
|
||||
if (hash) {
|
||||
this._h = hash._h.slice(0);
|
||||
this._buffer = hash._buffer.slice(0);
|
||||
this._length = hash._length;
|
||||
} else {
|
||||
this.reset();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hash a string or an array of words.
|
||||
* @static
|
||||
* @param {bitArray|String} data the data to hash.
|
||||
* @return {bitArray} The hash value, an array of 5 big-endian words.
|
||||
*/
|
||||
sjcl.hash.ripemd160.hash = function (data) {
|
||||
return (new sjcl.hash.ripemd160()).update(data).finalize();
|
||||
};
|
||||
|
||||
sjcl.hash.ripemd160.prototype = {
|
||||
/**
|
||||
* Reset the hash state.
|
||||
* @return this
|
||||
*/
|
||||
reset: function () {
|
||||
this._h = _h0.slice(0);
|
||||
this._buffer = [];
|
||||
this._length = 0;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the hash state.
|
||||
* @param {bitArray|String} data the data to hash.
|
||||
* @return this
|
||||
*/
|
||||
update: function (data) {
|
||||
if ( typeof data === "string" )
|
||||
data = sjcl.codec.utf8String.toBits(data);
|
||||
|
||||
var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data),
|
||||
ol = this._length,
|
||||
nl = this._length = ol + sjcl.bitArray.bitLength(data);
|
||||
for (i = 512+ol & -512; i <= nl; i+= 512) {
|
||||
var words = b.splice(0,16);
|
||||
for ( var w = 0; w < 16; ++w )
|
||||
words[w] = _cvt(words[w]);
|
||||
|
||||
_block.call( this, words );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Complete hashing and output the hash value.
|
||||
* @return {bitArray} The hash value, an array of 5 big-endian words.
|
||||
*/
|
||||
finalize: function () {
|
||||
var b = sjcl.bitArray.concat( this._buffer, [ sjcl.bitArray.partial(1,1) ] ),
|
||||
l = ( this._length + 1 ) % 512,
|
||||
z = ( l > 448 ? 512 : 448 ) - l % 448,
|
||||
zp = z % 32;
|
||||
|
||||
if ( zp > 0 )
|
||||
b = sjcl.bitArray.concat( b, [ sjcl.bitArray.partial(zp,0) ] )
|
||||
for ( ; z >= 32; z -= 32 )
|
||||
b.push(0);
|
||||
|
||||
b.push( _cvt( this._length | 0 ) );
|
||||
b.push( _cvt( Math.floor(this._length / 0x100000000) ) );
|
||||
|
||||
while ( b.length ) {
|
||||
var words = b.splice(0,16);
|
||||
for ( var w = 0; w < 16; ++w )
|
||||
words[w] = _cvt(words[w]);
|
||||
|
||||
_block.call( this, words );
|
||||
}
|
||||
|
||||
var h = this._h;
|
||||
this.reset();
|
||||
|
||||
for ( var w = 0; w < 5; ++w )
|
||||
h[w] = _cvt(h[w]);
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
var _h0 = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ];
|
||||
|
||||
var _k1 = [ 0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e ];
|
||||
var _k2 = [ 0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000 ];
|
||||
for ( var i = 4; i >= 0; --i ) {
|
||||
for ( var j = 1; j < 16; ++j ) {
|
||||
_k1.splice(i,0,_k1[i]);
|
||||
_k2.splice(i,0,_k2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var _r1 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
||||
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
||||
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
||||
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 ];
|
||||
var _r2 = [ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
||||
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
||||
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
||||
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
||||
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 ];
|
||||
|
||||
var _s1 = [ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
||||
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
||||
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
||||
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
||||
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ];
|
||||
var _s2 = [ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
||||
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
||||
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
||||
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
||||
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ];
|
||||
|
||||
function _f0(x,y,z) {
|
||||
return x ^ y ^ z;
|
||||
};
|
||||
|
||||
function _f1(x,y,z) {
|
||||
return (x & y) | (~x & z);
|
||||
};
|
||||
|
||||
function _f2(x,y,z) {
|
||||
return (x | ~y) ^ z;
|
||||
};
|
||||
|
||||
function _f3(x,y,z) {
|
||||
return (x & z) | (y & ~z);
|
||||
};
|
||||
|
||||
function _f4(x,y,z) {
|
||||
return x ^ (y | ~z);
|
||||
};
|
||||
|
||||
function _rol(n,l) {
|
||||
return (n << l) | (n >>> (32-l));
|
||||
}
|
||||
|
||||
function _cvt(n) {
|
||||
return ( (n & 0xff << 0) << 24 )
|
||||
| ( (n & 0xff << 8) << 8 )
|
||||
| ( (n & 0xff << 16) >>> 8 )
|
||||
| ( (n & 0xff << 24) >>> 24 );
|
||||
}
|
||||
|
||||
function _block(X) {
|
||||
var A1 = this._h[0], B1 = this._h[1], C1 = this._h[2], D1 = this._h[3], E1 = this._h[4],
|
||||
A2 = this._h[0], B2 = this._h[1], C2 = this._h[2], D2 = this._h[3], E2 = this._h[4];
|
||||
|
||||
var j = 0, T;
|
||||
|
||||
for ( ; j < 16; ++j ) {
|
||||
T = _rol( A1 + _f0(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
|
||||
A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
|
||||
T = _rol( A2 + _f4(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
|
||||
A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
|
||||
for ( ; j < 32; ++j ) {
|
||||
T = _rol( A1 + _f1(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
|
||||
A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
|
||||
T = _rol( A2 + _f3(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
|
||||
A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
|
||||
for ( ; j < 48; ++j ) {
|
||||
T = _rol( A1 + _f2(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
|
||||
A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
|
||||
T = _rol( A2 + _f2(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
|
||||
A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
|
||||
for ( ; j < 64; ++j ) {
|
||||
T = _rol( A1 + _f3(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
|
||||
A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
|
||||
T = _rol( A2 + _f1(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
|
||||
A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
|
||||
for ( ; j < 80; ++j ) {
|
||||
T = _rol( A1 + _f4(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
|
||||
A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
|
||||
T = _rol( A2 + _f0(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
|
||||
A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
|
||||
|
||||
T = this._h[1] + C1 + D2;
|
||||
this._h[1] = this._h[2] + D1 + E2;
|
||||
this._h[2] = this._h[3] + E1 + A2;
|
||||
this._h[3] = this._h[4] + A1 + B2;
|
||||
this._h[4] = this._h[0] + B1 + C2;
|
||||
this._h[0] = T;
|
||||
}
|
||||
|
||||
})();
|
||||
72
src/js/sjcl-custom/sjcl-secp256k1.js
Executable file
72
src/js/sjcl-custom/sjcl-secp256k1.js
Executable file
@@ -0,0 +1,72 @@
|
||||
// ----- for secp256k1 ------
|
||||
|
||||
// Overwrite NIST-P256 with secp256k1 so we're on even footing
|
||||
sjcl.ecc.curves.c256 = new sjcl.ecc.curve(
|
||||
sjcl.bn.pseudoMersennePrime(256, [[0,-1],[4,-1],[6,-1],[7,-1],[8,-1],[9,-1],[32,-1]]),
|
||||
"0x14551231950b75fc4402da1722fc9baee",
|
||||
0,
|
||||
7,
|
||||
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
|
||||
);
|
||||
|
||||
// Replace point addition and doubling algorithms
|
||||
// NIST-P256 is a=-3, we need algorithms for a=0
|
||||
sjcl.ecc.pointJac.prototype.add = function(T) {
|
||||
var S = this;
|
||||
if (S.curve !== T.curve) {
|
||||
throw("sjcl.ecc.add(): Points must be on the same curve to add them!");
|
||||
}
|
||||
|
||||
if (S.isIdentity) {
|
||||
return T.toJac();
|
||||
} else if (T.isIdentity) {
|
||||
return S;
|
||||
}
|
||||
|
||||
var z1z1 = S.z.square();
|
||||
var h = T.x.mul(z1z1).subM(S.x);
|
||||
var s2 = T.y.mul(S.z).mul(z1z1);
|
||||
|
||||
if (h.equals(0)) {
|
||||
if (S.y.equals(T.y.mul(z1z1.mul(S.z)))) {
|
||||
// same point
|
||||
return S.doubl();
|
||||
} else {
|
||||
// inverses
|
||||
return new sjcl.ecc.pointJac(S.curve);
|
||||
}
|
||||
}
|
||||
|
||||
var hh = h.square();
|
||||
var i = hh.copy().doubleM().doubleM();
|
||||
var j = h.mul(i);
|
||||
var r = s2.sub(S.y).doubleM();
|
||||
var v = S.x.mul(i);
|
||||
|
||||
var x = r.square().subM(j).subM(v.copy().doubleM());
|
||||
var y = r.mul(v.sub(x)).subM(S.y.mul(j).doubleM());
|
||||
var z = S.z.add(h).square().subM(z1z1).subM(hh);
|
||||
|
||||
return new sjcl.ecc.pointJac(this.curve,x,y,z);
|
||||
};
|
||||
|
||||
sjcl.ecc.pointJac.prototype.doubl = function () {
|
||||
if (this.isIdentity) { return this; }
|
||||
|
||||
var a = this.x.square();
|
||||
var b = this.y.square();
|
||||
var c = b.square();
|
||||
var d = this.x.add(b).square().subM(a).subM(c).doubleM();
|
||||
var e = a.mul(3);
|
||||
var f = e.square();
|
||||
var x = f.sub(d.copy().doubleM());
|
||||
var y = e.mul(d.sub(x)).subM(c.doubleM().doubleM().doubleM());
|
||||
var z = this.y.mul(this.z).doubleM();
|
||||
return new sjcl.ecc.pointJac(this.curve, x, y, z);
|
||||
};
|
||||
|
||||
sjcl.ecc.point.prototype.toBytesCompressed = function () {
|
||||
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
|
||||
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
|
||||
};
|
||||
@@ -43,10 +43,16 @@
|
||||
// - may or may not forward.
|
||||
//
|
||||
|
||||
var Amount = require('./amount').Amount;
|
||||
var Currency = require('./amount').Currency;
|
||||
var UInt160 = require('./amount').UInt160;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var sjcl = require('../../build/sjcl');
|
||||
|
||||
var Amount = require('./amount').Amount;
|
||||
var Currency = require('./amount').Currency;
|
||||
var UInt160 = require('./amount').UInt160;
|
||||
var Seed = require('./seed').Seed;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var SerializedObject = require('./serializedobject').SerializedObject;
|
||||
|
||||
var config = require('./config');
|
||||
|
||||
var SUBMIT_MISSING = 4; // Report missing.
|
||||
var SUBMIT_LOST = 8; // Give up tracking.
|
||||
@@ -112,6 +118,11 @@ Transaction.flags = {
|
||||
},
|
||||
};
|
||||
|
||||
Transaction.formats = require('./binformat').tx;
|
||||
|
||||
Transaction.HASH_SIGN = 0x53545800;
|
||||
Transaction.HASH_SIGN_TESTNET = 0x73747800;
|
||||
|
||||
Transaction.prototype.consts = {
|
||||
'telLOCAL_ERROR' : -399,
|
||||
'temMALFORMED' : -299,
|
||||
@@ -156,6 +167,30 @@ Transaction.prototype.set_state = function (state) {
|
||||
}
|
||||
};
|
||||
|
||||
Transaction.prototype.serialize = function () {
|
||||
return SerializedObject.from_json(this.tx_json);
|
||||
};
|
||||
|
||||
Transaction.prototype.signing_hash = function () {
|
||||
var prefix = config.testnet
|
||||
? Transaction.HASH_SIGN_TESTNET
|
||||
: Transaction.HASH_SIGN;
|
||||
|
||||
return SerializedObject.from_json(this.tx_json).signing_hash(prefix);
|
||||
};
|
||||
|
||||
Transaction.prototype.sign = function () {
|
||||
var seed = Seed.from_json(this._secret),
|
||||
priv = seed.generate_private(this.tx_json.Account),
|
||||
hash = this.signing_hash();
|
||||
|
||||
var key = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves['c256'], priv.to_bn()),
|
||||
sig = key.signDER(hash.to_bits(), 0),
|
||||
hex = sjcl.codec.hex.fromBits(sig).toUpperCase();
|
||||
|
||||
this.tx_json.TxnSignature = hex;
|
||||
};
|
||||
|
||||
// Submit a transaction to the network.
|
||||
// XXX Don't allow a submit without knowing ledger_index.
|
||||
// XXX Have a network canSubmit(), post events for following.
|
||||
@@ -355,21 +390,21 @@ Transaction.prototype.transfer_rate = function (rate) {
|
||||
// --> flags: undefined, _flag_, or [ _flags_ ]
|
||||
Transaction.prototype.set_flags = function (flags) {
|
||||
if (flags) {
|
||||
var transaction_flags = Transaction.flags[this.tx_json.TransactionType];
|
||||
var transaction_flags = Transaction.flags[this.tx_json.TransactionType];
|
||||
|
||||
if (undefined == this.tx_json.Flags) // We plan to not define this field on new Transaction.
|
||||
this.tx_json.Flags = 0;
|
||||
|
||||
var flag_set = 'object' === typeof flags ? flags : [ flags ];
|
||||
var flag_set = 'object' === typeof flags ? flags : [ flags ];
|
||||
|
||||
for (index in flag_set) {
|
||||
var flag = flag_set[index];
|
||||
for (var index in flag_set) {
|
||||
if (!flag_set.hasOwnProperty(index)) continue;
|
||||
|
||||
if (flag in transaction_flags)
|
||||
{
|
||||
this.tx_json.Flags += transaction_flags[flag];
|
||||
}
|
||||
else {
|
||||
var flag = flag_set[index];
|
||||
|
||||
if (flag in transaction_flags) {
|
||||
this.tx_json.Flags += transaction_flags[flag];
|
||||
} else {
|
||||
// XXX Immediately report an error or mark it.
|
||||
}
|
||||
}
|
||||
|
||||
221
src/js/uint.js
Normal file
221
src/js/uint.js
Normal file
@@ -0,0 +1,221 @@
|
||||
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
var jsbn = require('./jsbn');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
var nbi = jsbn.nbi;
|
||||
|
||||
var Base = require('./base').Base;
|
||||
|
||||
//
|
||||
// Abstract UInt class
|
||||
//
|
||||
// Base class for UInt??? classes
|
||||
//
|
||||
|
||||
var UInt = function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
};
|
||||
|
||||
UInt.json_rewrite = function (j) {
|
||||
return this.from_json(j).to_json();
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_generic = function (j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return (new this()).parse_generic(j);
|
||||
}
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_hex = function (j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return (new this()).parse_hex(j);
|
||||
}
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_json = function (j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return (new this()).parse_json(j);
|
||||
}
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_bits = function (j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return (new this()).parse_bits(j);
|
||||
}
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_bn = function (j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return (new this()).parse_bn(j);
|
||||
}
|
||||
};
|
||||
|
||||
UInt.is_valid = function (j) {
|
||||
return this.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
UInt.prototype.clone = function () {
|
||||
return this.copyTo(new this.constructor());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
UInt.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
UInt.prototype.equals = function (d) {
|
||||
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
|
||||
};
|
||||
|
||||
UInt.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
};
|
||||
|
||||
// value = NaN on error.
|
||||
UInt.prototype.parse_generic = function (j) {
|
||||
// Canonicalize and validate
|
||||
if (config.accounts && j in config.accounts)
|
||||
j = config.accounts[j].account;
|
||||
|
||||
switch (j) {
|
||||
case undefined:
|
||||
case "0":
|
||||
case this.constructor.STR_ZERO:
|
||||
case this.constructor.ADDRESS_ZERO:
|
||||
case this.constructor.HEX_ZERO:
|
||||
this._value = nbi();
|
||||
break;
|
||||
|
||||
case "1":
|
||||
case this.constructor.STR_ONE:
|
||||
case this.constructor.ADDRESS_ONE:
|
||||
case this.constructor.HEX_ONE:
|
||||
this._value = new BigInteger([1]);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "r") {
|
||||
this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j);
|
||||
}
|
||||
else if (this.constructor.width === j.length) {
|
||||
this._value = new BigInteger(utils.stringToArray(j), 256);
|
||||
}
|
||||
else if ((this.constructor.width*2) === j.length) {
|
||||
// XXX Check char set!
|
||||
this._value = new BigInteger(j, 16);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UInt.prototype.parse_hex = function (j) {
|
||||
if ('string' === typeof j &&
|
||||
j.length === (this.constructor.width * 2)) {
|
||||
this._value = new BigInteger(j, 16);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UInt.prototype.parse_bits = function (j) {
|
||||
if (sjcl.bitArray.bitLength(j) !== this.constructor.width * 8) {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
var bytes = sjcl.codec.bytes.fromBits(j);
|
||||
this._value = new BigInteger(bytes, 256);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UInt.prototype.parse_json = UInt.prototype.parse_hex;
|
||||
|
||||
UInt.prototype.parse_bn = function (j) {
|
||||
if (j instanceof sjcl.bn &&
|
||||
j.bitLength() <= this.constructor.width * 8) {
|
||||
var bytes = sjcl.codec.bytes.fromBits(j.toBits());
|
||||
this._value = new BigInteger(bytes, 256);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Convert from internal form.
|
||||
UInt.prototype.to_bytes = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return null;
|
||||
|
||||
var bytes = this._value.toByteArray();
|
||||
bytes = bytes.map(function (b) { return (b+256) % 256; });
|
||||
var target = this.constructor.width;
|
||||
|
||||
// XXX Make sure only trim off leading zeros.
|
||||
bytes = bytes.slice(-target);
|
||||
while (bytes.length < target) bytes.unshift(0);
|
||||
|
||||
return bytes;
|
||||
};
|
||||
|
||||
UInt.prototype.to_hex = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return null;
|
||||
|
||||
var bytes = this.to_bytes();
|
||||
|
||||
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(bytes)).toUpperCase();
|
||||
};
|
||||
|
||||
UInt.prototype.to_json = UInt.prototype.to_hex;
|
||||
|
||||
UInt.prototype.to_bits = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return null;
|
||||
|
||||
var bytes = this.to_bytes();
|
||||
|
||||
return sjcl.codec.bytes.toBits(bytes);
|
||||
};
|
||||
|
||||
UInt.prototype.to_bn = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return null;
|
||||
|
||||
var bits = this.to_bits();
|
||||
|
||||
return sjcl.bn.fromBits(bits);
|
||||
};
|
||||
|
||||
exports.UInt = UInt;
|
||||
63
src/js/uint160.js
Normal file
63
src/js/uint160.js
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
var jsbn = require('./jsbn');
|
||||
var extend = require('extend');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
var nbi = jsbn.nbi;
|
||||
|
||||
var UInt = require('./uint').UInt,
|
||||
Base = require('./base').Base;
|
||||
|
||||
//
|
||||
// UInt160 support
|
||||
//
|
||||
|
||||
var UInt160 = extend(function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
}, UInt);
|
||||
|
||||
UInt160.width = 20;
|
||||
UInt160.prototype = extend({}, UInt.prototype);
|
||||
UInt160.prototype.constructor = UInt160;
|
||||
|
||||
var ADDRESS_ZERO = UInt160.ADDRESS_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
|
||||
var ADDRESS_ONE = UInt160.ADDRESS_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji";
|
||||
var HEX_ZERO = UInt160.HEX_ZERO = "0000000000000000000000000000000000000000";
|
||||
var HEX_ONE = UInt160.HEX_ONE = "0000000000000000000000000000000000000001";
|
||||
var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO);
|
||||
var STR_ONE = UInt160.STR_ONE = utils.hexToString(HEX_ONE);
|
||||
|
||||
// value = NaN on error.
|
||||
UInt160.prototype.parse_json = function (j) {
|
||||
// Canonicalize and validate
|
||||
if (config.accounts && j in config.accounts)
|
||||
j = config.accounts[j].account;
|
||||
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "r") {
|
||||
this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// XXX Json form should allow 0 and 1, C++ doesn't currently allow it.
|
||||
UInt160.prototype.to_json = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return NaN;
|
||||
|
||||
var output = Base.encode_check(Base.VER_ACCOUNT_ID, this.to_bytes());
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
exports.UInt160 = UInt160;
|
||||
37
src/js/uint256.js
Normal file
37
src/js/uint256.js
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
var jsbn = require('./jsbn');
|
||||
var extend = require('extend');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
var nbi = jsbn.nbi;
|
||||
|
||||
var UInt = require('./uint').UInt,
|
||||
Base = require('./base').Base;
|
||||
|
||||
//
|
||||
// UInt256 support
|
||||
//
|
||||
|
||||
var UInt256 = extend(function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
}, UInt);
|
||||
|
||||
UInt256.width = 32;
|
||||
UInt256.prototype = extend({}, UInt.prototype);
|
||||
UInt256.prototype.constructor = UInt256;
|
||||
|
||||
// XXX Generate these constants (or remove them)
|
||||
var ADDRESS_ZERO = UInt256.ADDRESS_ZERO = "XXX";
|
||||
var ADDRESS_ONE = UInt256.ADDRESS_ONE = "XXX";
|
||||
var HEX_ZERO = UInt256.HEX_ZERO = "00000000000000000000000000000000" +
|
||||
"00000000000000000000000000000000";
|
||||
var HEX_ONE = UInt256.HEX_ONE = "00000000000000000000000000000000" +
|
||||
"00000000000000000000000000000001";
|
||||
var STR_ZERO = UInt256.STR_ZERO = utils.hexToString(HEX_ZERO);
|
||||
var STR_ONE = UInt256.STR_ONE = utils.hexToString(HEX_ONE);
|
||||
|
||||
exports.UInt256 = UInt256;
|
||||
@@ -27,11 +27,11 @@ var trace = function(comment, func) {
|
||||
};
|
||||
|
||||
var arraySet = function (count, value) {
|
||||
var a = new Array(count);
|
||||
var i;
|
||||
var i, a = new Array(count);
|
||||
|
||||
for (i = 0; i != count; i += 1)
|
||||
for (i = 0; i < count; i++) {
|
||||
a[i] = value;
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,8 @@ var amount = require("../src/js/amount.js");
|
||||
var Amount = require("../src/js/amount.js").Amount;
|
||||
var UInt160 = require("../src/js/amount.js").UInt160;
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
var config = require('./config.js');
|
||||
|
||||
@@ -20,16 +21,16 @@ buster.testCase("Amount", {
|
||||
buster.assert.equals(nbi(), UInt160.from_generic("0")._value);
|
||||
},
|
||||
"Parse 0 export" : function () {
|
||||
buster.assert.equals(amount.consts.address_xns, UInt160.from_generic("0").to_json());
|
||||
buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_generic("0").to_json());
|
||||
},
|
||||
"Parse 1" : function () {
|
||||
buster.assert.equals(new BigInteger([1]), UInt160.from_generic("1")._value);
|
||||
},
|
||||
"Parse rrrrrrrrrrrrrrrrrrrrrhoLvTp export" : function () {
|
||||
buster.assert.equals(amount.consts.address_xns, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json());
|
||||
buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json());
|
||||
},
|
||||
"Parse rrrrrrrrrrrrrrrrrrrrBZbvji export" : function () {
|
||||
buster.assert.equals(amount.consts.address_one, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json());
|
||||
buster.assert.equals(UInt160.ADDRESS_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json());
|
||||
},
|
||||
"Parse mtgox export" : function () {
|
||||
buster.assert.equals(config.accounts["mtgox"].account, UInt160.from_json("mtgox").to_json());
|
||||
|
||||
@@ -12,8 +12,8 @@ var testutils = require("./testutils.js");
|
||||
|
||||
var config = require("./config.js");
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
// How long to wait for server to start.
|
||||
var serverDelay = 1500;
|
||||
|
||||
@@ -7,8 +7,8 @@ var Server = require("./server.js").Server;
|
||||
|
||||
var testutils = require("./testutils.js");
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
buster.testRunner.timeout = 5000;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ var Server = require("./server").Server;
|
||||
|
||||
var testutils = require("./testutils");
|
||||
|
||||
require("../src/js/amount").config = require("./config");
|
||||
require("../src/js/remote").config = require("./config");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
buster.testRunner.timeout = 5000;
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ var Server = require("./server.js").Server;
|
||||
|
||||
var testutils = require("./testutils.js");
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
buster.testRunner.timeout = 5000;
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ var Server = require("./server.js").Server;
|
||||
|
||||
var testutils = require("./testutils.js");
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
// How long to wait for server to start.
|
||||
var serverDelay = 1500; // XXX Not implemented.
|
||||
|
||||
@@ -7,8 +7,8 @@ var Server = require("./server.js").Server;
|
||||
|
||||
var testutils = require("./testutils.js");
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
// How long to wait for server to start.
|
||||
var serverDelay = 1500;
|
||||
|
||||
@@ -4,8 +4,8 @@ var Amount = require("../src/js/amount.js").Amount;
|
||||
var Remote = require("../src/js/remote.js").Remote;
|
||||
var Server = require("./server.js").Server;
|
||||
|
||||
require("../src/js/amount.js").config = require("./config.js");
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
var config = require("./config.js");
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ var Server = require("./server.js").Server;
|
||||
var Remote = require("../src/js/remote.js").Remote;
|
||||
var config = require("./config.js");
|
||||
|
||||
require("../src/js/remote.js").config = require("./config.js");
|
||||
var extend = require('extend');
|
||||
extend(require('../src/js/config'), require('./config'));
|
||||
|
||||
buster.testRunner.timeout = 5000;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user