Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

#include "wallet/wallet.h"

#include "evo/deterministicmns.h"
#include "evo/evodb.h"
#include "evo/providertx.h"
#include "evo/specialtx.h"
#include "script/standard.h"

#include <set>
#include <stdint.h>
#include <utility>
Expand Down Expand Up @@ -363,6 +369,98 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
empty_wallet();
}*/

BOOST_AUTO_TEST_CASE(auto_lock_masternode_collaterals)
{
CWallet testWallet;
LOCK2(cs_main, testWallet.cs_wallet);

CKey ownedKey, watchedKey, foreignKey;
ownedKey.MakeNewKey(true);
watchedKey.MakeNewKey(true);
foreignKey.MakeNewKey(true);
BOOST_REQUIRE(testWallet.AddKeyPubKey(ownedKey, ownedKey.GetPubKey()));
const CScript ownedScript = GetScriptForDestination(ownedKey.GetPubKey().GetID());
const CScript watchedScript = GetScriptForDestination(watchedKey.GetPubKey().GetID());
const CScript foreignScript = GetScriptForDestination(foreignKey.GetPubKey().GetID());
BOOST_REQUIRE(testWallet.AddWatchOnly(watchedScript, 0));

uint32_t nonce = 0;
const auto addOutput = [&](const CScript& script, CAmount amount, bool internal) {
CMutableTransaction tx;
tx.nLockTime = ++nonce;
// The other output has the collateral amount too, but is not collateral.
tx.vout.emplace_back(1000 * COIN, ownedScript);
tx.vout.emplace_back(amount, script);
if (internal) {
tx.nVersion = 3;
tx.nType = TRANSACTION_PROVIDER_REGISTER;
CProRegTx proTx;
proTx.collateralOutpoint.n = 1;
SetTxPayload(tx, proTx);
}
const auto txRef = MakeTransactionRef(tx);
// Exercise the startup scan, not AddToWallet's independent auto-locking.
BOOST_REQUIRE(testWallet.LoadToWallet(CWalletTx(&testWallet, txRef)));
return COutPoint(txRef->GetHash(), 1);
};

const auto internalOwned = addOutput(ownedScript, 1000 * COIN, true);
const auto internalWatched = addOutput(watchedScript, 1000 * COIN, true);
const auto internalForeign = addOutput(foreignScript, 1000 * COIN, true);
const auto internalSpent = addOutput(ownedScript, 1000 * COIN, true);
const auto wrongAmount = addOutput(ownedScript, 999 * COIN, true);
const auto ordinary = addOutput(ownedScript, 1000 * COIN, false);
const auto externalOwned = addOutput(ownedScript, 1000 * COIN, false);
const auto externalForeign = addOutput(foreignScript, 1000 * COIN, false);
const auto externalSpent = addOutput(ownedScript, 1000 * COIN, false);

// Supply an MN-list snapshot in the fixture's in-memory EvoDB. These
// ordinary transactions are collateral only through their registered MNs.
const CBlockIndex* tip = chainActive.Tip();
BOOST_REQUIRE(tip);
CDeterministicMNList mnList(tip->GetBlockHash(), tip->nHeight, 3);
for (const auto& collateral : {externalOwned, externalForeign, externalSpent}) {
auto dmn = std::make_shared<CDeterministicMN>();
dmn->proTxHash = SerializeHash(collateral);
dmn->internalId = mnList.GetAllMNsCount();
dmn->collateralOutpoint = collateral;
dmn->nOperatorReward = 0;
auto dmnState = std::make_shared<CDeterministicMNState>();
CKey ownerKey;
ownerKey.MakeNewKey(true);
dmnState->keyIDOwner = ownerKey.GetPubKey().GetID();
dmn->pdmnState = dmnState;
mnList.AddMN(dmn);
}
evoDb->Write(std::make_pair(std::string("dmn_S"), tip->GetBlockHash()), mnList);
deterministicMNManager->ClearCache();
deterministicMNManager->UpdatedBlockTip(tip);
BOOST_REQUIRE(deterministicMNManager->GetListAtChainTip().HasMNByCollateral(externalOwned));

CMutableTransaction spend;
spend.vin.emplace_back(internalSpent);
spend.vin.emplace_back(externalSpent);
spend.vout.emplace_back(1 * COIN, ownedScript);
BOOST_REQUIRE(testWallet.LoadToWallet(CWalletTx(&testWallet, MakeTransactionRef(spend))));
BOOST_REQUIRE(testWallet.IsSpent(internalSpent.hash, internalSpent.n));
BOOST_REQUIRE(testWallet.IsSpent(externalSpent.hash, externalSpent.n));

testWallet.LockCoin(ordinary); // Preserve existing manual locks as well.
testWallet.AutoLockMasternodeCollaterals();
std::vector<COutPoint> locked;
testWallet.ListLockedCoins(locked);
const std::set<COutPoint> expected{internalOwned, internalWatched, externalOwned, ordinary};
BOOST_CHECK(std::set<COutPoint>(locked.begin(), locked.end()) == expected);
BOOST_CHECK(!testWallet.IsLockedCoin(internalForeign.hash, internalForeign.n));
BOOST_CHECK(!testWallet.IsLockedCoin(externalForeign.hash, externalForeign.n));
BOOST_CHECK(!testWallet.IsLockedCoin(wrongAmount.hash, wrongAmount.n));

testWallet.AutoLockMasternodeCollaterals();
locked.clear();
testWallet.ListLockedCoins(locked);
BOOST_CHECK(std::set<COutPoint>(locked.begin(), locked.end()) == expected);
}

BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
{
LOCK(cs_main);
Expand Down
5 changes: 3 additions & 2 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4200,8 +4200,9 @@ void CWallet::AutoLockMasternodeCollaterals()
LOCK2(cs_main, cs_wallet);
for (const auto& pair : mapWallet) {
for (unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
if (IsMine(pair.second.tx->vout[i], *pair.second.tx) && !IsSpent(pair.first, i)) {
if (deterministicMNManager->IsProTxWithCollateral(pair.second.tx, i) || mnList.HasMNByCollateral(COutPoint(pair.first, i))) {
// Avoid Spark ownership/spent checks for outputs that are not collateral.
if (deterministicMNManager->IsProTxWithCollateral(pair.second.tx, i) || mnList.HasMNByCollateral(COutPoint(pair.first, i))) {
if (IsMine(pair.second.tx->vout[i], *pair.second.tx) && !IsSpent(pair.first, i)) {
LockCoin(COutPoint(pair.first, i));
}
}
Expand Down
Loading