Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7e063ce
[DB] Add functions to database the common sapling OVK with given seed
random-zebra Nov 26, 2020
31b549a
[Wallet] Save sapling common OVK inside the key manager
random-zebra Nov 26, 2020
71550c1
[Refactor] Don't expose getCommonOVKFromSeed
random-zebra Nov 26, 2020
eddacb9
[Wallet] Update commonOVK when setting the HD seed
random-zebra Nov 26, 2020
5b4d104
[BUG] Fix selection of correct ovk from spent note
random-zebra Nov 26, 2020
cd40162
[Wallet] Add recovering from all inputs ovk in SSPKM::TryToRecoverNote
random-zebra Nov 26, 2020
a59f0b6
[Refactor] Don't try to recover with both commonOVK and spends OVK
random-zebra Nov 26, 2020
e349ef4
Refactor: cache SaplingNoteData for externally 'sent' notes too
random-zebra Nov 26, 2020
2c56171
Wallet: Add the memo to SaplingNoteData
random-zebra Nov 27, 2020
6235acb
Refactor: remove extra decrypt operations
random-zebra Nov 27, 2020
c171cb2
DB: include new cache data of SaplingNoteData in the serialization
random-zebra Nov 27, 2020
c26c65d
GUI: Add SPORK_20 check in sendCoins
random-zebra Nov 28, 2020
6b458ae
BUG: fix correct fee/amount in decomposeShieldedDebitTransaction
random-zebra Nov 28, 2020
d2faa04
BUG: fix SSPKM::GetCredit counting externally sent notes
random-zebra Nov 28, 2020
43c1e4b
GUI: connect labels calculation in coin control for shielded outputs
random-zebra Nov 28, 2020
9737bcf
Refactor: don't save empty memos in SaplingNoteData
random-zebra Nov 28, 2020
f1529c5
GUI: CoinControlDialog::updateLabels -Loop over OutPointWrapper directly
random-zebra Nov 28, 2020
2f12aaa
Cleanup: remove unused walletModel::getOutputs
random-zebra Nov 28, 2020
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
9 changes: 5 additions & 4 deletions src/coincontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class OutPointWrapper {
public:
BaseOutPoint outPoint;
CAmount value;
bool isP2CS;

bool operator<(const OutPointWrapper& obj2) const {
return this->outPoint < obj2.outPoint;
Expand Down Expand Up @@ -69,17 +70,17 @@ class CCoinControl

bool IsSelected(const BaseOutPoint& output) const
{
return (setSelected.count(OutPointWrapper{output, 0}) > 0);
return (setSelected.count(OutPointWrapper{output, 0, false}) > 0);
}

void Select(const BaseOutPoint& output, CAmount value = 0)
void Select(const BaseOutPoint& output, CAmount value = 0, bool isP2CS = false)
{
setSelected.insert(OutPointWrapper{output, value});
setSelected.insert(OutPointWrapper{output, value, isP2CS});
}

void UnSelect(const BaseOutPoint& output)
{
setSelected.erase(OutPointWrapper{output, 0});
setSelected.erase(OutPointWrapper{output, 0, false});
}

void UnSelectAll()
Expand Down
79 changes: 36 additions & 43 deletions src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
else {
CAmount value = 0;
ParseFixedPoint(item->text(COLUMN_AMOUNT).toStdString(), 8, &value);
coinControl->Select(outpt, value);
bool isP2CS = item->data(COLUMN_CHECKBOX, Qt::UserRole) == QString("Delegated");
coinControl->Select(outpt, value, isP2CS);
}

// selection changed -> update labels
Expand Down Expand Up @@ -493,23 +494,18 @@ void CoinControlDialog::updateLabels()
"Select PIV Outputs to Spend" :
"Select Shielded PIV to Spend");

// nPayAmount
// nPayAmount (!todo fix dust)
CAmount nPayAmount = 0;
bool fDust = false;
for (const CAmount& amount : payAmounts) {
nPayAmount += amount;
if (amount > 0) {
CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0));
for (const auto& amount : payAmounts) {
nPayAmount += amount.first;
if (amount.first > 0) {
CTxOut txout(amount.first, (CScript)std::vector<unsigned char>(24, 0));
if (IsDust(txout, ::minRelayTxFee))
fDust = true;
}
}

// TODO: Connect labels calculation in the future..
if (!fSelectTransparent) {
return;
}

CAmount nAmount = 0;
CAmount nPayFee = 0;
CAmount nAfterFee = 0;
Expand All @@ -519,29 +515,16 @@ void CoinControlDialog::updateLabels()
unsigned int nQuantity = 0;

std::vector<OutPointWrapper> vCoinControl;
std::vector<COutput> vOutputs;
coinControl->ListSelected(vCoinControl);
model->getOutputs(vCoinControl, vOutputs);

for (const COutput& out : vOutputs) {
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
uint256 txhash = out.tx->GetHash();
COutPoint outpt(txhash, out.i);
if (model->isSpent(outpt)) {
coinControl->UnSelect(outpt);
continue;
}

for (const OutPointWrapper& out : vCoinControl) {
// Quantity
nQuantity++;
// Amount
nAmount += out.tx->vout[out.i].nValue;
nAmount += out.value;
// Bytes
nBytesInputs += 148;
// Additional byte for P2CS
if (out.tx->vout[out.i].scriptPubKey.IsPayToColdStaking())
nBytesInputs++;
nBytesInputs += (fSelectTransparent ? (CTXIN_SPEND_DUST_SIZE + (out.isP2CS ? 1 : 0))
: SPENDDESCRIPTION_SIZE);
}

// update SelectAll button state
Expand All @@ -550,33 +533,43 @@ void CoinControlDialog::updateLabels()
updatePushButtonSelectAll(coinControl->QuantitySelected() * 2 > nSelectableInputs);

// calculation
const int P2PKH_OUT_SIZE = 34;
const int P2CS_OUT_SIZE = 61;
if (nQuantity > 0) {
// Bytes: nBytesInputs + (num_of_outputs * bytes_per_output)
nBytes = nBytesInputs + std::max(1, payAmounts.size()) * (forDelegation ? P2CS_OUT_SIZE : P2PKH_OUT_SIZE);
bool isShieldedTx = !fSelectTransparent;
// Bytes: nBytesInputs + (sum of nBytesOutputs)
// always assume +1 (p2pkh) output for change here
nBytes += P2PKH_OUT_SIZE;
nBytes = nBytesInputs + (fSelectTransparent ? CTXOUT_REGULAR_SIZE : OUTPUTDESCRIPTION_SIZE);
for (const auto& a : payAmounts) {
bool shieldedOut = a.second;
isShieldedTx |= shieldedOut;
nBytes += (shieldedOut ? OUTPUTDESCRIPTION_SIZE
: (forDelegation ? P2CS_OUT_SIZE : CTXOUT_REGULAR_SIZE));
}

// Shielded txes must include a binding sig
if (isShieldedTx) {
nBytes + BINDINGSIG_SIZE;
}

// nVersion, nLockTime and vin/vout len sizes
nBytes += 10;

// Fee
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
// Fee (default K fixed for shielded fee for now)
nPayFee = GetMinRelayFee(nBytes, false) * (fSelectTransparent ? 1 : DEFAULT_SHIELDEDTXFEE_K);

if (nPayAmount > 0) {
nChange = nAmount - nPayFee - nPayAmount;

// Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < CENT) {
CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
if (IsDust(txout, ::minRelayTxFee)) {
nPayFee += nChange;
nChange = 0;
}
CAmount dustThreshold = fSelectTransparent ? GetDustThreshold(minRelayTxFee) :
GetShieldedDustThreshold(minRelayTxFee);
if (nChange > 0 && nChange < dustThreshold) {
nPayFee += nChange;
nChange = 0;
}

if (nChange == 0)
nBytes -= P2PKH_OUT_SIZE;
nBytes -= (fSelectTransparent ? CTXOUT_REGULAR_SIZE : SPENDDESCRIPTION_SIZE);
}

// after fee
Expand Down Expand Up @@ -825,9 +818,9 @@ void CoinControlDialog::clearPayAmounts()
payAmounts.clear();
}

void CoinControlDialog::addPayAmount(const CAmount& amount)
void CoinControlDialog::addPayAmount(const CAmount& amount, bool isShieldedRecipient)
{
payAmounts.push_back(amount);
payAmounts.emplace_back(amount, isShieldedRecipient);
}

void CoinControlDialog::updatePushButtonSelectAll(bool checked)
Expand Down
5 changes: 3 additions & 2 deletions src/qt/coincontroldialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class CoinControlDialog : public QDialog
void updateView();
void refreshDialog();
void clearPayAmounts();
void addPayAmount(const CAmount& amount);
void addPayAmount(const CAmount& amount, bool isShieldedRecipient);
void setSelectionType(bool isTransparent) { fSelectTransparent = isTransparent; }

CCoinControl* coinControl;
Expand All @@ -64,7 +64,8 @@ class CoinControlDialog : public QDialog
int sortColumn;
Qt::SortOrder sortOrder;
bool forDelegation;
QList<CAmount> payAmounts{};
// pair (recipient amount, ishielded recipient)
std::vector<std::pair<CAmount, bool>> payAmounts{};
unsigned int nSelectableInputs{0};

// whether should show available utxo or notes.
Expand Down
2 changes: 1 addition & 1 deletion src/qt/pivx/coldstakingwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ void ColdStakingWidget::setCoinControlPayAmounts()
{
if (!coinControlDialog) return;
coinControlDialog->clearPayAmounts();
coinControlDialog->addPayAmount(sendMultiRow->getAmountValue());
coinControlDialog->addPayAmount(sendMultiRow->getAmountValue(), false);
}

void ColdStakingWidget::onColdStakeClicked()
Expand Down
13 changes: 10 additions & 3 deletions src/qt/pivx/send.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,13 @@ void SendWidget::onSendClicked()

void SendWidget::ProcessSend(const QList<SendCoinsRecipient>& recipients, bool hasShieldedOutput)
{
// First check SPORK_20 (before unlock)
bool isShieldedTx = hasShieldedOutput || !isTransparent;
if (isShieldedTx && walletModel->isSaplingInMaintenance()) {
inform(tr("Sapling Protocol temporarily in maintenance. Shielded transactions disabled (SPORK 20)"));
return;
}

auto ptrUnlockedContext = MakeUnique<WalletModel::UnlockContext>(walletModel->requestUnlock());
if (!ptrUnlockedContext->isValid()) {
// Unlock wallet was cancelled
Expand All @@ -390,9 +397,9 @@ void SendWidget::ProcessSend(const QList<SendCoinsRecipient>& recipients, bool h
return;
}
ptrModelTx = new WalletModelTransaction(recipients);
ptrModelTx->useV2 = hasShieldedOutput || !isTransparent;
ptrModelTx->useV2 = isShieldedTx;

// First prepare tx
// Prepare tx
window->showHide(true);
LoadingDialog *dialog = new LoadingDialog(window, tr("Preparing transaction"));
dialog->execute(this, REQUEST_PREPARE_TX, std::move(ptrUnlockedContext));
Expand Down Expand Up @@ -685,7 +692,7 @@ void SendWidget::setCoinControlPayAmounts()
coinControlDialog->clearPayAmounts();
QMutableListIterator<SendMultiRow*> it(entries);
while (it.hasNext()) {
coinControlDialog->addPayAmount(it.next()->getAmountValue());
coinControlDialog->addPayAmount(it.next()->getAmountValue(), !isTransparent);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/qt/pivx/sendconfirmdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,9 @@ void TxDetailDialog::onOutputsClicked()
// Obtain the noteData to get the cached amount value
SaplingNoteData noteData = walletTx->mapSaplingNoteData.at(op);
Optional<libzcash::SaplingPaymentAddress> opAddr =
pwalletMain->GetSaplingScriptPubKeyMan()->GetShieldedAddressFrom(*walletTx, op);
pwalletMain->GetSaplingScriptPubKeyMan()->GetOutPointAddress(*walletTx, op);

QString labelRes = QString::fromStdString(Standard::EncodeDestination(*opAddr));
QString labelRes = opAddr ? QString::fromStdString(Standard::EncodeDestination(*opAddr)) : "";
labelRes = labelRes.left(18) + "..." + labelRes.right(18);
appendOutput(layoutGrid, i, labelRes, *noteData.amount, nDisplayUnit);

Expand Down
19 changes: 10 additions & 9 deletions src/qt/transactionrecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ bool TransactionRecord::decomposeCreditTransaction(const CWallet* wallet, const
auto sspkm = wallet->GetSaplingScriptPubKeyMan();
for (int i = 0; i < (int) wtx.sapData->vShieldedOutput.size(); ++i) {
SaplingOutPoint out(sub.hash, i);
auto opAddr = sspkm->GetShieldedAddressFrom(wtx, out);
auto opAddr = sspkm->GetOutPointAddress(wtx, out);
if (opAddr) {
// skip it if change
if (sspkm->IsNoteSaplingChange(out, *opAddr)) {
Expand Down Expand Up @@ -281,7 +281,7 @@ bool TransactionRecord::decomposeSendToSelfTransaction(const CWalletTx& wtx, con
return true;
}

bool TransactionRecord::decomposeShieldedDebitTransaction(const CWallet* wallet, const CWalletTx& wtx,
bool TransactionRecord::decomposeShieldedDebitTransaction(const CWallet* wallet, const CWalletTx& wtx, CAmount nTxFee,
bool involvesWatchAddress, QList<TransactionRecord>& parts)
{
// Return early if there are no outputs.
Expand All @@ -291,10 +291,9 @@ bool TransactionRecord::decomposeShieldedDebitTransaction(const CWallet* wallet,

TransactionRecord sub(wtx.GetHash(), wtx.GetTxTime(), wtx.GetTotalSize());
auto sspkm = wallet->GetSaplingScriptPubKeyMan();
bool feeAdded = false;
for (int i = 0; i < (int) wtx.sapData->vShieldedOutput.size(); ++i) {
SaplingOutPoint out(sub.hash, i);
auto opAddr = sspkm->GetShieldedAddressFrom(wtx, out);
auto opAddr = sspkm->GetOutPointAddress(wtx, out);
// skip change
if (!opAddr || sspkm->IsNoteSaplingChange(out, *opAddr)) {
continue;
Expand All @@ -305,9 +304,9 @@ bool TransactionRecord::decomposeShieldedDebitTransaction(const CWallet* wallet,
sub.address = KeyIO::EncodePaymentAddress(*opAddr);
CAmount nValue = sspkm->GetOutPointValue(wtx, out);
/* Add fee to first output */
if (!feeAdded) { // future, move from the hardcoded fee.
nValue += COIN;
feeAdded = true;
if (nTxFee > 0) {
nValue += nTxFee;
nTxFee = 0;
}
sub.debit = -nValue;
parts.append(sub);
Expand All @@ -327,7 +326,9 @@ bool TransactionRecord::decomposeDebitTransaction(const CWallet* wallet, const C
return false;
}

CAmount nTxFee = nDebit - wtx.GetValueOut();
// GetValueOut is the sum of transparent outs and negative sapValueBalance (shielded outs minus shielded spends).
// Therefore to get the sum of the whole outputs of the tx, must re-add the shielded inputs spent to it
CAmount nTxFee = nDebit - (wtx.GetValueOut() + wtx.GetDebit(ISMINE_SPENDABLE_SHIELDED | ISMINE_WATCH_ONLY_SHIELDED));
unsigned int txSize = wtx.GetTotalSize();
const uint256& txHash = wtx.GetHash();
const int64_t txTime = wtx.GetTxTime();
Expand Down Expand Up @@ -377,7 +378,7 @@ bool TransactionRecord::decomposeDebitTransaction(const CWallet* wallet, const C
}

// Decompose shielded debit
return decomposeShieldedDebitTransaction(wallet, wtx, involvesWatchAddress, parts);
return decomposeShieldedDebitTransaction(wallet, wtx, nTxFee, involvesWatchAddress, parts);
}

// Check whether all the shielded inputs and outputs are from and send to this wallet
Expand Down
2 changes: 1 addition & 1 deletion src/qt/transactionrecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class TransactionRecord
const CAmount& nDebit, bool involvesWatchAddress,
QList<TransactionRecord>& parts);

static bool decomposeShieldedDebitTransaction(const CWallet* wallet, const CWalletTx& wtx,
static bool decomposeShieldedDebitTransaction(const CWallet* wallet, const CWalletTx& wtx, CAmount nTxFee,
bool involvesWatchAddress, QList<TransactionRecord>& parts);

static std::string getValueOrReturnEmpty(const std::map<std::string, std::string>& mapValue, const std::string& key);
Expand Down
25 changes: 8 additions & 17 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ bool WalletModel::isColdStakingNetworkelyEnabled() const
return !sporkManager.IsSporkActive(SPORK_19_COLDSTAKING_MAINTENANCE);
}

bool WalletModel::isSaplingInMaintenance() const
{
return sporkManager.IsSporkActive(SPORK_20_SAPLING_MAINTENANCE);
}

bool WalletModel::isStakingStatusActive() const
{
return wallet && wallet->pStakerStatus && wallet->pStakerStatus->IsActive();
Expand Down Expand Up @@ -457,15 +462,15 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction& tran
bool fColdStakingActive = isColdStakingNetworkelyEnabled();
bool fSaplingActive = Params().GetConsensus().NetworkUpgradeActive(cachedNumBlocks, Consensus::UPGRADE_V5_DUMMY);

// Double check tx before do anything
// Double check the tx before doing anything
CWalletTx* newTx = transaction.getTransaction();
CValidationState state;
if (!CheckTransaction(*transaction.getTransaction(), true, true, state, true, fColdStakingActive, fSaplingActive)) {
if (!CheckTransaction(*newTx, true, true, state, true, fColdStakingActive, fSaplingActive)) {
return TransactionCheckFailed;
}

{
LOCK2(cs_main, wallet->cs_wallet);
CWalletTx* newTx = transaction.getTransaction();
QList<SendCoinsRecipient> recipients = transaction.getRecipients();

// Store PaymentRequests in wtx.vOrderForm in wallet.
Expand Down Expand Up @@ -904,20 +909,6 @@ std::string WalletModel::getLabelForAddress(const CTxDestination& address)
return label;
}

// returns a list of COutputs from COutPoints
void WalletModel::getOutputs(const std::vector<OutPointWrapper>& vOutpoints, std::vector<COutput>& vOutputs)
{
LOCK2(cs_main, wallet->cs_wallet);
for (const auto& outpoint : vOutpoints) {
const auto* tx = wallet->GetWalletTx(outpoint.outPoint.hash);
if (!tx) continue;
bool fConflicted;
const int nDepth = tx->GetDepthAndMempool(fConflicted);
if (nDepth < 0 || fConflicted) continue;
vOutputs.emplace_back(tx, outpoint.outPoint.n, nDepth, true, true);
}
}

// returns a COutPoint of 10000 PIV if found
bool WalletModel::getMNCollateralCandidate(COutPoint& outPoint)
{
Expand Down
2 changes: 1 addition & 1 deletion src/qt/walletmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class WalletModel : public QObject
bool isRegTestNetwork() const;
/** Whether cold staking is enabled or disabled in the network **/
bool isColdStakingNetworkelyEnabled() const;
bool isSaplingInMaintenance() const;
CAmount getMinColdStakingAmount() const;
/* current staking status from the miner thread **/
bool isStakingStatusActive() const;
Expand Down Expand Up @@ -281,7 +282,6 @@ class WalletModel : public QObject
bool isMine(const QString& addressStr);
bool IsShieldedDestination(const CWDestination& address);
bool isUsed(CTxDestination address);
void getOutputs(const std::vector<OutPointWrapper>& vOutpoints, std::vector<COutput>& vOutputs);
bool getMNCollateralCandidate(COutPoint& outPoint);
bool isSpent(const COutPoint& outpoint) const;

Expand Down
Loading