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

Conflicts:
	validators.txt
This commit is contained in:
jed
2012-10-19 21:57:11 -07:00
27 changed files with 574 additions and 207 deletions

View File

@@ -6,6 +6,12 @@ var jsbn = require('./jsbn.js');
var BigInteger = jsbn.BigInteger;
var accounts = {};
var setAccounts = function (accounts_new) {
accounts = accounts_new;
};
var UInt160 = function () {
// Internal form:
// 0, 1, 'iXXXXX', 20 byte string, or NaN.
@@ -13,8 +19,15 @@ var UInt160 = function () {
this.value = NaN;
};
UInt160.json_rewrite = function (j) {
return UInt160.from_json(j).to_json();
};
// Return a new UInt160 from j.
UInt160.from_json = function (j) {
return (new UInt160()).parse_json(j in accounts ? accounts[j].account : j);
return 'string' === typeof j
? (new UInt160()).parse_json(j)
: j.clone();
};
UInt160.prototype.clone = function() {
@@ -28,9 +41,11 @@ UInt160.prototype.copyTo = function(d) {
return d;
};
// value === NaN on error.
// value = NaN on error.
UInt160.prototype.parse_json = function (j) {
// Canonicalize and validate
if (j in accounts)
j = accounts[j].account;
switch (j) {
case undefined:
@@ -117,7 +132,7 @@ Currency.prototype.copyTo = function(d) {
return d;
};
// this.value === NaN on error.
// this.value = NaN on error.
Currency.prototype.parse_json = function(j) {
if ("" === j || "0" === j || "XNS" === j) {
this.value = 0;
@@ -140,12 +155,6 @@ Currency.prototype.to_human = function() {
return this.value ? this.value : "XNS";
};
var accounts = {};
var setAccounts = function (accounts_new) {
accounts = accounts_new;
};
var Amount = function () {
// Json format:
// integer : XNS
@@ -160,6 +169,10 @@ var Amount = function () {
this.issuer = new UInt160();
};
Amount.json_rewrite = function(j) {
return Amount.from_json(j).to_json();
};
Amount.from_json = function(j) {
return (new Amount()).parse_json(j);
};
@@ -191,12 +204,12 @@ Amount.prototype.copyTo = function(d) {
// YYY Might also provide is_valid_json.
Amount.prototype.is_valid = function() {
return NaN !== this.value;
return !isNaN(this.value);
};
// Convert only value to JSON wire format.
Amount.prototype.to_text = function(allow_nan) {
if (NaN === this.value) {
if (isNaN(this.value)) {
// Never should happen.
return allow_nan ? NaN : "0";
}
@@ -237,7 +250,7 @@ Amount.prototype.to_text = function(allow_nan) {
};
Amount.prototype.canonicalize = function() {
if (NaN === this.value || !this.currency) {
if (isNaN(this.value) || !this.currency) {
// nothing
}
else if (this.value.equals(BigInteger.ZERO)) {
@@ -282,7 +295,7 @@ Amount.prototype.to_json = function() {
};
Amount.prototype.to_text_full = function() {
return this.value === NaN
return isNaN(this.value)
? NaN
: this.is_native
? this.to_text() + "/XNS"
@@ -332,44 +345,58 @@ Amount.prototype.parse_native = function(j) {
// Parse a non-native value.
Amount.prototype.parse_value = function(j) {
this.is_native = false;
if ('number' === typeof j) {
this.is_negative = j < 0;
if (this.is_negative) j = -j;
this.value = new BigInteger(j);
this.offset = 0;
this.is_native = false;
this.is_negative = j < 0;
this.canonicalize();
}
else if ('string' === typeof j) {
var e = j.match(/^(-?\d+)e(\d+)/);
var d = j.match(/^(-?\d+)\.(\d+)/);
var i = j.match(/^(-?)(\d+)$/);
var d = !i && j.match(/^(-?)(\d+)\.(\d*)$/);
var e = !e && j.match(/^(-?)(\d+)e(\d+)$/);
if (e) {
// e notation
this.value = new BigInteger(e[1]);
this.offset = parseInt(e[2]);
this.value = new BigInteger(e[2]);
this.offset = parseInt(e[3]);
this.is_negative = !!e[1];
this.canonicalize();
}
else if (d) {
// float notation
var integer = new BigInteger(d[1]);
var fraction = new BigInteger(d[2]);
this.value = integer.multiply(exports.consts.bi_10.clone().pow(d[2].length)).add(fraction);
this.offset = -d[2].length;
var integer = new BigInteger(d[2]);
var fraction = new BigInteger(d[3]);
var precision = d[3].length;
this.value = integer.multiply(exports.consts.bi_10.clone().pow(precision)).add(fraction);
this.offset = -precision;
this.is_negative = !!d[1];
this.canonicalize();
}
else
{
else if (i) {
// integer notation
this.value = new BigInteger(j);
this.offset = 0;
this.value = new BigInteger(i[2]);
this.offset = 0;
this.is_negative = !!i[1];
this.canonicalize();
}
this.is_native = false;
this.is_negative = undefined;
this.canonicalize();
else {
this.value = NaN;
}
}
else if (j.constructor == BigInteger) {
this.value = j.clone();
}
else {
this.value = NaN;
@@ -395,15 +422,18 @@ Amount.prototype.parse_json = function(j) {
this.issuer = new UInt160();
}
}
else if ('object' === typeof j && j.currency) {
// Never XNS.
else if ('object' === typeof j && j.constructor == Amount) {
j.copyTo(this);
}
else if ('object' === typeof j && 'value' in j) {
// Parse the passed value to sanitize and copy it.
this.parse_value(j.value);
this.currency.parse_json(j.currency);
this.currency.parse_json(j.currency); // Never XNS.
this.issuer.parse_json(j.issuer);
}
else {
this.value = NaN;
this.value = NaN;
}
return this;

View File

@@ -4,9 +4,6 @@
// http://www.w3.org/TR/websockets/#the-websocket-interface
//
// YYY Will later provide a network access which use multiple instances of this.
// YYY A better model might be to allow requesting a target state: keep connected or not.
// XXX Make subscribe target state.
// XXX Auto subscribe on connect.
//
// Node
@@ -93,7 +90,7 @@ Request.prototype.ledger_index = function (ledger_index) {
};
Request.prototype.account_root = function (account) {
this.message.account_root = account;
this.message.account_root = UInt160.json_rewrite(account);
return this;
};
@@ -119,7 +116,10 @@ Request.prototype.transaction = function (t) {
Request.prototype.ripple_state = function (account, issuer, currency) {
this.message.ripple_state = {
'accounts' : [ account, issuer ],
'accounts' : [
UInt160.from_json(account).to_json(),
UInt160.from_json(issuer).to_json()
],
'currency' : currency
};
@@ -168,7 +168,9 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) {
// Cache for various ledgers.
// XXX Clear when ledger advances.
this.ledgers = {
'current' : {}
'current' : {
'account_root' : {}
}
};
};
@@ -626,6 +628,8 @@ Remote.prototype._server_subscribe = function () {
// Ask the remote to accept the current ledger.
// - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_closed' events.
// A good way to be notified of the result of this is:
// remote.once('ledger_closed', function (ledger_closed, ledger_closed_index) { ... } );
Remote.prototype.ledger_accept = function () {
if (this.stand_alone || undefined === this.stand_alone)
{
@@ -655,12 +659,13 @@ Remote.prototype.request_account_balance = function (account, current) {
// Return the next account sequence if possible.
// <-- undefined or Sequence
Remote.prototype.account_seq = function (account, advance) {
var account_info = this.accounts[account];
var account = UInt160.json_rewrite(account);
var account_info = this.accounts[account];
var seq;
if (account_info && account_info.seq)
{
var seq = account_info.seq;
seq = account_info.seq;
if (advance) account_info.seq += 1;
}
@@ -668,6 +673,14 @@ Remote.prototype.account_seq = function (account, advance) {
return seq;
}
Remote.prototype.set_account_seq = function (account, seq) {
var account = UInt160.json_rewrite(account);
if (!this.accounts[account]) this.accounts[account] = {};
this.accounts[account].seq = seq;
}
// Return a request to refresh accounts[account].seq.
Remote.prototype.account_seq_cache = function (account, current) {
var self = this;
@@ -690,7 +703,7 @@ Remote.prototype.account_seq_cache = function (account, current) {
// Mark an account's root node as dirty.
Remote.prototype.dirty_account_root = function (account) {
delete this.ledgers.current.account_root[account];
delete this.ledgers.current.account_root[UInt160.json_rewrite(account)];
};
// Return a request to get a ripple balance.
@@ -700,26 +713,28 @@ Remote.prototype.dirty_account_root = function (account) {
// --> currency: String
// --> current: bool : true = current ledger
Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) {
var src = this.remote.config.accounts[account] ? this.remote.config.accounts[account].account : account;
var dst = this.remote.config.accounts[issuer] ? this.remote.config.accounts[issuer].account : issuer;
var account_u = UInt160.from_json(account);
var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger.
return (this.request_ledger_entry('ripple_state')) // YYY Could be cached per ledger.
.ripple_state(src, dst, currency)
return request
.ripple_state(account, issuer, currency)
.ledger_choose(current)
.on('success', function (message) {
var node = message.node;
var flip = UInt160.from_json(src) == node.HighLimit.issuer;
var issuerLimit = flip ? node.LowLimit : node.HighLimit;
var accountLimit = flip ? node.HighLimit : node.LowLimit;
var issuerBalance = (flip ? node.Balance.clone().negate() : node.Balance.clone()).parse_issuer(dst);
var accountBalance = issuerBalance.clone().parse_issuer(dst);
var lowLimit = Amount.from_json(node.LowLimit);
var highLimit = Amount.from_json(node.HighLimit);
var balance = Amount.from_json(node.Balance);
var flip = account_u == highLimit.issuer;
var issuerLimit = flip ? lowLimit : highLimit;
var accountLimit = flip ? highLimit : lowLimit;
var issuerBalance = (flip ? balance.negate() : balance).parse_issuer(issuer);
var accountBalance = issuerBalance.clone().parse_issuer(issuer);
// If the caller also waits for 'success', they might run before this.
request.emit('ripple_state', {
'issuer_balance' : issuerBalance, // Balance with dst as issuer.
'account_balance' : accountBalance, // Balance with src as issuer.
'issuer_limit' : issuerLimit.clone().parse_issuer(src), // Limit set by issuer with src as issuer.
'account_limit' : accountLimit.clone().parse_issuer(dst) // Limit set by account with dst as issuer.
'issuer_balance' : issuerBalance, // Balance with dst as issuer.
'account_balance' : accountBalance, // Balance with account as issuer.
'issuer_limit' : issuerLimit.clone().parse_issuer(account), // Limit set by issuer with src as issuer.
'account_limit' : accountLimit.clone().parse_issuer(issuer) // Limit set by account with dst as issuer.
});
});
}
@@ -800,6 +815,7 @@ var Transaction = function (remote) {
self.set_state('client_proposed');
self.emit('proposed', {
'transaction' : message.transaction,
'result' : message.engine_result,
'result_code' : message.engine_result_code,
'result_message' : message.engine_result_message,
@@ -997,13 +1013,23 @@ Transaction.prototype.account_secret = function (account) {
return this.remote.config.accounts[account] ? this.remote.config.accounts[account].secret : undefined;
};
Transaction.prototype.offer_cancel = function (src, sequence) {
this.secret = this.account_secret(src);
this.transaction.TransactionType = 'OfferCancel';
this.transaction.Account = UInt160.from_json(src).to_json();
this.transaction.OfferSequence = Number(sequence);
return this;
};
// XXX Expiration should use a time.
Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) {
this.secret = this.account_secret(src);
this.transaction.TransactionType = 'OfferCreate';
this.transaction.Account = this.account_default(src);
this.transaction.Account = UInt160.from_json(src).to_json();
this.transaction.Fee = fees.offer.to_json();
this.transaction.TakerPays = taker_pays.to_json();
this.transaction.TakerGets = taker_gets.to_json();
this.transaction.TakerPays = Amount.json_rewrite(taker_pays);
this.transaction.TakerGets = Amount.json_rewrite(taker_gets);
if (expiration)
this.transaction.Expiration = expiration;
@@ -1015,12 +1041,15 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi
//
// When a transaction is submitted:
// - If the connection is reliable and the server is not merely forwarding and is not malicious,
// --> src : UInt160 or String
// --> dst : UInt160 or String
// --> deliver_amount : Amount or String.
Transaction.prototype.payment = function (src, dst, deliver_amount) {
this.secret = this.account_secret(src);
this.transaction.TransactionType = 'Payment';
this.transaction.Account = this.account_default(src);
this.transaction.Amount = deliver_amount.to_json();
this.transaction.Destination = this.account_default(dst);
this.transaction.Account = UInt160.from_json(src).to_json();
this.transaction.Amount = Amount.json_rewrite(deliver_amount);
this.transaction.Destination = UInt160.from_json(dst).to_json();
return this;
}
@@ -1028,7 +1057,7 @@ Transaction.prototype.payment = function (src, dst, deliver_amount) {
Transaction.prototype.ripple_line_set = function (src, limit, quality_in, quality_out) {
this.secret = this.account_secret(src);
this.transaction.TransactionType = 'CreditSet';
this.transaction.Account = this.account_default(src);
this.transaction.Account = UInt160.from_json(src).to_json();
// Allow limit of 0 through.
if (undefined !== limit)

View File

@@ -108,6 +108,7 @@
<ClCompile Include="src\ECIES.cpp" />
<ClCompile Include="src\FieldNames.cpp" />
<ClCompile Include="src\HashedObject.cpp" />
<ClCompile Include="src\HTTPRequest.cpp" />
<ClCompile Include="src\HttpsClient.cpp" />
<ClCompile Include="src\Interpreter.cpp" />
<ClCompile Include="src\Ledger.cpp" />
@@ -134,7 +135,6 @@
<ClCompile Include="src\PeerDoor.cpp" />
<ClCompile Include="src\PlatRand.cpp" />
<ClCompile Include="src\PubKeyCache.cpp" />
<ClCompile Include="src\RequestParser.cpp" />
<ClCompile Include="src\rfc1751.cpp" />
<ClCompile Include="src\RippleCalc.cpp" />
<ClCompile Include="src\RippleLines.cpp" />

View File

@@ -120,9 +120,6 @@
<ClCompile Include="src\PubKeyCache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\RequestParser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\rfc1751.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -303,6 +300,9 @@
<ClCompile Include="obj\src\ripple.pb.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\HTTPRequest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="KnownNodeList.h">

View File

@@ -70,7 +70,10 @@ void Application::run()
{
assert(mTxnDB == NULL);
if (!theConfig.DEBUG_LOGFILE.empty())
{
Log::setLogFile(theConfig.DEBUG_LOGFILE);
LogPartition::setSeverity(lsDEBUG);
}
boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService));
auxThread.detach();

View File

@@ -14,6 +14,7 @@
#include "utils.h"
#include "Log.h"
SETUP_LOG();
// How often to enforce policies.
#define POLICY_INTERVAL_SECONDS 5
@@ -164,7 +165,7 @@ void ConnectionPool::policyLowWater()
if (mConnectedMap.size() > theConfig.PEER_CONNECT_LOW_WATER)
{
// Above low water mark, don't need more connections.
Log(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER;
cLog(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER;
nothing();
}
@@ -178,7 +179,7 @@ void ConnectionPool::policyLowWater()
else if (!peerAvailable(strIp, iPort))
{
// No more connections available to start.
Log(lsTRACE) << "Pool: Low water: no peers available.";
cLog(lsTRACE) << "Pool: Low water: no peers available.";
// XXX Might ask peers for more ips.
nothing();
@@ -186,10 +187,12 @@ void ConnectionPool::policyLowWater()
else
{
// Try to start connection.
Log(lsTRACE) << "Pool: Low water: start connection.";
cLog(lsTRACE) << "Pool: Low water: start connection.";
if (!peerConnect(strIp, iPort))
Log(lsINFO) << "Pool: Low water: already connected.";
{
cLog(lsINFO) << "Pool: Low water: already connected.";
}
// Check if we need more.
policyLowWater();
@@ -303,11 +306,11 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
if (ppResult)
{
//Log(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort;
//cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort;
}
else
{
//Log(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort;
//cLog(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort;
}
return ppResult;
@@ -355,7 +358,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer,
if (naPeer == theApp->getWallet().getNodePublic())
{
Log(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
cLog(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
}
else
{
@@ -365,7 +368,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer,
if (itCm == mConnectedMap.end())
{
// New connection.
//Log(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
//cLog(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
mConnectedMap[naPeer] = peer;
bNew = true;
@@ -378,7 +381,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer,
if (itCm->second->getIP().empty())
{
// Old peer did not know it's IP.
//Log(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort;
//cLog(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort;
itCm->second->setIpPort(strIP, iPort);
@@ -388,14 +391,14 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer,
else
{
// Old peer knew its IP. Do nothing.
//Log(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
//cLog(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
nothing();
}
}
else
{
//Log(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
//cLog(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
nothing();
}
@@ -418,12 +421,12 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPe
if (itCm == mConnectedMap.end())
{
// Did not find it. Not already connecting or connected.
Log(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent.";
cLog(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent.";
// XXX Maybe bad error, considering we have racing connections, may not so bad.
}
else if (itCm->second != peer)
{
Log(lsWARNING) << "Pool: disconected: non canonical entry";
cLog(lsWARNING) << "Pool: disconected: non canonical entry";
nothing();
}
@@ -432,12 +435,12 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPe
// Found it. Delete it.
mConnectedMap.erase(itCm);
//Log(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort();
//cLog(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort();
}
}
else
{
//Log(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort();
//cLog(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort();
}
}
@@ -463,7 +466,7 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort)
boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
boost::posix_time::ptime tpNext = tpNow + boost::posix_time::seconds(iInterval);
//Log(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)")
//cLog(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)")
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
@@ -479,13 +482,13 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort)
// boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
// boost::posix_time::ptime tpNext = ptFromSeconds(db->getInt("ScanNext"));
//Log(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)")
//cLog(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)")
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
}
}
else
{
//Log(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort;
//cLog(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort;
}
return bScanDirty;
@@ -500,7 +503,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP
// If the connection was our scan, we are no longer scanning.
if (mScanning && mScanning == peer)
{
//Log(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort;
//cLog(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort;
mScanning = Peer::pointer(); // No longer scanning.
bScanRefresh = true; // Look for more to scan.
@@ -517,13 +520,13 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP
if (itIp == mIpMap.end())
{
// Did not find it. Not already connecting or connected.
Log(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
cLog(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
// XXX Internal error.
}
else if (mIpMap[ipPeer] == peer)
{
// We were the identified connection.
//Log(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
//cLog(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
// Delete our entry.
mIpMap.erase(itIp);
@@ -533,7 +536,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP
else
{
// Found it. But, we were redundant.
//Log(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
//cLog(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
}
}
@@ -557,7 +560,7 @@ void ConnectionPool::peerVerified(Peer::ref peer)
std::string strIpPort = str(boost::format("%s %d") % strIp % iPort);
//Log(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort);
//cLog(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort);
if (peer->getNodePublic() == theApp->getWallet().getNodePublic())
{
@@ -604,7 +607,7 @@ void ConnectionPool::scanRefresh()
if (mScanning)
{
// Currently scanning, will scan again after completion.
Log(lsTRACE) << "Pool: Scan: already scanning";
cLog(lsTRACE) << "Pool: Scan: already scanning";
nothing();
}
@@ -641,7 +644,7 @@ void ConnectionPool::scanRefresh()
if (tpNow.is_not_a_date_time())
{
//Log(lsINFO) << "Pool: Scan: stop.";
//cLog(lsINFO) << "Pool: Scan: stop.";
(void) mScanTimer.cancel();
}
@@ -656,7 +659,7 @@ void ConnectionPool::scanRefresh()
tpNext = tpNow + boost::posix_time::seconds(iInterval);
//Log(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)")
//cLog(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)")
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
iInterval *= 2;
@@ -681,7 +684,7 @@ void ConnectionPool::scanRefresh()
}
else
{
//Log(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)")
//cLog(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)")
// % strIpPort % tpNext % (tpNext-tpNow).total_seconds());
mScanTimer.expires_at(tpNext);

View File

@@ -58,4 +58,4 @@ public:
int getDataSize() { return iDataSize; }
};
#endif
#endif

View File

@@ -155,14 +155,14 @@ void LCTransaction::setVote(const uint160& peer, bool votesYes)
}
else if (votesYes && !res.first->second)
{ // changes vote to yes
cLog(lsTRACE) << "Peer " << peer << " now votes YES on " << mTransactionID;
cLog(lsDEBUG) << "Peer " << peer << " now votes YES on " << mTransactionID;
--mNays;
++mYays;
res.first->second = true;
}
else if (!votesYes && res.first->second)
{ // changes vote to no
cLog(lsTRACE) << "Peer " << peer << " now votes NO on " << mTransactionID;
cLog(lsDEBUG) << "Peer " << peer << " now votes NO on " << mTransactionID;
++mNays;
--mYays;
res.first->second = false;
@@ -216,7 +216,7 @@ bool LCTransaction::updateVote(int percentTime, bool proposing)
return false;
}
mOurVote = newPosition;
cLog(lsTRACE) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID;
cLog(lsDEBUG) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID;
return true;
}
@@ -239,6 +239,7 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou
cLog(lsINFO) << "Entering consensus process, validating";
mValidating = true;
mProposing = theApp->getOPs().getOperatingMode() == NetworkOPs::omFULL;
mValPublic = NewcoinAddress::createNodePublic(mValSeed);
}
else
{
@@ -246,11 +247,16 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou
mProposing = mValidating = false;
}
handleLCL(prevLCLHash);
mHaveCorrectLCL = (mPreviousLedger->getHash() == mPrevLedgerHash);
if (!mHaveCorrectLCL)
{
cLog(lsINFO) << "Entering consensus with: " << previousLedger->getHash();
cLog(lsINFO) << "Correct LCL is: " << prevLCLHash;
handleLCL(mPrevLedgerHash);
if (!mHaveCorrectLCL)
{
mProposing = mValidating = false;
cLog(lsINFO) << "Entering consensus with: " << previousLedger->getHash();
cLog(lsINFO) << "Correct LCL is: " << prevLCLHash;
}
}
}
@@ -356,7 +362,7 @@ void LedgerConsensus::handleLCL(const uint256& lclHash)
}
}
cLog(lsINFO) << "Acquired the consensus ledger " << mPrevLedgerHash;
cLog(lsINFO) << "Have the consensus ledger " << mPrevLedgerHash;
mHaveCorrectLCL = true;
mAcquiringLedger = LedgerAcquire::pointer();
theApp->getOPs().clearNeedNetworkLedger();
@@ -556,9 +562,9 @@ void LedgerConsensus::stateEstablish()
updateOurPositions();
if (!mHaveCloseTimeConsensus)
{
tLog(haveConsensus(), lsINFO) << "We have TX consensus but not CT consensus";
tLog(haveConsensus(false), lsINFO) << "We have TX consensus but not CT consensus";
}
else if (haveConsensus())
else if (haveConsensus(true))
{
cLog(lsINFO) << "Converge cutoff (" << mPeerPositions.size() << " participants)";
mState = lcsFINISHED;
@@ -715,7 +721,7 @@ void LedgerConsensus::updateOurPositions()
}
}
bool LedgerConsensus::haveConsensus()
bool LedgerConsensus::haveConsensus(bool forReal)
{ // FIXME: Should check for a supermajority on each disputed transaction
// counting unacquired TX sets as disagreeing
int agree = 0, disagree = 0;
@@ -732,12 +738,10 @@ bool LedgerConsensus::haveConsensus()
}
int currentValidations = theApp->getValidations().getNodesAfter(mPrevLedgerHash);
#ifdef LC_DEBUG
cLog(lsINFO) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree;
#endif
cLog(lsDEBUG) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree;
return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations,
mPreviousMSeconds, mCurrentMSeconds);
mPreviousMSeconds, mCurrentMSeconds, forReal);
}
SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool doAcquire)
@@ -812,7 +816,8 @@ void LedgerConsensus::startAcquiring(const TransactionAcquire::pointer& acquire)
void LedgerConsensus::propose()
{
cLog(lsTRACE) << "We propose: " << mOurPosition->getCurrentHash();
cLog(lsTRACE) << "We propose: " <<
(mOurPosition->isBowOut() ? std::string("bowOut") : mOurPosition->getCurrentHash().GetHex());
ripple::TMProposeSet prop;
prop.set_currenttxhash(mOurPosition->getCurrentHash().begin(), 256 / 8);
@@ -830,7 +835,7 @@ void LedgerConsensus::propose()
void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vector<unsigned char>& tx)
{
cLog(lsTRACE) << "Transaction " << txID << " is disputed";
cLog(lsDEBUG) << "Transaction " << txID << " is disputed";
boost::unordered_map<uint256, LCTransaction::pointer>::iterator it = mDisputes.find(txID);
if (it != mDisputes.end())
return;
@@ -910,7 +915,7 @@ bool LedgerConsensus::peerPosition(const LedgerProposal::pointer& newPosition)
it.second->setVote(peerID, set->hasItem(it.first));
}
else
cLog(lsTRACE) << "Don't have that tx set";
cLog(lsDEBUG) << "Don't have that tx set";
return true;
}
@@ -1139,9 +1144,11 @@ void LedgerConsensus::accept(SHAMap::ref set)
statusChange(ripple::neACCEPTED_LEDGER, *newLCL);
if (mValidating)
{
uint256 signingHash;
SerializedValidation::pointer v = boost::make_shared<SerializedValidation>
(newLCLHash, theApp->getOPs().getValidationTimeNC(), mValSeed, mProposing);
(newLCLHash, theApp->getOPs().getValidationTimeNC(), mValSeed, mProposing, boost::ref(signingHash));
v->setTrusted();
theApp->isNew(signingHash); // suppress it if we receive it
theApp->getValidations().addValidation(v);
std::vector<unsigned char> validation = v->getSigned();
ripple::TMValidation val;

View File

@@ -87,7 +87,7 @@ protected:
Ledger::pointer mPreviousLedger;
LedgerAcquire::pointer mAcquiringLedger;
LedgerProposal::pointer mOurPosition;
NewcoinAddress mValSeed;
NewcoinAddress mValSeed, mValPublic;
bool mProposing, mValidating, mHaveCorrectLCL;
int mCurrentMSeconds, mClosePercent, mCloseResolution;
@@ -174,7 +174,7 @@ public:
void stateFinished();
void stateAccepted();
bool haveConsensus();
bool haveConsensus(bool forReal);
bool peerPosition(const LedgerProposal::pointer&);
@@ -183,6 +183,8 @@ public:
bool peerGaveNodes(Peer::ref peer, const uint256& setHash,
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
bool isOurPubKey(const NewcoinAddress &k) { return k == mValPublic; }
// test/debug
void simulate();
};

View File

@@ -6,6 +6,7 @@
#include <boost/format.hpp>
#include "Log.h"
SETUP_LOG();
// NOTE: First and last times must be repeated
int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 };
@@ -23,7 +24,7 @@ bool ContinuousLedgerTiming::shouldClose(
if ((previousMSeconds < -1000) || (previousMSeconds > 600000) ||
(currentMSeconds < -1000) || (currentMSeconds > 600000))
{
Log(lsWARNING) <<
cLog(lsWARNING) <<
boost::str(boost::format("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)")
% (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed
% currentMSeconds % previousMSeconds);
@@ -34,14 +35,14 @@ bool ContinuousLedgerTiming::shouldClose(
{ // no transactions so far this interval
if (proposersClosed > (previousProposers / 4)) // did we miss a transaction?
{
Log(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, "
cLog(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, "
<< previousProposers << " before)";
return true;
}
#if 0 // This false triggers on the genesis ledger
if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close
{
Log(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")";
cLog(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")";
if (previousMSeconds < 2000)
return previousMSeconds;
return previousMSeconds - 1000;
@@ -59,12 +60,14 @@ bool ContinuousLedgerTiming::haveConsensus(
int previousProposers, // proposers in the last closing (not including us)
int currentProposers, // proposers in this closing so far (not including us)
int currentAgree, // proposers who agree with us
int currentClosed, // proposers who have currently closed their ledgers
int currentFinished, // proposers who have validated a ledger after this one
int previousAgreeTime, // how long it took to agree on the last ledger
int currentAgreeTime) // how long we've been trying to agree
int currentAgreeTime, // how long we've been trying to agree
bool forReal) // deciding whether to stop consensus process
{
Log(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d closed=%d time=%d/%d") %
currentProposers % previousProposers % currentAgree % currentClosed % currentAgreeTime % previousAgreeTime);
cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") %
currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime %
(forReal ? "" : "X"));
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS)
return false;
@@ -73,7 +76,7 @@ bool ContinuousLedgerTiming::haveConsensus(
{ // Less than 3/4 of the last ledger's proposers are present, we may need more time
if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS))
{
Log(lsTRACE) << "too fast, not enough proposers";
tLog(forReal, lsTRACE) << "too fast, not enough proposers";
return false;
}
}
@@ -81,19 +84,19 @@ bool ContinuousLedgerTiming::haveConsensus(
// If 80% of current proposers (plus us) agree on a set, we have consensus
if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80)
{
Log(lsTRACE) << "normal consensus";
tLog(forReal, lsINFO) << "normal consensus";
return true;
}
// If 50% of the nodes on your UNL (minus us) have closed, you should close
if (((currentClosed * 100 - 100) / (currentProposers + 1)) > 50)
// If 50% of the nodes on your UNL have moved on, you should declare consensus
if (((currentFinished * 100) / (currentProposers + 1)) > 50)
{
Log(lsTRACE) << "many closers";
tLog(forReal, lsWARNING) << "We see no consensus, but 50% of nodes have moved on";
return true;
}
// no consensus yet
Log(lsTRACE) << "no consensus";
tLog(forReal, lsTRACE) << "no consensus";
return false;
}

View File

@@ -65,7 +65,8 @@ public:
static bool haveConsensus(
int previousProposers, int currentProposers,
int currentAgree, int currentClosed,
int previousAgreeTime, int currentAgreeTime);
int previousAgreeTime, int currentAgreeTime,
bool forReal);
static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq);
};

View File

@@ -712,6 +712,12 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, cons
return mMode != omFULL;
}
if (mConsensus->isOurPubKey(naPeerPublic))
{
cLog(lsTRACE) << "Received our own validation";
return false;
}
// Is this node on our UNL?
if (!theApp->getUNL().nodeInUNL(naPeerPublic))
{

View File

@@ -758,11 +758,9 @@ void Peer::recvValidation(ripple::TMValidation& packet)
return;
}
// The four #ifndef/#endif's are commented out temporarily to avoid
// an update hassle. They can be removed once all nodes are running this code
//#ifndef TRUST_NETWORK
#ifndef TRUST_NETWORK
try
//#endif
#endif
{
Serializer s(packet.validation());
SerializerIterator sit(s);
@@ -788,13 +786,13 @@ void Peer::recvValidation(ripple::TMValidation& packet)
theApp->getConnectionPool().relayMessage(this, message);
}
}
//#ifndef TRUST_NETWORK
#ifndef TRUST_NETWORK
catch (...)
{
cLog(lsWARNING) << "Exception processing validation";
punishPeer(PP_UNKNOWN_REQUEST);
}
//#endif
#endif
}
void Peer::recvGetValidation(ripple::TMGetValidations& packet)

View File

@@ -2647,7 +2647,7 @@ Json::Value RPCServer::doLogin(const Json::Value& params)
}
}
Json::Value RPCServer::doLogSeverity(const Json::Value& params)
Json::Value RPCServer::doLogLevel(const Json::Value& params)
{
if (params.size() == 0)
{ // get log severities
@@ -2666,10 +2666,7 @@ Json::Value RPCServer::doLogSeverity(const Json::Value& params)
{ // set base log severity
LogSeverity sv = Log::stringToSeverity(params[0u].asString());
if (sv == lsINVALID)
{
Log(lsWARNING) << "Unable to parse severity: " << params[0u].asString();
return RPCError(rpcINVALID_PARAMS);
}
Log::setMinSeverity(sv);
return RPCError(rpcSUCCESS);
}
@@ -2721,8 +2718,8 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params
{ "data_fetch", &RPCServer::doDataFetch, 1, 1, true },
{ "data_store", &RPCServer::doDataStore, 2, 2, true },
{ "ledger", &RPCServer::doLedger, 0, 2, false, optNetwork },
{ "log_level", &RPCServer::doLogLevel, 0, 2, true },
{ "logrotate", &RPCServer::doLogRotate, 0, 0, true },
{ "logseverity", &RPCServer::doLogSeverity, 0, 2, true },
{ "nickname_info", &RPCServer::doNicknameInfo, 1, 1, false, optCurrent },
{ "nickname_set", &RPCServer::doNicknameSet, 2, 3, false, optCurrent },
{ "offer_create", &RPCServer::doOfferCreate, 9, 10, false, optCurrent },

View File

@@ -155,7 +155,7 @@ private:
Json::Value doServerInfo(const Json::Value& params);
Json::Value doSessionClose(const Json::Value& params);
Json::Value doSessionOpen(const Json::Value& params);
Json::Value doLogSeverity(const Json::Value& params);
Json::Value doLogLevel(const Json::Value& params);
Json::Value doStop(const Json::Value& params);
Json::Value doTransitSet(const Json::Value& params);
Json::Value doTx(const Json::Value& params);

View File

@@ -1400,30 +1400,30 @@ TER PathState::pushImply(
// Currency is different, need to convert via an offer.
terResult = pushNode(
STPathElement::typeCurrency // Offer.
| STPathElement::typeIssuer,
ACCOUNT_ONE, // Placeholder for offers.
uCurrencyID, // The offer's output is what is now wanted.
uIssuerID);
STPathElement::typeCurrency // Offer.
| STPathElement::typeIssuer,
ACCOUNT_ONE, // Placeholder for offers.
uCurrencyID, // The offer's output is what is now wanted.
uIssuerID);
}
// For ripple, non-stamps, ensure the issuer is on at least one side of the transaction.
if (tesSUCCESS == terResult
&& !!uCurrencyID // Not stamps.
&& (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs.
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
&& !!uCurrencyID // Not stamps.
&& (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs.
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
{
// Need to ripple through uIssuerID's account.
terResult = pushNode(
STPathElement::typeAccount,
uIssuerID, // Intermediate account is the needed issuer.
uCurrencyID,
uIssuerID);
STPathElement::typeAccount,
uIssuerID, // Intermediate account is the needed issuer.
uCurrencyID,
uIssuerID);
}
cLog(lsINFO) << "pushImply< " << terResult;
cLog(lsDEBUG) << boost::str(boost::format("pushImply< : %s") % transToken(terResult));
return terResult;
}
@@ -1544,11 +1544,12 @@ TER PathState::pushNode(
vpnNodes.push_back(pnCur);
}
}
cLog(lsINFO) << "pushNode< " << terResult;
cLog(lsDEBUG) << boost::str(boost::format("pushNode< : %s") % transToken(terResult));
return terResult;
}
// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, or temBAD_PATH_LOOP
PathState::PathState(
const int iIndex,
const LedgerEntrySet& lesSource,
@@ -1886,7 +1887,7 @@ TER RippleCalc::rippleCalc(
if (pspDirect)
{
// Return if malformed.
if (pspDirect->terStatus >= temMALFORMED && pspDirect->terStatus < tefFAILURE)
if (isTemMalformed(pspDirect->terStatus))
return pspDirect->terStatus;
if (tesSUCCESS == pspDirect->terStatus)
@@ -1917,14 +1918,11 @@ TER RippleCalc::rippleCalc(
if (pspExpanded)
{
// Return if malformed.
if (pspExpanded->terStatus >= temMALFORMED && pspExpanded->terStatus < tefFAILURE)
if (isTemMalformed(pspExpanded->terStatus))
return pspExpanded->terStatus;
if (tesSUCCESS == pspExpanded->terStatus)
{
// Had a success.
terResult = tesSUCCESS;
}
terResult = tesSUCCESS; // Had a success.
vpsPaths.push_back(pspExpanded);
}
@@ -1932,7 +1930,8 @@ TER RippleCalc::rippleCalc(
if (vpsPaths.empty())
{
return tefEXCEPTION;
// No paths. Missing credit lines.
return terNO_LINE;
}
else if (tesSUCCESS != terResult)
{

View File

@@ -151,3 +151,5 @@
FIELD(Template, ARRAY, 5)
FIELD(Necessary, ARRAY, 6)
FIELD(Sufficient, ARRAY, 7)
// vim:ts=4

View File

@@ -37,7 +37,7 @@ SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSi
}
SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 signTime,
const NewcoinAddress& naSeed, bool isFull)
const NewcoinAddress& naSeed, bool isFull, uint256& signingHash)
: STObject(sValidationFormat, sfValidation), mTrusted(false)
{
setFieldH256(sfLedgerHash, ledgerHash);
@@ -52,8 +52,9 @@ SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 sig
if (!isFull)
setFlag(sFullFlag);
signingHash = getSigningHash();
std::vector<unsigned char> signature;
NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), signature);
NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(signingHash, signature);
setFieldVL(sfSignature, signature);
// XXX Check if this can fail.
// if (!NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue()))

View File

@@ -21,7 +21,8 @@ public:
// These throw if the object is not valid
SerializedValidation(SerializerIterator& sit, bool checkSignature = true);
SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull);
SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull,
uint256& signingHash);
uint256 getLedgerHash() const;
uint32 getSignTime() const;

View File

@@ -1136,24 +1136,37 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe
TER TransactionEngine::doOfferCancel(const SerializedTransaction& txn)
{
TER terResult;
const uint32 uSequence = txn.getFieldU32(sfOfferSequence);
const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence);
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
const uint32 uOfferSequence = txn.getFieldU32(sfOfferSequence);
const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32(sfSequence);
if (sleOffer)
Log(lsDEBUG) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
if (!uOfferSequence || uAccountSequenceNext-1 <= uOfferSequence)
{
Log(lsWARNING) << "doOfferCancel: uSequence=" << uSequence;
Log(lsINFO) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID);
terResult = temBAD_SEQUENCE;
}
else
{
Log(lsWARNING) << "doOfferCancel: offer not found: "
<< NewcoinAddress::createHumanAccountID(mTxnAccountID)
<< " : " << uSequence
<< " : " << uOfferIndex.ToString();
const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uOfferSequence);
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
terResult = terOFFER_NOT_FOUND;
if (sleOffer)
{
Log(lsWARNING) << "doOfferCancel: uOfferSequence=" << uOfferSequence;
terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID);
}
else
{
Log(lsWARNING) << "doOfferCancel: offer not found: "
<< NewcoinAddress::createHumanAccountID(mTxnAccountID)
<< " : " << uOfferSequence
<< " : " << uOfferIndex.ToString();
terResult = tesSUCCESS;
}
}
return terResult;
@@ -1179,7 +1192,6 @@ TER TransactionEngine::doContractAdd(const SerializedTransaction& txn)
// place contract in ledger
// run create code
if (mLedger->getParentCloseTimeNC() >= expiration)
{

View File

@@ -33,6 +33,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
{ temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: Bad publish." },
{ temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" },
{ temBAD_SET_ID, "temBAD_SET_ID", "Malformed." },
{ temBAD_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence in not in the past." },
{ temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." },
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
{ temDST_NEEDED, "temDST_NEEDED", "Destination not specified." },
@@ -53,7 +54,6 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
{ terNO_DST, "terNO_DST", "The destination does not exist." },
{ terNO_LINE, "terNO_LINE", "No such line." },
{ terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." },
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." },
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
{ terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." },

View File

@@ -34,6 +34,7 @@ enum TER // aka TransactionEngineResult
temBAD_PATH_LOOP,
temBAD_PUBLISH,
temBAD_TRANSFER_RATE,
temBAD_SEQUENCE,
temBAD_SET_ID,
temCREATEXNS,
temDST_IS_SRC,
@@ -83,7 +84,6 @@ enum TER // aka TransactionEngineResult
terNO_DST,
terNO_LINE,
terNO_LINE_NO_ZERO,
terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure.
terPRE_SEQ,
terSET_MISSING_DST,
terUNFUNDED,

View File

@@ -9,6 +9,8 @@
SETUP_LOG();
typedef std::pair<const uint160, SerializedValidation::pointer> u160_val_pair;
bool ValidationCollection::addValidation(const SerializedValidation::pointer& val)
{
NewcoinAddress signer = val->getSignerPublic();
@@ -121,10 +123,9 @@ int ValidationCollection::getNodesAfter(const uint256& ledger)
{ // Number of trusted nodes that have moved past this ledger
int count = 0;
boost::mutex::scoped_lock sl(mValidationLock);
for (boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin(),
end = mCurrentValidations.end(); it != end; ++it)
BOOST_FOREACH(u160_val_pair& it, mCurrentValidations)
{
if (it->second->isTrusted() && it->second->isPreviousHash(ledger))
if (it.second->isTrusted() && it.second->isPreviousHash(ledger))
++count;
}
return count;
@@ -136,12 +137,11 @@ int ValidationCollection::getLoadRatio(bool overLoaded)
int badNodes = overLoaded ? 0 : 1;
{
boost::mutex::scoped_lock sl(mValidationLock);
for (boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin(),
end = mCurrentValidations.end(); it != end; ++it)
BOOST_FOREACH(u160_val_pair& it, mCurrentValidations)
{
if (it->second->isTrusted())
if (it.second->isTrusted())
{
if (it->second->isFull())
if (it.second->isFull())
++goodNodes;
else
++badNodes;
@@ -190,14 +190,13 @@ ValidationCollection::getCurrentValidations(uint256 currentLedger)
void ValidationCollection::flush()
{
boost::mutex::scoped_lock sl(mValidationLock);
boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin();
bool anyNew = false;
while (it != mCurrentValidations.end())
boost::mutex::scoped_lock sl(mValidationLock);
BOOST_FOREACH(u160_val_pair& it, mCurrentValidations)
{
if (it->second)
mStaleValidations.push_back(it->second);
++it;
if (it.second)
mStaleValidations.push_back(it.second);
anyNew = true;
}
mCurrentValidations.clear();
@@ -243,7 +242,6 @@ void ValidationCollection::doWrite()
% db->escape(strCopy(it->getSignature()))));
db->executeSQL("END TRANSACTION;");
}
sl.lock();
}
mWriting = false;

View File

@@ -637,7 +637,8 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq
{
NewcoinAddress naAccount;
if (!naAccount.setAccountID(jvRequest["account_root"].asString()))
if (!naAccount.setAccountID(jvRequest["account_root"].asString())
|| !naAccount.getAccountID())
{
jvResult["error"] = "malformedAddress";
}
@@ -749,18 +750,27 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq
NewcoinAddress naA;
NewcoinAddress naB;
uint160 uCurrency;
Json::Value jvRippleState = jvRequest["ripple_state"];
if (!jvRippleState.isMember("accounts")
|| !jvRippleState.isMember("currency")
|| !jvRippleState["accounts"].isArray()
|| 2 != jvRippleState["accounts"].size()) {
cLog(lsINFO)
<< boost::str(boost::format("ledger_entry: ripple_state: accounts: %d currency: %d array=%d, size=%d")
% jvRippleState.isMember("accounts")
% jvRippleState.isMember("currency")
% jvRippleState["accounts"].isArray()
% jvRippleState["accounts"].size());
if (!jvRequest.isMember("accounts")
|| !jvRequest.isMember("currency")
|| !jvRequest["accounts"].isArray()
|| 2 != jvRequest["accounts"].size()) {
jvResult["error"] = "malformedRequest";
}
else if (!naA.setAccountID(jvRequest["accounts"][0u].asString())
|| !naB.setAccountID(jvRequest["accounts"][1u].asString())) {
else if (!naA.setAccountID(jvRippleState["accounts"][0u].asString())
|| !naB.setAccountID(jvRippleState["accounts"][1u].asString())) {
jvResult["error"] = "malformedAddress";
}
else if (!STAmount::currencyFromString(uCurrency, jvRequest["currency"].asString())) {
else if (!STAmount::currencyFromString(uCurrency, jvRippleState["currency"].asString())) {
jvResult["error"] = "malformedCurrency";
}
else

265
test/offer-test.js Normal file
View File

@@ -0,0 +1,265 @@
var async = require("async");
var buster = require("buster");
var fs = require("fs");
var server = require("./server.js");
var remote = require("../js/remote.js");
var config = require("./config.js");
var Amount = require("../js/amount.js").Amount;
require("../js/amount.js").setAccounts(config.accounts);
buster.testRunner.timeout = 5000;
var alpha;
buster.testCase("Work in progress", {
'setUp' :
function (done) {
server.start("alpha",
function (e) {
buster.refute(e);
alpha = remote.remoteConfig(config, "alpha", 'TRACE');
alpha
.once('ledger_closed', done)
.connect();
}
// , 'MOCK'
);
},
'tearDown' :
function (done) {
alpha
.on('disconnected', function () {
server.stop("alpha", function (e) {
buster.refute(e);
done();
});
})
.connect(false);
},
"offer create then cancel in one ledger" :
function (done) {
var final_create;
async.waterfall([
function (callback) {
alpha.transaction()
.offer_create("root", "500", "100/USD/root")
.on("proposed", function (m) {
console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS', m);
})
.on("final", function (m) {
console.log("FINAL: offer_create: %s", JSON.stringify(m));
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
final_create = m;
})
.submit();
},
function (m, callback) {
alpha.transaction()
.offer_cancel("root", m.transaction.Sequence)
.on("proposed", function (m) {
console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS', m);
})
.on("final", function (m) {
console.log("FINAL: offer_cancel: %s", JSON.stringify(m));
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
buster.assert(final_create);
done();
})
.submit();
},
function (m, callback) {
alpha
.once("ledger_closed", function (ledger_closed, ledger_closed_index) {
console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed);
})
.ledger_accept();
}
], function (error) {
console.log("result: error=%s", error);
buster.refute(error);
if (error) done();
});
},
"offer_create then ledger_accept then offer_cancel then ledger_accept." :
function (done) {
var final_create;
var offer_seq;
async.waterfall([
function (callback) {
alpha.transaction()
.offer_create("root", "500", "100/USD/root")
.on("proposed", function (m) {
console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
offer_seq = m.transaction.Sequence;
callback(m.result != 'tesSUCCESS');
})
.on("final", function (m) {
console.log("FINAL: offer_create: %s", JSON.stringify(m));
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
final_create = m;
callback();
})
.submit();
},
function (callback) {
if (!final_create) {
alpha
.once("ledger_closed", function (ledger_closed, ledger_closed_index) {
console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed);
})
.ledger_accept();
}
else {
callback();
}
},
function (callback) {
console.log("CANCEL: offer_cancel: %d", offer_seq);
alpha.transaction()
.offer_cancel("root", offer_seq)
.on("proposed", function (m) {
console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS');
})
.on("final", function (m) {
console.log("FINAL: offer_cancel: %s", JSON.stringify(m));
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
buster.assert(final_create);
done();
})
.submit();
},
// See if ledger_accept will crash.
function (callback) {
alpha
.once("ledger_closed", function (ledger_closed, ledger_closed_index) {
console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed);
callback();
})
.ledger_accept();
},
function (callback) {
alpha
.once("ledger_closed", function (ledger_closed, ledger_closed_index) {
console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed);
callback();
})
.ledger_accept();
},
], function (error) {
console.log("result: error=%s", error);
buster.refute(error);
if (error) done();
});
},
"offer cancel past and future sequence" :
function (done) {
var final_create;
async.waterfall([
function (callback) {
alpha.transaction()
.payment('root', 'alice', Amount.from_json("10000"))
.flags('CreateAccount')
.on("proposed", function (m) {
console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS', m);
})
.on('error', function(m) {
console.log("error: %s", m);
buster.assert(false);
callback(m);
})
.submit();
},
// Past sequence but wrong
function (m, callback) {
alpha.transaction()
.offer_cancel("root", m.transaction.Sequence)
.on("proposed", function (m) {
console.log("PROPOSED: offer_cancel past: %s", JSON.stringify(m));
callback(m.result != 'tesSUCCESS', m);
})
.submit();
},
// Same sequence
function (m, callback) {
alpha.transaction()
.offer_cancel("root", m.transaction.Sequence+1)
.on("proposed", function (m) {
console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m));
callback(m.result != 'temBAD_SEQUENCE', m);
})
.submit();
},
// Future sequence
function (m, callback) {
// After a malformed transaction, need to recover correct sequence.
alpha.set_account_seq("root", alpha.account_seq("root")-1);
alpha.transaction()
.offer_cancel("root", m.transaction.Sequence+2)
.on("proposed", function (m) {
console.log("ERROR: offer_cancel future: %s", JSON.stringify(m));
callback(m.result != 'temBAD_SEQUENCE');
})
.submit();
},
// See if ledger_accept will crash.
function (callback) {
alpha
.once("ledger_closed", function (ledger_closed, ledger_closed_index) {
console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed);
callback();
})
.ledger_accept();
},
function (callback) {
alpha
.once("ledger_closed", function (ledger_closed, ledger_closed_index) {
console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed);
callback();
})
.ledger_accept();
},
function (callback) {
callback();
}
], function (error) {
console.log("result: error=%s", error);
buster.refute(error);
done();
});
},
});
// vim:sw=2:sts=2:ts=8

View File

@@ -103,6 +103,7 @@ buster.testCase("Remote functions", {
.request();
},
// XXX This should be detected locally.
'account_root remote malformedAddress' :
function (done) {
alpha.request_ledger_closed().on('success', function (r) {

View File

@@ -20,7 +20,6 @@
#
[validators]
n94rSdgTyBNGvYg8pZXGuNt59Y5bGAZGxbxyvjDaqD9ceRAgD85P first
n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second
n9KPnVLn7ewVzHvn218DcEYsnWLzKerTDwhpofhk4Ym1RUq4TeGw first
n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second
n94rSdgTyBNGvYg8pZXGuNt59Y5bGAZGxbxyvjDaqD9ceRAgD85P third
n94365hzFKrkgCULeJwczs3kwzpri3KVHkfhUWGT4MjmbEbC5xBy