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

wallet, contract: Implement contract change option #2388

Merged
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
19 changes: 16 additions & 3 deletions src/gridcoin/contract/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,26 @@ bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, CAmount burn_
CCoinControl coin_control_out;
CAmount applied_fee_out; // Unused
bool admin = false;
bool contract_change_to_input_address = gArgs.GetBoolArg("-contractchangetoinputaddress", false);
CTxDestination out_address {CNoDestination()};

// If the input transaction already selected some inputs, ensure that we
// pick those inputs again when creating the final transaction:
//
for (auto& txin : wtx_out.vin) {
coin_control_out.Select(txin.prevout);

// If contract_change_to_input_address is false or once the first already selected input is encountered
// that has a valid address, select that for change and then skip over further address selection for change.
if (!contract_change_to_input_address
|| !std::get_if<CNoDestination>(&out_address)
|| !ExtractDestination(txin.scriptSig, out_address)) {
continue;
}

coin_control_out.destChange = out_address;

LogPrintf("INFO: %s: Change sent to %s for contract transaction per contractchangetoinputaddress setting.",
__func__, CBitcoinAddress(coin_control_out.destChange).ToString());
}

for (const auto& contract : wtx_out.vContracts) {
Expand All @@ -82,7 +96,6 @@ bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, CAmount burn_
// Nodes validate administrative contracts by checking that the containing
// transactions include an input signed by the master key, so select coins
// from the master address and send any change back to it:
//
if (admin && !SelectMasterInputOutput(coin_control_out)) {
return false;
}
Expand All @@ -100,7 +113,7 @@ bool CreateContractTx(CWalletTx& wtx_out, CReserveKey reserve_key, CAmount burn_
wtx_out,
reserve_key,
applied_fee_out,
&coin_control_out);
&coin_control_out, contract_change_to_input_address);
}

//!
Expand Down
4 changes: 3 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,9 @@ void SetupServerArgs()
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-maxsigcachesize=<n>", "Set maximum size for signature cache (default: 50000)",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);

argsman.AddArg("-contractchangetoinputaddress", "Change from a contract transaction is returned to an input address "
"rather than creating a new change address (default: 0)",
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::WALLET);

// Connections
argsman.AddArg("-timeout=<n>", "Specify connection timeout in milliseconds (default: 5000)",
Expand Down
7 changes: 7 additions & 0 deletions src/qt/forms/optionsdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="returnChangeToInputAddressForContracts">
<property name="text">
<string>Return change to an input address for contract transactions</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_Main">
<property name="orientation">
Expand Down
1 change: 1 addition & 0 deletions src/qt/optionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->gridcoinAtStartup, OptionsModel::StartAtStartup);
mapper->addMapping(ui->gridcoinAtStartupMinimised, OptionsModel::StartMin);
mapper->addMapping(ui->disableUpdateCheck, OptionsModel::DisableUpdateCheck);
mapper->addMapping(ui->returnChangeToInputAddressForContracts, OptionsModel::ContractChangeToInput);

/* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
Expand Down
10 changes: 10 additions & 0 deletions src/qt/optionsmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
case MinStakeSplitValue:
// This comes from the core and is a read-write setting (see below).
return QVariant((qint64) gArgs.GetArg("-minstakesplitvalue", MIN_STAKE_SPLIT_VALUE_GRC));
case ContractChangeToInput:
// This comes from the core and is a read-write setting (see below).
return QVariant(gArgs.GetBoolArg("-contractchangetoinputaddress", false));
default:
return QVariant();
}
Expand Down Expand Up @@ -303,6 +306,13 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
//config file.
gArgs.ForceSetArg("-minstakesplitvalue", value.toString().toStdString());
updateRwSetting("minstakesplitvalue", gArgs.GetArg("-minstakesplitvalue", MIN_STAKE_SPLIT_VALUE_GRC));
break;
case ContractChangeToInput:
// This is a core setting stored in the read-write settings file and once set will override the read-only
//config file.
gArgs.ForceSetArg("-contractchangetoinputaddress", value.toBool() ? "1" : "0");
updateRwSetting("contractchangetoinputaddress", gArgs.GetBoolArg("contractchangetoinputaddress"));
break;
default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/qt/optionsmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class OptionsModel : public QAbstractListModel
EnableStakeSplit, // bool
StakingEfficiency, // double
MinStakeSplitValue, // int
ContractChangeToInput, // bool
OptionIDRowCount
};

Expand Down
73 changes: 47 additions & 26 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1770,7 +1770,8 @@ bool CWallet::SelectCoinsForStaking(unsigned int nSpendTime, std::vector<pair<co
}

bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, set<pair<const CWalletTx*,unsigned int>>& setCoins_in,
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl)
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl,
bool change_back_to_input_address)
{

int64_t nValueOut = 0;
Expand Down Expand Up @@ -1938,31 +1939,50 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
CScript scriptChange;

// coin control: send change to custom address
if (coinControl && !std::get_if<CNoDestination>(&coinControl->destChange))
scriptChange.SetDestination(coinControl->destChange);
if (coinControl && !std::get_if<CNoDestination>(&coinControl->destChange)) {
LogPrintf("INFO: %s: Setting custom change address: %s", __func__,
CBitcoinAddress(coinControl->destChange).ToString());

// no coin control: send change to newly generated address
else
{
// Note: We use a new key here to keep it from being obvious which side is the change.
// The drawback is that by not reusing a previous key, the change may be lost if a
// backup is restored, if the backup doesn't have the new private key for the change.
// If we reused the old key, it would be possible to add code to look for and
// rediscover unknown transactions that were written with keys of ours to recover
// post-backup change.

// Reserve a new key pair from key pool
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey))
{
LogPrintf("Keypool ran out, please call keypoolrefill first");
return false;
scriptChange.SetDestination(coinControl->destChange);
} else { // no coin control
if (change_back_to_input_address) { // send change back to an existing input address
CTxDestination change_address;

if (!setCoins_out.empty()) {
// Select the first input with a valid address as the change address. This seems as good
// a choice as any, and is the fastest.
for (const auto& input : setCoins_out) {
if (ExtractDestination(input.first->vout[input.second].scriptPubKey, change_address)) {
scriptChange.SetDestination(change_address);

break;
}
}

LogPrintf("INFO: %s: Sending change to input address %s", __func__,
CBitcoinAddress(change_address).ToString());
}
} else { // send change to newly generated address
// Note: We use a new key here to keep it from being obvious which side is the change.
// The drawback is that by not reusing a previous key, the change may be lost if a
// backup is restored, if the backup doesn't have the new private key for the change.
// If we reused the old key, it would be possible to add code to look for and
// rediscover unknown transactions that were written with keys of ours to recover
// post-backup change.

// Reserve a new key pair from key pool
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey))
{
LogPrintf("Keypool ran out, please call keypoolrefill first");
return false;
}

scriptChange.SetDestination(vchPubKey.GetID());
}

scriptChange.SetDestination(vchPubKey.GetID());
}

// Insert change txn at random position:
// Insert change output at random position in the transaction:
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
}
Expand Down Expand Up @@ -2054,22 +2074,23 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
}

bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey,
int64_t& nFeeRet, const CCoinControl* coinControl)
int64_t& nFeeRet, const CCoinControl* coinControl, bool change_back_to_input_address)
{
// Initialize setCoins empty to let CreateTransaction choose via SelectCoins...
set<pair<const CWalletTx*,unsigned int>> setCoins;

return CreateTransaction(vecSend, setCoins, wtxNew, reservekey, nFeeRet, coinControl);
return CreateTransaction(vecSend, setCoins, wtxNew, reservekey, nFeeRet, coinControl, change_back_to_input_address);
}




bool CWallet::CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl)
bool CWallet::CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey,
int64_t& nFeeRet, const CCoinControl* coinControl, bool change_back_to_input_address)
{
vector< pair<CScript, int64_t> > vecSend;
vecSend.push_back(make_pair(scriptPubKey, nValue));
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, coinControl);
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, coinControl, change_back_to_input_address);
}

// Call after CreateTransaction unless you want to abort
Expand Down
7 changes: 4 additions & 3 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,12 @@ class CWallet : public CCryptoKeyStore
int64_t GetStake() const;
int64_t GetNewMint() const;
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t>>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey,
int64_t& nFeeRet, const CCoinControl* coinControl = nullptr);
int64_t& nFeeRet, const CCoinControl* coinControl = nullptr, bool change_back_to_input_address = false);
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t>>& vecSend, std::set<std::pair<const CWalletTx*,unsigned int>>& setCoins,
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr);
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl = nullptr,
bool change_back_to_input_address = false);
bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet,
const CCoinControl* coinControl = nullptr);
const CCoinControl* coinControl = nullptr, bool change_back_to_input_address = false);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);

std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false);
Expand Down