Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/vote delegate #2063

Closed
wants to merge 12 commits into from
Closed
2 changes: 1 addition & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2323,7 +2323,7 @@ bool AppInitMain(InitInterfaces& interfaces)
node.ownerAuthAddress = keyID;
node.operatorType = WitV0KeyHashType;
node.operatorAuthAddress = keyID;
node.version = CMasternode::VERSION0;
node.version = CMasternode::VERSION1;
pcustomcsview->CreateMasternode(uint256S(std::string{64, '0'}), node, CMasternode::ZEROYEAR);
for (uint8_t i{0}; i < SUBNODE_COUNT; ++i) {
pcustomcsview->SetSubNodesBlockTime(node.operatorAuthAddress, chain_active_height, i, time);
Expand Down
1 change: 1 addition & 0 deletions src/masternodes/govvariables/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ enum DFIPKeys : uint8_t {
EVMEnabled = 'u',
ICXEnabled = 'v',
AllowDUSDLoops = 'w',
MNSetDelegateAddress = 'x'
};

enum GovernanceKeys : uint8_t {
Expand Down
51 changes: 49 additions & 2 deletions src/masternodes/masternodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ std::optional<uint256> CMasternodesView::GetMasternodeIdByOwner(const CKeyID &id
return ReadBy<Owner, uint256>(id);
}

std::optional<uint256> CMasternodesView::GetMasternodeIdByDelegate(const CKeyID &id) const {
return ReadBy<VoteDelegate, uint256>(id);
}

void CMasternodesView::ForEachMasternode(std::function<bool(const uint256 &, CLazySerialize<CMasternode>)> callback,
const uint256 &start) {
ForEach<ID, uint256, CMasternode>(callback, start);
Expand Down Expand Up @@ -322,6 +326,10 @@ Res CMasternodesView::CreateMasternode(const uint256 &nodeId, const CMasternode
return Res::Err(
"bad owner and|or operator address (should be P2PKH or P2WPKH only) or node with those addresses exists");
}
if (!node.voteDelegationAddress.IsNull() && ((node.voteDelegationType != 1 && node.voteDelegationType != 4) || GetMasternodeIdByDelegate(node.voteDelegationAddress))) {
return Res::Err(
"bad delegate address (should be P2PKH or P2WPKH only) or node with those addresses exists");
}

WriteBy<ID>(nodeId, node);
WriteBy<Owner>(node.ownerAuthAddress, nodeId);
Expand All @@ -330,6 +338,7 @@ Res CMasternodesView::CreateMasternode(const uint256 &nodeId, const CMasternode
if (timelock > 0) {
WriteBy<Timelock>(nodeId, timelock);
}
WriteBy<VoteDelegate>(node.voteDelegationAddress, nodeId);

return Res::Ok();
}
Expand Down Expand Up @@ -361,8 +370,8 @@ void CMasternodesView::SetForcedRewardAddress(const uint256 &nodeId,
const CKeyID &rewardAddress,
int height) {
// If old masternode update for new serialisation
if (node.version < CMasternode::VERSION0) {
node.version = CMasternode::VERSION0;
if (node.version < CMasternode::VERSION1) {
node.version = CMasternode::VERSION1;
}

// Set new reward address
Expand All @@ -383,6 +392,34 @@ void CMasternodesView::RemForcedRewardAddress(const uint256 &nodeId, CMasternode
WriteBy<PendingHeight>(node.ownerAuthAddress, static_cast<uint32_t>(height + GetMnResignDelay(height)));
}

void CMasternodesView::SetForcedDelegateAddress(const uint256 &nodeId,
CMasternode &node,
const char delegateAddressType,
const CKeyID &delegateAddress,
int height) {
// If old masternode update for new serialisation
if (node.version < CMasternode::VERSION2) {
node.version = CMasternode::VERSION2;
}

// Set new reward address
node.voteDelegationType = delegateAddressType;
node.voteDelegationAddress = delegateAddress;
WriteBy<ID>(nodeId, node);

// Pending change
WriteBy<PendingHeight>(node.ownerAuthAddress, static_cast<uint32_t>(height + GetMnResignDelay(height)));
}

void CMasternodesView::RemForcedDelegateAddress(const uint256 &nodeId, CMasternode &node, int height) {
node.voteDelegationType = 0;
node.voteDelegationAddress.SetNull();
WriteBy<ID>(nodeId, node);

// Pending change
WriteBy<PendingHeight>(node.ownerAuthAddress, static_cast<uint32_t>(height + GetMnResignDelay(height)));
}

std::optional<uint32_t> CMasternodesView::GetPendingHeight(const CKeyID &ownerAuthAddress) const {
return ReadBy<PendingHeight, uint32_t>(ownerAuthAddress);
}
Expand Down Expand Up @@ -411,6 +448,8 @@ void CMasternodesView::UpdateMasternodeOperator(const uint256 &nodeId,
WriteBy<ID>(nodeId, node);
WriteBy<Operator>(node.operatorAuthAddress, nodeId);

WriteBy<VoteDelegate>(node.voteDelegationAddress, nodeId);

// Pending change
WriteBy<PendingHeight>(node.ownerAuthAddress, static_cast<uint32_t>(height + GetMnResignDelay(height)));
}
Expand Down Expand Up @@ -781,6 +820,14 @@ void CSettingsView::SettingsSetRewardAddresses(const std::set<CScript> &addresse
WriteBy<KVSettings>(MN_REWARD_ADDRESSES, addresses);
}

std::optional<std::set<CScript>> CSettingsView::SettingsGetDelegateAddresses() {
return ReadBy<KVSettings, std::set<CScript>>(MN_DELEGATE_ADDRESSES);
}

void CSettingsView::SettingsSetDelegateAddresses(const std::set<CScript> &addresses) {
WriteBy<KVSettings>(MN_DELEGATE_ADDRESSES, addresses);
}

/*
* CCustomCSView
*/
Expand Down
42 changes: 34 additions & 8 deletions src/masternodes/masternodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ CAmount GetProposalCreationFee(int height, const CCustomCSView &view, const CCre
void CalcMissingRewardTempFix(CCustomCSView &mnview, const uint32_t targetHeight, const CWallet &wallet);

enum class UpdateMasternodeType : uint8_t {
None = 0x00,
OwnerAddress = 0x01,
OperatorAddress = 0x02,
SetRewardAddress = 0x03,
RemRewardAddress = 0x04
None = 0x00,
OwnerAddress = 0x01,
OperatorAddress = 0x02,
SetRewardAddress = 0x03,
RemRewardAddress = 0x04,
SetDelegateAddress = 0x05,
RemDelegateAddress = 0x06
};

constexpr uint8_t SUBNODE_COUNT{4};
Expand All @@ -75,8 +77,9 @@ class CMasternode {
enum TimeLock { ZEROYEAR, FIVEYEAR = 260, TENYEAR = 520 };

enum Version : int32_t {
PRE_FORT_CANNING = -1,
VERSION0 = 0,
VERSION0_PRE_FORT_CANNING = -1,
VERSION1 = 0,
VERSION2 = 1,
};

//! Minted blocks counter
Expand Down Expand Up @@ -105,6 +108,10 @@ class CMasternode {
uint256 resignTx;
uint256 collateralTx;

//! THis field will be used for vote delegation
char voteDelegationType;
CKeyID voteDelegationAddress;

//! empty constructor
CMasternode();

Expand Down Expand Up @@ -133,10 +140,15 @@ class CMasternode {
READWRITE(collateralTx);

// Only available after FortCanning
if (version > PRE_FORT_CANNING) {
if (version > VERSION0_PRE_FORT_CANNING) {
READWRITE(rewardAddress);
READWRITE(rewardAddressType);
}

if (version > VERSION1){
READWRITE(voteDelegationType);
READWRITE(voteDelegationAddress);
}
}

//! equality test
Expand Down Expand Up @@ -206,6 +218,7 @@ class CMasternodesView : public virtual CStorageView {
std::optional<CMasternode> GetMasternode(const uint256 &id) const;
std::optional<uint256> GetMasternodeIdByOperator(const CKeyID &id) const;
std::optional<uint256> GetMasternodeIdByOwner(const CKeyID &id) const;
std::optional<uint256> GetMasternodeIdByDelegate(const CKeyID &id) const;
void ForEachMasternode(std::function<bool(const uint256 &, CLazySerialize<CMasternode>)> callback,
const uint256 &start = uint256());

Expand All @@ -228,6 +241,12 @@ class CMasternodesView : public virtual CStorageView {
const CKeyID &rewardAddress,
int height);
void RemForcedRewardAddress(const uint256 &nodeId, CMasternode &node, int height);
void SetForcedDelegateAddress(const uint256 &nodeId,
CMasternode &node,
const char delegateAddressType,
const CKeyID &delegateAddress,
int height);
void RemForcedDelegateAddress(const uint256 &nodeId, CMasternode &node, int height);
void UpdateMasternodeOperator(const uint256 &nodeId,
CMasternode &node,
const char operatorType,
Expand Down Expand Up @@ -300,6 +319,10 @@ class CMasternodesView : public virtual CStorageView {
struct Timelock {
static constexpr uint8_t prefix() { return 'K'; }
};

struct VoteDelegate {
static constexpr uint8_t prefix() { return 'D'; }
};
};

class CLastHeightView : public virtual CStorageView {
Expand Down Expand Up @@ -381,6 +404,7 @@ class CSettingsView : public virtual CStorageView {
const std::string DEX_STATS_LAST_HEIGHT = "DexStatsLastHeight";
const std::string DEX_STATS_ENABLED = "DexStatsEnabled";
const std::string MN_REWARD_ADDRESSES = "MNRewardAddresses";
const std::string MN_DELEGATE_ADDRESSES = "MNDelegateAddresses";

void SetDexStatsLastHeight(int32_t height);
std::optional<int32_t> GetDexStatsLastHeight();
Expand All @@ -389,6 +413,8 @@ class CSettingsView : public virtual CStorageView {

std::optional<std::set<CScript>> SettingsGetRewardAddresses();
void SettingsSetRewardAddresses(const std::set<CScript> &addresses);
std::optional<std::set<CScript>> SettingsGetDelegateAddresses();
void SettingsSetDelegateAddresses(const std::set<CScript> &addresses);

struct KVSettings {
static constexpr uint8_t prefix() { return '0'; }
Expand Down
112 changes: 109 additions & 3 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ std::string ToString(CustomTxType type) {
switch (type) {
case CustomTxType::CreateMasternode:
return "CreateMasternode";
case CustomTxType::CreateMasternodeV2:
return "CreateMasternodeV2";
case CustomTxType::ResignMasternode:
return "ResignMasternode";
case CustomTxType::UpdateMasternode:
Expand Down Expand Up @@ -189,6 +191,8 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) {
switch (txType) {
case CustomTxType::CreateMasternode:
return CCreateMasterNodeMessage{};
case CustomTxType::CreateMasternodeV2:
return CCreateMasterNodeV2Message{};
case CustomTxType::ResignMasternode:
return CResignMasterNodeMessage{};
case CustomTxType::UpdateMasternode:
Expand Down Expand Up @@ -442,6 +446,7 @@ class CCustomMetadataParseVisitor {
return IsHardforkEnabled(consensus.NextNetworkUpgradeHeight);
else if constexpr (IsOneOf<T,
CCreateMasterNodeMessage,
CCreateMasterNodeV2Message,
CResignMasterNodeMessage>())
return Res::Ok();
else
Expand Down Expand Up @@ -823,7 +828,82 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {

// Set masternode version2 after FC for new serialisation
if (height >= static_cast<uint32_t>(consensus.FortCanningHeight))
node.version = CMasternode::VERSION0;
node.version = CMasternode::VERSION1;

bool duplicate{};
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 == node.ownerAuthAddress || storedID == node.operatorAuthAddress) {
duplicate = true;
return false;
}
return true;
});

if (duplicate) {
return Res::ErrCode(CustomTxErrCodes::Fatal, "Masternode exist with that owner address pending");
}

Require(mnview.CreateMasternode(tx.GetHash(), node, obj.timelock));
// Build coinage from the point of masternode creation

if (height >= static_cast<uint32_t>(consensus.EunosPayaHeight))
for (uint8_t i{0}; i < SUBNODE_COUNT; ++i)
mnview.SetSubNodesBlockTime(node.operatorAuthAddress, static_cast<uint32_t>(height), i, time);

else if (height >= static_cast<uint32_t>(consensus.DakotaCrescentHeight))
mnview.SetMasternodeLastBlockTime(node.operatorAuthAddress, static_cast<uint32_t>(height), time);

return Res::Ok();
}

Res operator()(const CCreateMasterNodeV2Message &obj) const {
Require(CheckMasternodeCreationTx());

if (height >= static_cast<uint32_t>(consensus.EunosHeight))
Require(HasAuth(tx.vout[1].scriptPubKey), "masternode creation needs owner auth");

if (height >= static_cast<uint32_t>(consensus.EunosPayaHeight)) {
switch (obj.timelock) {
case CMasternode::ZEROYEAR:
case CMasternode::FIVEYEAR:
case CMasternode::TENYEAR:
break;
default:
return Res::Err("Timelock must be set to either 0, 5 or 10 years");
}
} else
Require(obj.timelock == 0, "collateral timelock cannot be set below EunosPaya");

CMasternode node;
CTxDestination dest;
if (ExtractDestination(tx.vout[1].scriptPubKey, dest)) {
if (dest.index() == PKHashType) {
node.ownerType = 1;
node.ownerAuthAddress = CKeyID(std::get<PKHash>(dest));
} else if (dest.index() == WitV0KeyHashType) {
node.ownerType = 4;
node.ownerAuthAddress = CKeyID(std::get<WitnessV0KeyHash>(dest));
}
}
node.creationHeight = height;
node.operatorType = obj.operatorType;
node.operatorAuthAddress = obj.operatorAuthAddress;
node.voteDelegationType = obj.voteDelegationType;
node.voteDelegationAddress = obj.voteDelegationAddress;

// Set masternode version2 after FC for new serialisation
if (height >= static_cast<uint32_t>(consensus.FortCanningHeight))
node.version = CMasternode::VERSION1;

bool duplicate{};
mnview.ForEachNewCollateral([&](const uint256 &key, CLazySerialize<MNNewOwnerHeightValue> valueKey) {
Expand Down Expand Up @@ -1024,6 +1104,32 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
rewardType = true;

mnview.RemForcedRewardAddress(obj.mnId, *node, height);
} else if (type == static_cast<uint8_t>(UpdateMasternodeType::SetDelegateAddress)) {
CDataStructureV0 key{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::MNSetDelegateAddress};
if (!attributes->GetValue(key, false)) {
return Res::Err("Updating masternode reward address not currently enabled in attributes.");
}

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

const auto keyID = CKeyID(uint160(rawAddress));
mnview.SetForcedDelegateAddress(obj.mnId, *node, addressType, keyID, height);

if (auto addresses = mnview.SettingsGetDelegateAddresses()) {
const CScript delegateAddress = GetScriptForDestination(addressType == PKHashType ?
CTxDestination(PKHash(keyID)) :
CTxDestination(WitnessV0KeyHash(keyID)));
addresses->insert(delegateAddress);
mnview.SettingsSetDelegateAddresses(*addresses);
}
} else if (type == static_cast<uint8_t>(UpdateMasternodeType::RemDelegateAddress)) {
CDataStructureV0 key{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::MNSetDelegateAddress};
if (!attributes->GetValue(key, false)) {
return Res::Err("Updating masternode reward address not currently enabled in attributes.");
}
mnview.RemForcedDelegateAddress(obj.mnId, *node, height);
} else {
return Res::Err("Unknown update type provided");
}
Expand Down Expand Up @@ -4270,7 +4376,7 @@ Res ApplyCustomTx(CCustomCSView &mnview,
}

// Track burn fee
if (txType == CustomTxType::CreateToken || txType == CustomTxType::CreateMasternode) {
if (txType == CustomTxType::CreateToken || txType == CustomTxType::CreateMasternode || txType == CustomTxType::CreateMasternodeV2) {
mnview.GetHistoryWriters().AddFeeBurn(tx.vout[0].scriptPubKey, tx.vout[0].nValue);
}

Expand Down Expand Up @@ -4480,7 +4586,7 @@ bool IsMempooledCustomTxCreate(const CTxMemPool &pool, const uint256 &txid) {
if (ptx) {
std::vector<unsigned char> dummy;
CustomTxType txType = GuessCustomTxType(*ptx, dummy);
return txType == CustomTxType::CreateMasternode || txType == CustomTxType::CreateToken;
return txType == CustomTxType::CreateMasternode || txType == CustomTxType::CreateToken || txType == CustomTxType::CreateMasternodeV2;
}
return false;
}
Expand Down
Loading
Loading