Skip to content

Commit

Permalink
Restore updatemasternode and combine setforcedrewardaddress / removef…
Browse files Browse the repository at this point in the history
…orcedaddress (#934)

* Restore updatemasternode

* test: create MN one at a time

* Update MN update PR for latest v3

* Fix PoS test

* Resolve issues after merge

* Use local view variable

* Resolve errors after merge

* Remove outdated code and update ApplyCustomTx call

* tests: resolve random failure
  • Loading branch information
Bushstar authored May 19, 2022
1 parent 21d3a2e commit 0364a90
Show file tree
Hide file tree
Showing 28 changed files with 916 additions and 546 deletions.
30 changes: 15 additions & 15 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,20 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
strprintf("%s: inputs missing/spent", __func__));
}

// check for tokens values
uint256 canSpend;
std::vector<unsigned char> dummy;
const auto txType = GuessCustomTxType(tx, dummy);

if (NotAllowedToFail(txType, nSpendHeight) || (nSpendHeight >= chainparams.GetConsensus().GreatWorldHeight && txType == CustomTxType::UpdateMasternode)) {
CCustomCSView discardCache(mnview);
CFutureSwapView futureSwapView(*pfutureSwapView);
auto res = ApplyCustomTx(discardCache, futureSwapView, inputs, tx, chainparams.GetConsensus(), nSpendHeight, 0, &canSpend);
if (!res.ok && (res.code & CustomTxErrCodes::Fatal) && txType != CustomTxType::UpdateMasternode) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-customtx", res.msg);
}
}

TAmounts nValuesIn;
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
const COutPoint &prevout = tx.vin[i].prevout;
Expand All @@ -188,8 +202,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
}
/// @todo tokens: later match the range with TotalSupply

if (prevout.n == 1 && !mnview.CanSpend(prevout.hash, nSpendHeight)) {
if (canSpend != prevout.hash && prevout.n == 1 && !mnview.CanSpend(prevout.hash, nSpendHeight)) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-collateral-locked",
strprintf("tried to spend locked collateral for %s", prevout.hash.ToString())); /// @todo may be somehow place the height of unlocking?
}
Expand All @@ -216,19 +229,6 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-tokens-in-old-version-tx");
}

// check for tokens values
std::vector<unsigned char> dummy;
const auto txType = GuessCustomTxType(tx, dummy);

if (NotAllowedToFail(txType, nSpendHeight)) {
CCustomCSView discardCache(mnview);
CFutureSwapView futureSwapView(*pfutureSwapView);
auto res = ApplyCustomTx(discardCache, futureSwapView, inputs, tx, chainparams.GetConsensus(), nSpendHeight);
if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-customtx", res.msg);
}
}

