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

[GUI] Deterministic Masternode #2804

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d40aac0
mnmodel: implement MN wrapper first step
furszy Jan 25, 2022
9ec79d3
Move FundSpecialTx function from rpcevo.cpp to specialtx_utils.cpp
furszy Jan 18, 2022
316d924
Move CalcTxInputsHash from specialtx_validation.h to transaction.h
furszy Jan 19, 2022
2e797f1
Move SignSpecialTxPayload* functions to specialtx_utils
furszy Jan 21, 2022
c6b415b
RPC: Remove extra special tx validation.
furszy Jan 21, 2022
4e603db
RPC: Commit special transaction inside the wallet before relay it to …
furszy Jan 21, 2022
9590eb8
Encapsulate SignTransaction inside the wallet class.
furszy Jan 21, 2022
13c438b
RPC: Move SignAndSendSpecialTx to specialtx_utils.h/cpp
furszy Jan 21, 2022
34eddd9
RPC: cleanup unneeded variable from parseProReg
furszy Jan 23, 2022
42be387
chainparams: set v6.0 regtest activation height.
furszy Jan 24, 2022
7e7c018
Move ParsePubKeyIDFromAddress to specialtx_utils.h/cpp
furszy Jan 24, 2022
1784fff
GUI: implement basic create DMN process
furszy Jan 23, 2022
b62a622
Create tiertwo interface, initializing and connecting the local DMNs …
furszy Jan 27, 2022
f36ca3d
GUI: Connect known DMN to the MN screen
furszy Jan 28, 2022
b43129c
GUI: Add DMN PoSe banned start.
furszy Jan 31, 2022
26d8715
GUI: Generalize and simplify TooltipMenu
furszy Jan 31, 2022
acc12da
Store DMN operator sk within the dmn registration tx (if the key was …
furszy Jan 31, 2022
5806d8d
GUI: Implement and connect owner "kill DMN" functionality.
furszy Jan 31, 2022
f056cbf
GUI: Implement and connect create DMN summary page
furszy Feb 1, 2022
5f2fe77
GUI: Masternode creation wizard; generalize pages setup, next and bac…
furszy Feb 2, 2022
e9100d4
interfaces:tiertwo, implement isBlsPubKeyValid function.
furszy Feb 4, 2022
c89102a
GUI: Masternode creation wizard dialog, create and connect owner and …
furszy Feb 4, 2022
3beb523
GUI: add copy to clipboard action to DMN creation summary page
furszy Feb 7, 2022
6109b88
GUI: add subtitle to masternode creation wizard summary page
furszy Feb 8, 2022
48cae01
GUI: do not invalidate addresses filter when the new filter type is e…
furszy Feb 8, 2022
01c45e8
GUI: masternode creation wizard, connect dropdown list to owner addre…
furszy Feb 8, 2022
a17173c
interfaces::tiertwo, implement function to get single DMN data.
furszy Feb 8, 2022
c6a893a
GUI: Implement and connect MN information dialog for Deterministic Ma…
furszy Feb 8, 2022
5d7c07b
GUI: add export DMN functionality
furszy Feb 9, 2022
ab5fb95
GUI: Masternode creation wizard; add voter customization page.
furszy Feb 17, 2022
811e0e6
GUI: Masternode creation wizard; connect custom voting key to the bac…
furszy Feb 18, 2022
7410e45
GUI: connect custom voting address to summary screen
furszy Feb 18, 2022
377b09d
GUI: DMN creation, convert rejection error into an understandable str…
furszy Feb 19, 2022
fce1940
test: failing txs are now properly considered RPC_VERIFY_REJECTED and…
furszy Feb 21, 2022
c019cc8
Add and connect function to validate the operator service
furszy Feb 21, 2022
2a11af8
GUI: enable custom operator payout
furszy Feb 21, 2022
527ac1b
GUI, Voting dialog: filter out legacy MNs and DMNs that don't have th…
furszy Feb 21, 2022
32e289a
GUI: MN wizard dialog text cut off correction.
furszy Feb 28, 2022
b127b1e
GUI: MN creation wizard, get default port number from the client model.
furszy Mar 1, 2022
4a4bfef
GUI: hide MN start button if it's a deterministic MN
furszy Mar 4, 2022
9e4b653
trigger NotifyMasternodeListChanged asynchronously.
furszy Mar 9, 2022
f357ba3
Merge remote-tracking branch 'furszy/2022_GUI_dmn_features' into 2023…
Liquid369 Feb 9, 2023
4f3cee2
Fixed two bugs, one of which halted the chain
panleone Jan 6, 2023
11fbcbd
Fixed collateral spent on proregtx creation bug
Liquid369 Feb 9, 2023
08442d5
Fix collateral used as input in ProRegTx
panleone Jan 8, 2023
af3cea2
[Budget] Fix DMN Voting
Liquid369 Feb 4, 2023
ab5069b
Fixed DMN creation system: the collateral can now be included in the …
Liquid369 Feb 9, 2023
20c5455
Added possibility to POSE-UNBAN fom GUI
Liquid369 Apr 28, 2023
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
Prev Previous commit
Next Next commit
Fixed DMN creation system: the collateral can now be included in the …
…proreg
  • Loading branch information
Liquid369 committed Feb 9, 2023
commit ab5069bd8da6772a7043110647eeb4da153a4d19
41 changes: 35 additions & 6 deletions src/qt/pivx/masternodewizarddialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "qt/pivx/masternodewizarddialog.h"
#include "optional.h"
#include "primitives/transaction.h"
#include "qt/pivx/forms/ui_masternodewizarddialog.h"

#include "key_io.h"
Expand Down Expand Up @@ -425,7 +427,6 @@ bool MasterNodeWizardDialog::createMN()
}

