diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index dec30dde02..7ac6a59db6 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -661,6 +661,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor return res; } + if (height >= static_cast(Params().GetConsensus().EunosHeight) && !HasAuth(tx.vout[1].scriptPubKey)) { + return Res::Err("masternode creation needs owner auth"); + } + CMasternode node; CTxDestination dest; if (ExtractDestination(tx.vout[1].scriptPubKey, dest)) { diff --git a/src/masternodes/rpc_masternodes.cpp b/src/masternodes/rpc_masternodes.cpp index 9dfab15c5b..9cf27bf7d1 100644 --- a/src/masternodes/rpc_masternodes.cpp +++ b/src/masternodes/rpc_masternodes.cpp @@ -120,22 +120,32 @@ UniValue createmasternode(const JSONRPCRequest& request) const auto txVersion = GetTransactionVersion(targetHeight); CMutableTransaction rawTx(txVersion); - if (request.params.size() > 2) { - rawTx.vin = GetInputs(request.params[2].get_array()); + CTransactionRef optAuthTx; + auto scriptOwner = GetScriptForDestination(ownerDest); + std::set auths{scriptOwner}; + rawTx.vin = GetAuthInputsSmart(pwallet, rawTx.nVersion, auths, false, optAuthTx, request.params[2]); + + // Return change to owner address + CCoinControl coinControl; + if (IsValidDestination(ownerDest)) { + coinControl.destChange = ownerDest; } rawTx.vout.push_back(CTxOut(EstimateMnCreationFee(targetHeight), scriptMeta)); - rawTx.vout.push_back(CTxOut(GetMnCollateralAmount(targetHeight), GetScriptForDestination(ownerDest))); + rawTx.vout.push_back(CTxOut(GetMnCollateralAmount(targetHeight), scriptOwner)); - fund(rawTx, pwallet, {}); + fund(rawTx, pwallet, optAuthTx, &coinControl); // check execution { LOCK(cs_main); + CCoinsViewCache coins(&::ChainstateActive().CoinsTip()); + if (optAuthTx) + AddCoins(coins, *optAuthTx, targetHeight); auto metadata = ToByteVector(CDataStream{SER_NETWORK, PROTOCOL_VERSION, static_cast(operatorDest.which()), operatorAuthKey}); - execTestTx(CTransaction(rawTx), targetHeight, metadata, CCreateMasterNodeMessage{}); + execTestTx(CTransaction(rawTx), targetHeight, metadata, CCreateMasterNodeMessage{}, coins); } - return signsend(rawTx, pwallet, {})->GetHash().GetHex(); + return signsend(rawTx, pwallet, optAuthTx)->GetHash().GetHex(); } UniValue resignmasternode(const JSONRPCRequest& request) diff --git a/test/functional/feature_auth_return_change.py b/test/functional/feature_auth_return_change.py index 762c26206a..62b4b94ff5 100755 --- a/test/functional/feature_auth_return_change.py +++ b/test/functional/feature_auth_return_change.py @@ -31,7 +31,11 @@ def check_auto_auth_txs(self, tx, owner, outputs = 2): # Auth TX outputs all belong to auth address assert_equal(auth_tx['vout'][1]['scriptPubKey']['addresses'][0], owner) - assert_equal(len(auth_tx['vout']), 2) + decTx = self.nodes[0].getrawtransaction(tx) + customTx = self.nodes[0].decodecustomtx(decTx) + vouts = 2 + if customTx['type'] == 'ResignMasternode': vouts = 3 + assert_equal(len(auth_tx['vout']), vouts) # Two outputs, single input and change to auth address on final TX assert_equal(final_rawtx['vout'][1]['scriptPubKey']['addresses'][0], owner) diff --git a/test/functional/feature_burn_address.py b/test/functional/feature_burn_address.py index a9aa1daa66..1f74d46c34 100755 --- a/test/functional/feature_burn_address.py +++ b/test/functional/feature_burn_address.py @@ -34,7 +34,7 @@ def run_test(self): # Check create masternode burn fee result = self.nodes[0].listburnhistory() assert_equal(result[0]['owner'][0:16], "6a1a446654784301") # OP_RETURN data - assert_equal(result[0]['txn'], 1) + assert_equal(result[0]['txn'], 2) assert_equal(result[0]['type'], 'CreateMasternode') assert_equal(result[0]['amounts'][0], '1.00000000@DFI') diff --git a/test/functional/rpc_mn_basic.py b/test/functional/rpc_mn_basic.py index a4b4d63884..6e05c8f11d 100755 --- a/test/functional/rpc_mn_basic.py +++ b/test/functional/rpc_mn_basic.py @@ -21,9 +21,9 @@ class MasternodesRpcBasicTest (DefiTestFramework): def set_test_params(self): self.num_nodes = 3 self.setup_clean_chain = True - self.extra_args = [['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=50', '-dakotaheight=136'], - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=50', '-dakotaheight=136'], - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=50', '-dakotaheight=136']] + self.extra_args = [['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=50', '-dakotaheight=136', '-eunosheight=140'], + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=50', '-dakotaheight=136', '-eunosheight=140'], + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=50', '-dakotaheight=136', '-eunosheight=140']] def run_test(self): assert_equal(len(self.nodes[0].listmasternodes()), 8) @@ -56,6 +56,8 @@ def run_test(self): collateral0 ) + rawTx0 = self.nodes[0].getrawtransaction(idnode0) + decodeTx0 = self.nodes[0].decoderawtransaction(rawTx0) # Create and sign (only) collateral spending tx spendTx = self.nodes[0].createrawtransaction([{'txid':idnode0, 'vout':1}],[{collateral0:9.999}]) signedTx = self.nodes[0].signrawtransactionwithwallet(spendTx) @@ -148,7 +150,7 @@ def run_test(self): self.sync_blocks(self.nodes[0:3]) assert_equal(len(self.nodes[0].listmasternodes()), 8) # fundingTx is removed for a block - assert_equal(self.nodes[0].getrawmempool(), [idnode0]) + assert_equal(len(self.nodes[0].getrawmempool()), 1) # auto auth collateral0 = self.nodes[0].getnewaddress("", "legacy") self.nodes[0].createmasternode(collateral0) @@ -158,5 +160,25 @@ def run_test(self): assert_equal(self.nodes[0].getblockcount(), 136) # Dakota height assert_equal(balance_before_creation - 2 + 50, self.nodes[0].getbalance()) + self.nodes[0].generate(3) + colTx = self.nodes[0].sendtoaddress(collateral0, 3) + decTx = self.nodes[0].getrawtransaction(colTx, 1) + self.nodes[0].generate(1) + for outputs in decTx['vout']: + if outputs['scriptPubKey']['addresses'][0] == collateral0: + vout = outputs['n'] + + assert_equal(self.nodes[0].getblockcount(), 140) # Eunos height + # get createmasternode data + data = decodeTx0['vout'][0]['scriptPubKey']['hex'][4:] + cmTx = self.nodes[0].createrawtransaction([{'txid':colTx,'vout':vout}], [{'data':data},{collateral1:2}]) + # HACK replace vout 0 to 1DFI + cmTx = cmTx.replace('020000000000000000', '0200e1f50500000000') + signedTx = self.nodes[0].signrawtransactionwithwallet(cmTx) + assert_equal(signedTx['complete'], True) + assert_raises_rpc_error(-26, 'masternode creation needs owner auth', self.nodes[0].sendrawtransaction, signedTx['hex']) + self.nodes[0].createmasternode(self.nodes[0].getnewaddress("", "legacy")) + self.nodes[0].generate(1) + if __name__ == '__main__': MasternodesRpcBasicTest ().main ()