identify and handle bug in monero core: failed tx in pool (after

testUpdateLockedDifferentAccounts()) associated with non-originating
saved wallets returning duplicate incoming transfers but one has
amount/numSuggestedConfirmations of 0.  Resolved by rescan_bc;
MoneroUtils::reconcile() supports optional custom error message for
debugging
This commit is contained in:
woodser
2019-07-20 18:22:53 -04:00
parent 26ea3fa2b7
commit fcf9da67a3
3 changed files with 51 additions and 48 deletions
+25 -25
View File
@@ -113,22 +113,22 @@ namespace monero {
if (this != self.get()) throw runtime_error("this != self");
if (self == other) return;
id = MoneroUtils::reconcile(id, other->id);
height = MoneroUtils::reconcile(height, other->height, boost::none, boost::none, true); // height can increase
timestamp = MoneroUtils::reconcile(timestamp, other->timestamp, boost::none, boost::none, true); // timestamp can increase
size = MoneroUtils::reconcile(size, other->size);
weight = MoneroUtils::reconcile(weight, other->weight);
longTermWeight = MoneroUtils::reconcile(longTermWeight, other->longTermWeight);
depth = MoneroUtils::reconcile(depth, other->depth);
difficulty = MoneroUtils::reconcile(difficulty, other->difficulty);
cumulativeDifficulty = MoneroUtils::reconcile(cumulativeDifficulty, other->cumulativeDifficulty);
majorVersion = MoneroUtils::reconcile(majorVersion, other->majorVersion);
minorVersion = MoneroUtils::reconcile(minorVersion, other->minorVersion);
nonce = MoneroUtils::reconcile(nonce, other->nonce);
height = MoneroUtils::reconcile(height, other->height, boost::none, boost::none, true, "block height"); // height can increase
timestamp = MoneroUtils::reconcile(timestamp, other->timestamp, boost::none, boost::none, true, "block header timestamp"); // timestamp can increase
size = MoneroUtils::reconcile(size, other->size, "block header size");
weight = MoneroUtils::reconcile(weight, other->weight, "block header weight");
longTermWeight = MoneroUtils::reconcile(longTermWeight, other->longTermWeight, "block header long term weight");
depth = MoneroUtils::reconcile(depth, other->depth, "block header depth");
difficulty = MoneroUtils::reconcile(difficulty, other->difficulty, "difficulty");
cumulativeDifficulty = MoneroUtils::reconcile(cumulativeDifficulty, other->cumulativeDifficulty, "cumulativeDifficulty");
majorVersion = MoneroUtils::reconcile(majorVersion, other->majorVersion, "majorVersion");
minorVersion = MoneroUtils::reconcile(minorVersion, other->minorVersion, "minorVersion");
nonce = MoneroUtils::reconcile(nonce, other->nonce, "nonce");
coinbaseTxId = MoneroUtils::reconcile(coinbaseTxId, other->coinbaseTxId);
numTxs = MoneroUtils::reconcile(numTxs, other->numTxs);
numTxs = MoneroUtils::reconcile(numTxs, other->numTxs, "block header numTxs");
orphanStatus = MoneroUtils::reconcile(orphanStatus, other->orphanStatus);
prevId = MoneroUtils::reconcile(prevId, other->prevId);
reward = MoneroUtils::reconcile(reward, other->reward);
reward = MoneroUtils::reconcile(reward, other->reward, "block header reward");
powHash = MoneroUtils::reconcile(powHash, other->powHash);
}
@@ -249,8 +249,8 @@ namespace monero {
id = MoneroUtils::reconcile(id, other->id);
version = MoneroUtils::reconcile(version, other->version);
paymentId = MoneroUtils::reconcile(paymentId, other->paymentId);
fee = MoneroUtils::reconcile(fee, other->fee);
mixin = MoneroUtils::reconcile(mixin, other->mixin);
fee = MoneroUtils::reconcile(fee, other->fee, "tx fee");
mixin = MoneroUtils::reconcile(mixin, other->mixin, "tx mixin");
isConfirmed = MoneroUtils::reconcile(isConfirmed, other->isConfirmed);
doNotRelay = MoneroUtils::reconcile(doNotRelay, other->doNotRelay);
isRelayed = MoneroUtils::reconcile(isRelayed, other->isRelayed);
@@ -260,8 +260,8 @@ namespace monero {
prunedHex = MoneroUtils::reconcile(prunedHex, other->prunedHex);
prunableHex = MoneroUtils::reconcile(prunableHex, other->prunableHex);
prunableHash = MoneroUtils::reconcile(prunableHash, other->prunableHash);
size = MoneroUtils::reconcile(size, other->size);
weight = MoneroUtils::reconcile(weight, other->weight);
size = MoneroUtils::reconcile(size, other->size, "tx size");
weight = MoneroUtils::reconcile(weight, other->weight, "tx weight");
//outputIndices = MoneroUtils::reconcile(outputIndices, other->outputIndices); // TODO
metadata = MoneroUtils::reconcile(metadata, other->metadata);
commonTxSets = MoneroUtils::reconcile(commonTxSets, other->commonTxSets);
@@ -270,13 +270,13 @@ namespace monero {
rctSigPrunable = MoneroUtils::reconcile(rctSigPrunable, other->rctSigPrunable);
isKeptByBlock = MoneroUtils::reconcile(isKeptByBlock, other->isKeptByBlock);
isFailed = MoneroUtils::reconcile(isFailed, other->isFailed);
lastFailedHeight = MoneroUtils::reconcile(lastFailedHeight, other->lastFailedHeight);
lastFailedHeight = MoneroUtils::reconcile(lastFailedHeight, other->lastFailedHeight, "lastFailedHeight");
lastFailedId = MoneroUtils::reconcile(lastFailedId, other->lastFailedId);
maxUsedBlockHeight = MoneroUtils::reconcile(maxUsedBlockHeight, other->maxUsedBlockHeight);
maxUsedBlockHeight = MoneroUtils::reconcile(maxUsedBlockHeight, other->maxUsedBlockHeight, "maxUsedBlockHeight");
maxUsedBlockId = MoneroUtils::reconcile(maxUsedBlockId, other->maxUsedBlockId);
//signatures = MoneroUtils::reconcile(signatures, other->signatures); // TODO
unlockTime = MoneroUtils::reconcile(unlockTime, other->unlockTime);
numConfirmations = MoneroUtils::reconcile(numConfirmations, other->numConfirmations);
unlockTime = MoneroUtils::reconcile(unlockTime, other->unlockTime, "unlockTime");
numConfirmations = MoneroUtils::reconcile(numConfirmations, other->numConfirmations, "numConfirmations");
// merge vins
if (!other->vins.empty()) {
@@ -349,8 +349,8 @@ namespace monero {
lastRelayedTimestamp = boost::none;
} else {
inTxPool = MoneroUtils::reconcile(inTxPool, other->inTxPool, boost::none, true, boost::none); // unrelayed -> tx pool
receivedTimestamp = MoneroUtils::reconcile(receivedTimestamp, other->receivedTimestamp, boost::none, boost::none, false); // take earliest receive time
lastRelayedTimestamp = MoneroUtils::reconcile(lastRelayedTimestamp, other->lastRelayedTimestamp, boost::none, boost::none, true); // take latest relay time
receivedTimestamp = MoneroUtils::reconcile(receivedTimestamp, other->receivedTimestamp, boost::none, boost::none, false, "receivedTimestamp"); // take earliest receive time
lastRelayedTimestamp = MoneroUtils::reconcile(lastRelayedTimestamp, other->lastRelayedTimestamp, boost::none, boost::none, true, "lastRelayedTimestamp"); // take latest relay time
}
}
@@ -395,7 +395,7 @@ namespace monero {
// otherwise merge output fields
if (keyImage == boost::none) keyImage = other->keyImage;
else if (other->keyImage != boost::none) keyImage.get()->merge(keyImage.get(), other->keyImage.get());
amount = MoneroUtils::reconcile(amount, other->amount);
index = MoneroUtils::reconcile(index, other->index);
amount = MoneroUtils::reconcile(amount, other->amount, "output amount");
index = MoneroUtils::reconcile(index, other->index, "output index");
}
}
+14 -19
View File
@@ -108,7 +108,7 @@ namespace MoneroUtils
// TODO: refactor common template code
template <class T, typename std::enable_if<std::is_same<T, string>::value, T>::type* = nullptr>
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, boost::optional<bool> resolveDefined, boost::optional<bool> resolveTrue, boost::optional<bool> resolveMax) {
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, boost::optional<bool> resolveDefined, boost::optional<bool> resolveTrue, boost::optional<bool> resolveMax, const string& errMsg = "") {
// check for equality
if (val1 == val2) return val1;
@@ -119,16 +119,15 @@ namespace MoneroUtils
else return val1 == boost::none ? val2 : val1;
}
throw runtime_error("Cannot reconcile strings");
throw runtime_error(string("Cannot reconcile strings: ") + to_string(val1) + string(" vs ") + to_string(val2) + (!errMsg.empty() ? string(". ") + errMsg : string("")));
}
template <class T, typename std::enable_if<std::is_same<T, string>::value, T>::type* = nullptr>
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2) {
return reconcile(val1, val2, boost::none, boost::none, boost::none);
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, const string& errMsg = "") {
return reconcile(val1, val2, boost::none, boost::none, boost::none, errMsg);
}
template <class T, typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, boost::optional<bool> resolveDefined, boost::optional<bool> resolveTrue, boost::optional<bool> resolveMax) {
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, boost::optional<bool> resolveDefined, boost::optional<bool> resolveTrue, boost::optional<bool> resolveMax, const string& errMsg = "") {
// check for equality
if (val1 == val2) return val1;
@@ -145,16 +144,15 @@ namespace MoneroUtils
}
// throw runtime_error("Cannot reconcile values " + val1 + " and " + val2 + " with config: [" + resolveDefined + ", " + resolveTrue + ", " + resolveMax + "]", val1, val2);
throw runtime_error("Cannot reconcile integrals");
throw runtime_error(string("Cannot reconcile integrals: ") + to_string(val1) + string(" vs ") + to_string(val2) + (!errMsg.empty() ? string(". ") + errMsg : string("")));
}
template <class T, typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2) {
return reconcile(val1, val2, boost::none, boost::none, boost::none);
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, const string& errMsg = "") {
return reconcile(val1, val2, boost::none, boost::none, boost::none, errMsg);
}
template <class T, typename std::enable_if<std::is_same<T, bool>::value, T>::type>
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, boost::optional<bool> resolveDefined, boost::optional<bool> resolveTrue, boost::optional<bool> resolveMax) {
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, boost::optional<bool> resolveDefined, boost::optional<bool> resolveTrue, boost::optional<bool> resolveMax, const string& errMsg = "") {
// check for equality
if (val1 == val2) return val1;
@@ -169,19 +167,16 @@ namespace MoneroUtils
if (resolveTrue != boost::none) {
return val1 == resolveTrue ? val1 : val2; // if resolve true, return true, else return false
} else {
throw runtime_error("Cannot reconcile booleans");
throw runtime_error(string("Cannot reconcile booleans: ") + to_string(val1) + string(" vs ") + to_string(val2) + (!errMsg.empty() ? string(". ") + errMsg : string("")));
}
throw runtime_error("Cannot reconcile booleans");
}
template <class T, typename std::enable_if<std::is_same<T, bool>::value, T>::type>
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2) {
return reconcile(val1, val2, boost::none, boost::none, boost::none);
boost::optional<T> reconcile(const boost::optional<T>& val1, const boost::optional<T>& val2, const string& errMsg = "") {
return reconcile(val1, val2, boost::none, boost::none, boost::none, errMsg);
}
template <class T>
vector<T> reconcile(const vector<T>& v1, const vector<T>& v2) {
vector<T> reconcile(const vector<T>& v1, const vector<T>& v2, const string& errMsg = "") {
// check for equality
if (v1 == v2) return v1;
@@ -191,7 +186,7 @@ namespace MoneroUtils
if (v2.empty()) return v1;
// otherwise cannot reconcile
throw runtime_error("Cannot reconcile vectors");
throw runtime_error("Cannot reconcile vectors" + (!errMsg.empty() ? string(". ") + errMsg : string("")));
}
// template <class T>
+12 -4
View File
@@ -289,9 +289,17 @@ namespace monero {
}
// otherwise merge transfer fields
amount = MoneroUtils::reconcile(amount, other->amount);
accountIndex = MoneroUtils::reconcile(accountIndex, other->accountIndex);
numSuggestedConfirmations = MoneroUtils::reconcile(numSuggestedConfirmations, other->numSuggestedConfirmations, boost::none, boost::none, false);
accountIndex = MoneroUtils::reconcile(accountIndex, other->accountIndex, "acountIndex");
// TODO monero core: failed tx in pool (after testUpdateLockedDifferentAccounts()) causes non-originating saved wallets to return duplicate incoming transfers but one has amount/numSuggestedConfirmations of 0
if (amount != boost::none && other->amount != boost::none && *amount != *other->amount && (*amount == 0 || *other->amount == 0)) {
accountIndex = MoneroUtils::reconcile(accountIndex, other->accountIndex, boost::none, boost::none, true, "acountIndex");
numSuggestedConfirmations = MoneroUtils::reconcile(numSuggestedConfirmations, other->numSuggestedConfirmations, boost::none, boost::none, true, "numSuggestedConfirmations");
cout << "WARNING: failed tx in pool causes non-originating wallets to return duplicate incoming transfers but with one amount/numSuggestedConfirmations of 0" << endl;
} else {
amount = MoneroUtils::reconcile(amount, other->amount, "transfer amount");
numSuggestedConfirmations = MoneroUtils::reconcile(numSuggestedConfirmations, other->numSuggestedConfirmations, boost::none, boost::none, false, "numSuggestedConfirmations");
}
}
// ----------------------- MONERO INCOMING TRANSFER -------------------------
@@ -314,7 +322,7 @@ namespace monero {
//cout << "MoneroIncomingTransfer::merge" << endl;
if (self == other) return;
MoneroTransfer::merge(self, other);
subaddressIndex = MoneroUtils::reconcile(subaddressIndex, other->subaddressIndex);
subaddressIndex = MoneroUtils::reconcile(subaddressIndex, other->subaddressIndex, "incoming transfer subaddressIndex");
address = MoneroUtils::reconcile(address, other->address);
}