if (isDeterministic) {
// 1) Get or create the owner addr
auto opOwnerAddrAndKeyId = getOrCreateOwnerAddress(alias);
if (!opOwnerAddrAndKeyId.getRes()) {
return errorOut(tr(opOwnerAddrAndKeyId.getError().c_str()));
Expand Down Expand Up @@ -462,11 +463,20 @@ bool MasterNodeWizardDialog::createMN()
votingAddr = ownerKeyId;
}

// For now, collateral key is always inside the wallet
Optional<COutPoint> collateralOut = COutPoint();
bool foundCandidate = false;
if(!walletModel->getMNCollateralCandidate(*collateralOut)){
collateralOut= nullopt;
}else{
//We have to lock the collateral or the system could spend it
walletModel->lockCoin(*collateralOut);
foundCandidate = true;
}
std::string error_str;
walletModel->lockCoin(collateralOut);

auto res = mnModel->createDMN(alias,
collateralOut,
addressLabel,
ipAddress,
port,
ownerKeyId,
Expand All @@ -477,16 +487,18 @@ bool MasterNodeWizardDialog::createMN()
(uint16_t) operatorPercentage * 100, // operator percentage
operatorPayoutKeyId); // operator payout script
if (!res) {
walletModel->unlockCoin(collateralOut);
//unlock in case of error
if(foundCandidate){
walletModel->unlockCoin(*collateralOut);
}
return errorOut(tr(error_str.c_str()));
}

// If the operator key was created locally, let's get it for the summary
// future: move "operatorSk" to a constant field
std::string operatorSk = walletModel->getStrFromTxExtraData(*res.getObjResult(), "operatorSk");
mnSummary = std::make_unique<MNSummary>(alias,
ipAddress+":"+port,
collateralOut,
collateralOut?*collateralOut:COutPoint(UINT256_ZERO,0), //TODO: the outpoint index is not 0 in general, but it does not matter (just display)
ownerAddrStr,
payoutAddrStr,
operatorSk.empty() ? operatorKey.toStdString() : operatorSk,
Expand All @@ -496,7 +508,24 @@ bool MasterNodeWizardDialog::createMN()

returnStr = tr("Deterministic Masternode created! It will appear on your list on the next mined block!");
} else {

// Legacy
// Look for a valid collateral utxo
COutPoint collateralOut;

if (!walletModel->getMNCollateralCandidate(collateralOut)) {
// New receive address
auto r = walletModel->getNewAddress(alias);
if (!r) return errorOut(tr(r.getError().c_str()));
if (!mnModel->createDMNExternalCollateral(addressLabel,
QString::fromStdString(r.getObjResult()->ToString()),
collateralOut,
returnStr)) {
// error str set internally
return false;
}
}

CKey secret;
secret.MakeNewKey(false);
std::string mnKeyString = KeyIO::EncodeSecret(secret);
Expand Down
147 changes: 123 additions & 24 deletions src/qt/pivx/mnmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "net.h" // for validateMasternodeIP
#include "netbase.h"
#include "operationresult.h"
#include "primitives/transaction.h"
#include "tiertwo/tiertwo_sync_state.h"
#include "uint256.h"
#include "qt/bitcoinunits.h"
Expand All @@ -24,6 +25,7 @@

#include <QFile>
#include <QHostAddress>
#include <cstddef>

uint16_t MasternodeWrapper::getType() const
{
Expand Down Expand Up @@ -311,8 +313,9 @@ std::string translateRejectionError(const std::string& rejection)
return rejection;
}

static CallResult<uint256> createDMNInternal(const COutPoint& collateral,
const CKey& keyCollateral,
CallResult<uint256> MNModel::createDMNInternal(const Optional<COutPoint>& collateral,
const Optional<QString>& addr_label,
const Optional<CKey>& keyCollateral,
const CService& service,
const CKeyID& ownerAddr,
const CBLSPublicKey& operatorPubKey,
Expand All @@ -328,7 +331,7 @@ static CallResult<uint256> createDMNInternal(const COutPoint& collateral,
pl.keyIDOwner = ownerAddr;
pl.pubKeyOperator = operatorPubKey;
pl.keyIDVoting = votingAddr ? *votingAddr : pl.keyIDOwner;
pl.collateralOutpoint = collateral;
pl.collateralOutpoint = (collateral ? *collateral : COutPoint(UINT256_ZERO, 0)); //dummy outpoint if collateral is nullopt
pl.scriptPayout = GetScriptForDestination(payoutAddr);
if (operatorPayoutAddr) {
pl.nOperatorReward = *operatorPercentage;
Expand All @@ -337,29 +340,68 @@ static CallResult<uint256> createDMNInternal(const COutPoint& collateral,
// make sure fee calculation works
pl.vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE);

CMutableTransaction tx;
tx.nVersion = CTransaction::TxVersion::SAPLING;
tx.nType = CTransaction::TxType::PROREG;

auto wallet = vpwallets[0]; // TODO: Move to walletModel
auto res = FundSpecialTx(wallet, tx, pl);
if (!res) return {res.getError()};

res = SignSpecialTxPayloadByString(pl, keyCollateral);
if (!res) return {res.getError()};

std::map<std::string, std::string> extraValues;
if (operatorSk) {
// Only if the operator sk was provided
extraValues.emplace("operatorSk", bls::EncodeSecret(Params(), *operatorSk));
}
res = SignAndSendSpecialTx(wallet, tx, pl, &extraValues);
return res ? CallResult<uint256>(tx.GetHash()) :
auto wallet = vpwallets[0]; // TODO: Move to walletModel
if(collateral){
if(!keyCollateral){
return CallResult<uint256>("null key collateral");
}
CMutableTransaction tx;
tx.nVersion = CTransaction::TxVersion::SAPLING;
tx.nType = CTransaction::TxType::PROREG;
auto res = FundSpecialTx(wallet, tx, pl);
if (!res) return {res.getError()};

res = SignSpecialTxPayloadByString(pl, *keyCollateral);
if (!res) return {res.getError()};
res = SignAndSendSpecialTx(wallet, tx, pl, &extraValues);
return res ? CallResult<uint256>(tx.GetHash()) :
CallResult<uint256>(translateRejectionError(res.getError()));
}else{
if(!addr_label){
return CallResult<uint256>("Null address label");
}
std::string alias = addr_label->toStdString();
CTransactionRef ret_tx;
auto r = walletModel->getNewAddress(alias);
QString returnStr;

//CmutTx used only to compute the size of payload
CMutableTransaction tx_test;
tx_test.nVersion= CTransaction::TxVersion::SAPLING;
tx_test.nType = CTransaction::TxType::PROREG;
SetTxPayload(tx_test, pl);
const int nExtraSize = int(GetSerializeSize(tx_test.extraPayload)+GetSerializeSize(tx_test.sapData));

COutPoint collateral_outpoint;
if (!r) return CallResult<uint256>(translateRejectionError(r.getError()));
if (!createDMNInternalCollateral(*addr_label,
QString::fromStdString(r.getObjResult()->ToString()),
ret_tx,
collateral_outpoint,
returnStr,nExtraSize)) {
//error str set internally
return CallResult<uint256>(returnStr.toStdString());
}
pl.collateralOutpoint = collateral_outpoint;
CMutableTransaction tx = CMutableTransaction(*ret_tx);
tx.nVersion = CTransaction::TxVersion::SAPLING;
tx.nType = CTransaction::TxType::PROREG;
pl.vchSig.clear();
UpdateSpecialTxInputsHash(tx, pl);
auto res = SignAndSendSpecialTx(wallet, tx, pl, &extraValues);
return res ? CallResult<uint256>(tx.GetHash()) :
CallResult<uint256>(translateRejectionError(res.getError()));
}
}

CallResult<uint256> MNModel::createDMN(const std::string& alias,
const COutPoint& collateral,
const Optional<COutPoint>& collateral,
const Optional<QString>& addr_label,
std::string& serviceAddr,
const std::string& servicePort,
const CKeyID& ownerAddr,
Expand All @@ -370,18 +412,16 @@ CallResult<uint256> MNModel::createDMN(const std::string& alias,
const Optional<uint16_t>& operatorPercentage,
const Optional<CKeyID>& operatorPayoutAddr)
{
// Parse and validate inputs

// Different DMN creation types:
// 1. internal.
// 2. external.
// 3. fund.

//Either one of them must be non null
auto p_wallet = vpwallets[0]; // TODO: Move to walletModel
const auto& chainparams = Params();

// 1) Create the simplest DMN, the collateral was generated by this wallet.

CService service;
if (!serviceAddr.empty()) {
if (!Lookup(serviceAddr+":"+servicePort, service, chainparams.GetDefaultPort(), false)) {
Expand All @@ -391,11 +431,15 @@ CallResult<uint256> MNModel::createDMN(const std::string& alias,
}

CPubKey pubKeyCollateral;
CKey keyCollateral;
if (!p_wallet->GetMasternodeVinAndKeys(pubKeyCollateral, keyCollateral, collateral, false, strError)) {
Optional<CKey> keyCollateral = nullopt;

if (collateral){
keyCollateral = CKey();
if(!p_wallet->GetMasternodeVinAndKeys(pubKeyCollateral, *keyCollateral, *collateral, false, strError)) {
return {strError};
}
}

// parse operator pubkey or create one
Optional<CBLSSecretKey> operatorSk{nullopt};
CBLSPublicKey operatorPk;
Expand All @@ -414,6 +458,7 @@ CallResult<uint256> MNModel::createDMN(const std::string& alias,
}

auto res = createDMNInternal(collateral,
addr_label,
keyCollateral,
service,
ownerAddr,
Expand Down Expand Up @@ -475,8 +520,62 @@ OperationResult MNModel::killDMN(const uint256& collateralHash, unsigned int out

return {true};
}
//This functions create a collateral that will be "locked" inside the ProRegTx (so INTERNAL collateral)
bool MNModel::createDMNInternalCollateral(
const QString& alias,
const QString& addr,
CTransactionRef& ret_tx,
COutPoint& ret_outpoint,
QString& ret_error,
int nExtraSize
){
SendCoinsRecipient sendCoinsRecipient(addr, alias, getMNCollateralRequiredAmount(), "");

// Send the 10 tx to one of your address
QList<SendCoinsRecipient> recipients;
recipients.append(sendCoinsRecipient);
WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
// no coincontrol, no P2CS delegations
prepareStatus = walletModel->prepareTransaction(&currentTransaction, nullptr, false,nExtraSize);
ret_tx = currentTransaction.getTransaction();

QString returnMsg = tr("Unknown error");
// process prepareStatus and on error generate message shown to user
CClientUIInterface::MessageBoxFlags informType;
returnMsg = GuiTransactionsUtils::ProcessSendCoinsReturn(
prepareStatus,
walletModel,
informType, // this flag is not needed
BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(),
currentTransaction.getTransactionFee()),
true
);

if (prepareStatus.status != WalletModel::OK) {
ret_error = tr("Prepare master node failed.\n\n%1\n").arg(returnMsg);
return false;
}

int indexOut = -1;
for (int i=0; i < (int)ret_tx->vout.size(); i++) {
const CTxOut& out = ret_tx->vout[i];
if (out.nValue == getMNCollateralRequiredAmount()) {
indexOut = i;
break;
}
}
if (indexOut == -1) {
ret_error = tr("Invalid collateral output index");
return false;
}
// save the collateral outpoint
ret_outpoint = COutPoint(UINT256_ZERO, indexOut); //generalise to second case
return true;
}

bool MNModel::createMNCollateral(
//This functions creates and send an EXTERNAL collateral the ProRegTx will just reference it
bool MNModel::createDMNExternalCollateral(
const QString& alias,
const QString& addr,
COutPoint& ret_outpoint,
Expand Down
20 changes: 18 additions & 2 deletions src/qt/pivx/mnmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "masternodeconfig.h"
#include "operationresult.h"
#include "primitives/transaction.h"
#include "bls/key_io.h"
#include "wallet/wallet.h" // TODO: Move to walletModel

class CMasternode;
class DMNView;
Expand Down Expand Up @@ -110,7 +112,8 @@ class MNModel : public QAbstractTableModel

// Creates the DMN and return the hash of the proregtx
CallResult<uint256> createDMN(const std::string& alias,
const COutPoint& collateral,
const Optional<COutPoint>& collateral,
const Optional<QString>& addr_label,
std::string& serviceAddr,
const std::string& servicePort,
const CKeyID& ownerAddr,
Expand All @@ -124,8 +127,10 @@ class MNModel : public QAbstractTableModel
// Completely stops the Masternode spending the collateral
OperationResult killDMN(const uint256& collateralHash, unsigned int outIndex);


// Generates the collateral transaction
bool createMNCollateral(const QString& alias, const QString& addr, COutPoint& ret_outpoint, QString& ret_error);
bool createDMNExternalCollateral(const QString& alias, const QString& addr, COutPoint& ret_outpoint, QString& ret_error);
bool createDMNInternalCollateral(const QString& alias, const QString& addr, CTransactionRef& ret_tx,COutPoint& ret_outpoint, QString& ret_error,int nExtraSize=0);
// Creates the mnb and broadcast it to the network
bool startLegacyMN(const CMasternodeConfig::CMasternodeEntry& mne, int chainHeight, std::string& strError);
void startAllLegacyMNs(bool onlyMissing, int& amountOfMnFailed, int& amountOfMnStarted,
Expand All @@ -148,6 +153,17 @@ class MNModel : public QAbstractTableModel
QMap<std::string, bool> collateralTxAccepted;

const MasternodeWrapper* getMNWrapper(const QString& mnAlias);
CallResult<uint256> createDMNInternal(const Optional<COutPoint>& collateral,
const Optional<QString>& addr_label,
const Optional<CKey>& keyCollateral,
const CService& service,
const CKeyID& ownerAddr,
const CBLSPublicKey& operatorPubKey,
const Optional<CKeyID>& votingAddr,
const CKeyID& payoutAddr,
const Optional<CBLSSecretKey>& operatorSk,
const Optional<uint16_t>& operatorPercentage,
const Optional<CKeyID>& operatorPayoutAddr);
};

#endif // MNMODEL_H
4 changes: 2 additions & 2 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ bool WalletModel::addKeys(const CKey& key, const CPubKey& pubkey, WalletRescanRe
return true;
}

WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction* transaction, const CCoinControl* coinControl, bool fIncludeDelegations)
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction* transaction, const CCoinControl* coinControl, bool fIncludeDelegations, int nExtraSize)
{
CAmount total = 0;
QList<SendCoinsRecipient> recipients = transaction->getRecipients();
Expand Down Expand Up @@ -540,7 +540,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
true,
0,
fIncludeDelegations,
&transaction->fIsStakeDelegationVoided);
&transaction->fIsStakeDelegationVoided,nExtraSize);
transaction->setTransactionFee(nFeeRequired);

if (!fCreated) {
Expand Down
2 changes: 1 addition & 1 deletion src/qt/walletmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ class WalletModel : public QObject
const CWalletTx* getTx(uint256 id);

// prepare transaction for getting txfee before sending coins
SendCoinsReturn prepareTransaction(WalletModelTransaction* transaction, const CCoinControl* coinControl = NULL, bool fIncludeDelegations = true);
SendCoinsReturn prepareTransaction(WalletModelTransaction* transaction, const CCoinControl* coinControl = NULL, bool fIncludeDelegations = true, int nExtraSize=0);

// Send coins to a list of recipients
SendCoinsReturn sendCoins(WalletModelTransaction& transaction);
Expand Down