mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-02 08:55:53 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
Conflicts: validators.txt
This commit is contained in:
102
js/amount.js
102
js/amount.js
@@ -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;
|
||||
|
||||
89
js/remote.js
89
js/remote.js
@@ -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)
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -58,4 +58,4 @@ public:
|
||||
int getDataSize() { return iDataSize; }
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
10
src/Peer.cpp
10
src/Peer.cpp
@@ -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)
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -151,3 +151,5 @@
|
||||
FIELD(Template, ARRAY, 5)
|
||||
FIELD(Necessary, ARRAY, 6)
|
||||
FIELD(Sufficient, ARRAY, 7)
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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." },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
265
test/offer-test.js
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#
|
||||
|
||||
[validators]
|
||||
n94rSdgTyBNGvYg8pZXGuNt59Y5bGAZGxbxyvjDaqD9ceRAgD85P first
|
||||
n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second
|
||||
n9KPnVLn7ewVzHvn218DcEYsnWLzKerTDwhpofhk4Ym1RUq4TeGw first
|
||||
n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second
|
||||
n94rSdgTyBNGvYg8pZXGuNt59Y5bGAZGxbxyvjDaqD9ceRAgD85P third
|
||||
n94365hzFKrkgCULeJwczs3kwzpri3KVHkfhUWGT4MjmbEbC5xBy
|
||||
|
||||
Reference in New Issue
Block a user