for (auto const & kv : non_minted_values_out) {
DCT_ID const & tokenId = kv.first;

Expand Down
4 changes: 2 additions & 2 deletions src/masternodes/anchors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ bool CAnchorAwaitingConfirms::Validate(CAnchorConfirmMessage const &confirmMessa
}

auto it = pcustomcsview->GetMasternodeIdByOperator(signer);
if (!it || !pcustomcsview->GetMasternode(*it)->IsActive(height)) {
if (!it || !pcustomcsview->GetMasternode(*it)->IsActive(height, *pcustomcsview)) {
LogPrint(BCLog::ANCHORING, "%s: Warning! Masternode with operator key %s does not exist or not active!\n", __func__, signer.ToString());
return false;
}
Expand Down Expand Up @@ -1117,7 +1117,7 @@ std::map<CKeyID, CKey> AmISignerNow(int height, CAnchorData::CTeam const & team)
continue;
}

if (node->IsActive(height) && team.find(mnId.first) != team.end()) {
if (node->IsActive(height, *pcustomcsview) && team.find(mnId.first) != team.end()) {
CKey masternodeKey;
for (auto const & wallet : GetWallets()) {
if (wallet->GetKey(mnId.first, masternodeKey)) {
Expand Down
159 changes: 128 additions & 31 deletions src/masternodes/consensus/masternodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.

#include <masternodes/masternodes.h>

#include <coins.h>
#include <consensus/params.h>
#include <masternodes/consensus/masternodes.h>
#include <masternodes/masternodes.h>
#include <masternodes/customtx.h>
#include <primitives/transaction.h>

Res CMasternodesConsensus::operator()(const CCreateMasterNodeMessage& obj) const {
Expand Down Expand Up @@ -62,42 +65,136 @@ Res CMasternodesConsensus::operator()(const CCreateMasterNodeMessage& obj) const
}

Res CMasternodesConsensus::operator()(const CResignMasterNodeMessage& obj) const {
auto res = HasCollateralAuth(obj);
return !res ? res : mnview.ResignMasternode(obj, tx.GetHash(), height);
auto node = mnview.GetMasternode(obj);
if (!node) {
return Res::Err("node %s does not exists", obj.ToString());
}
auto res = HasCollateralAuth(node->collateralTx.IsNull() ? static_cast<uint256>(obj) : node->collateralTx);
return !res ? res : mnview.ResignMasternode(*node, obj, tx.GetHash(), height);
}

Res CMasternodesConsensus::operator()(const CSetForcedRewardAddressMessage& obj) const {
// Temporarily disabled for 2.2
return Res::Err("reward address change is disabled for Fort Canning");

auto node = mnview.GetMasternode(obj.nodeId);
if (!node)
return Res::Err("masternode %s does not exist", obj.nodeId.ToString());

if (!HasCollateralAuth(obj.nodeId))
return Res::Err("%s: %s", obj.nodeId.ToString(), "tx must have at least one input from masternode owner");

return mnview.SetForcedRewardAddress(obj.nodeId, obj.rewardAddressType, obj.rewardAddress, height);
}
Res CMasternodesConsensus::operator()(const CUpdateMasterNodeMessage& obj) const {
if (obj.updates.empty()) {
return Res::Err("No update arguments provided");
}

Res CMasternodesConsensus::operator()(const CRemForcedRewardAddressMessage& obj) const {
// Temporarily disabled for 2.2
return Res::Err("reward address change is disabled for Fort Canning");
if (obj.updates.size() > 3) {
return Res::Err("Too many updates provided");
}

auto node = mnview.GetMasternode(obj.nodeId);
if (!node)
return Res::Err("masternode %s does not exist", obj.nodeId.ToString());
auto node = mnview.GetMasternode(obj.mnId);
if (!node) {
return Res::Err("masternode %s does not exists", obj.mnId.ToString());
}

if (!HasCollateralAuth(obj.nodeId))
return Res::Err("%s: %s", obj.nodeId.ToString(), "tx must have at least one input from masternode owner");
const auto collateralTx = node->collateralTx.IsNull() ? obj.mnId : node->collateralTx;
const auto res = HasCollateralAuth(collateralTx);
if (!res) {
return res;
}

return mnview.RemForcedRewardAddress(obj.nodeId, height);
}
auto state = node->GetState(height, mnview);
if (state != CMasternode::ENABLED) {
return Res::Err("Masternode %s is not in 'ENABLED' state", obj.mnId.ToString());
}

Res CMasternodesConsensus::operator()(const CUpdateMasterNodeMessage& obj) const {
// Temporarily disabled for 2.2
return Res::Err("updatemasternode is disabled for Fort Canning");
bool ownerType{false}, operatorType{false}, rewardType{false};
for (const auto& item : obj.updates) {
if (item.first == static_cast<uint8_t>(UpdateMasternodeType::OwnerAddress)) {
if (ownerType) {
return Res::Err("Multiple owner updates provided");
}
ownerType = true;
bool collateralFound{false};
for (const auto vin : tx.vin) {
if (vin.prevout.hash == collateralTx && vin.prevout.n == 1) {
collateralFound = true;
}
}
if (!collateralFound) {
return Res::Err("Missing previous collateral from transaction inputs");
}
if (tx.vout.size() == 1) {
return Res::Err("Missing new collateral output");
}
if (!HasAuth(tx.vout[1].scriptPubKey)) {
return Res::Err("Missing auth input for new masternode owner");
}

CTxDestination dest;
if (!ExtractDestination(tx.vout[1].scriptPubKey, dest) || (dest.index() != PKHashType && dest.index() != WitV0KeyHashType)) {
return Res::Err("Owner address must be P2PKH or P2WPKH type");
}

if (tx.vout[1].nValue != GetMnCollateralAmount(height)) {
return Res::Err("Incorrect collateral amount");
}

const auto keyID = dest.index() == PKHashType ? CKeyID(std::get<PKHash>(dest)) : CKeyID(std::get<WitnessV0KeyHash>(dest));
if (mnview.GetMasternodeIdByOwner(keyID) || mnview.GetMasternodeIdByOperator(keyID)) {
return Res::Err("Masternode with that owner address already exists");
}

bool duplicate{false};
mnview.ForEachNewCollateral([&](const uint256& key, CLazySerialize<MNNewOwnerHeightValue> valueKey) {
const auto& value = valueKey.get();
if (height > value.blockHeight) {
return true;
}
const auto& coin = coins.AccessCoin({key, 1});
assert(!coin.IsSpent());
CTxDestination pendingDest;
assert(ExtractDestination(coin.out.scriptPubKey, pendingDest));
const CKeyID storedID = pendingDest.index() == PKHashType ? CKeyID(std::get<PKHash>(pendingDest)) : CKeyID(std::get<WitnessV0KeyHash>(pendingDest));
if (storedID == keyID) {
duplicate = true;
return false;
}
return true;
});
if (duplicate) {
return Res::ErrCode(CustomTxErrCodes::Fatal, "Masternode exist with that owner address pending already");
}

mnview.UpdateMasternodeCollateral(obj.mnId, *node, tx.GetHash(), height);
} else if (item.first == static_cast<uint8_t>(UpdateMasternodeType::OperatorAddress)) {
if (operatorType) {
return Res::Err("Multiple operator updates provided");
}
operatorType = true;

if (item.second.first != 1 && item.second.first != 4) {
return Res::Err("Operator address must be P2PKH or P2WPKH type");
}

const auto keyID = CKeyID(uint160(item.second.second));
if (mnview.GetMasternodeIdByOwner(keyID) || mnview.GetMasternodeIdByOperator(keyID)) {
return Res::Err("Masternode with that operator address already exists");
}
mnview.UpdateMasternodeOperator(obj.mnId, *node, item.second.first, keyID, height);
} else if (item.first == static_cast<uint8_t>(UpdateMasternodeType::SetRewardAddress)) {
if (rewardType) {
return Res::Err("Multiple reward address updates provided");
}
rewardType = true;

if (item.second.first != 1 && item.second.first != 4) {
return Res::Err("Reward address must be P2PKH or P2WPKH type");
}

const auto keyID = CKeyID(uint160(item.second.second));
mnview.SetForcedRewardAddress(obj.mnId, *node, item.second.first, keyID, height);
} else if (item.first == static_cast<uint8_t>(UpdateMasternodeType::RemRewardAddress)) {
if (rewardType) {
return Res::Err("Multiple reward address updates provided");
}
rewardType = true;

mnview.RemForcedRewardAddress(obj.mnId, *node, height);
} else {
return Res::Err("Unknown update type provided");
}
}

auto res = HasCollateralAuth(obj.mnId);
return !res ? res : mnview.UpdateMasternode(obj.mnId, obj.operatorType, obj.operatorAuthAddress, height);
return Res::Ok();
}
4 changes: 0 additions & 4 deletions src/masternodes/consensus/masternodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,13 @@

struct CCreateMasterNodeMessage;
struct CResignMasterNodeMessage;
struct CSetForcedRewardAddressMessage;
struct CRemForcedRewardAddressMessage;
struct CUpdateMasterNodeMessage;

class CMasternodesConsensus : public CCustomTxVisitor {
public:
using CCustomTxVisitor::CCustomTxVisitor;
Res operator()(const CCreateMasterNodeMessage& obj) const;
Res operator()(const CResignMasterNodeMessage& obj) const;
Res operator()(const CSetForcedRewardAddressMessage& obj) const;
Res operator()(const CRemForcedRewardAddressMessage& obj) const;
Res operator()(const CUpdateMasterNodeMessage& obj) const;
};

Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/consensus/proposals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Res CProposalsConsensus::operator()(const CPropVoteMessage& obj) const {
if (!HasAuth(GetScriptForDestination(ownerDest)))
return Res::Err("tx must have at least one input from the owner");

if (!node->IsActive(height))
if (!node->IsActive(height, mnview))
return Res::Err("masternode <%s> is not active", obj.masternodeId.GetHex());

if (node->mintedBlocks < 1)
Expand Down
4 changes: 0 additions & 4 deletions src/masternodes/customtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ CustomTxType CustomTxCodeToType(uint8_t ch) {
switch (type) {
case CustomTxType::CreateMasternode:
case CustomTxType::ResignMasternode:
case CustomTxType::SetForcedRewardAddress:
case CustomTxType::RemForcedRewardAddress:
case CustomTxType::UpdateMasternode:
case CustomTxType::CreateToken:
case CustomTxType::MintToken:
Expand Down Expand Up @@ -80,8 +78,6 @@ std::string ToString(CustomTxType type) {
switch (type) {
CustomTxTypeString(CreateMasternode);
CustomTxTypeString(ResignMasternode);
CustomTxTypeString(SetForcedRewardAddress);
CustomTxTypeString(RemForcedRewardAddress);
CustomTxTypeString(UpdateMasternode);
CustomTxTypeString(CreateToken);
CustomTxTypeString(UpdateToken);
Expand Down
2 changes: 0 additions & 2 deletions src/masternodes/customtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ enum struct CustomTxType : uint8_t
CreateMasternode = 'C',
ResignMasternode = 'R',
UpdateMasternode = 'm',
SetForcedRewardAddress = 'F',
RemForcedRewardAddress = 'f',

// tokens
CreateToken = 'T',
Expand Down
Loading

0 comments on commit 0364a90

Please sign in to comment.