mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
Merge #6131: feat: make a support of Qt app to show Platform transfer Tx
21f174aff1
feat: improve query categorisation in Qt App (Konstantin Akimov)c863473286
test: add spending asset unlock tx in functional tests (Konstantin Akimov)1fb67ece0e
feat: make a support of Qt app to show Platform Transfer transaction as a new type of transaction (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented Transfers from platform have incorrectly shown amount in Dash Core wallet app. They also shown in Qt app as self-send that is not completely true. ## What was done? Added new type of transaction to Qt App, added a filter for its type, fixed calculation of output for tx records. As well added a new type of transaction `platform-transfer` in rpc output of `gettransaction` RPC ## How Has This Been Tested? Make a Platform Transfer transaction on RegTest and check it in Dash Core ![image](https://github.com/user-attachments/assets/16c83f09-724f-4b8b-99c8-9bb0df1428da) Helper to see it: export dpath=/tmp/dash_func_test_PATHPATH/ ; src/qt/dash-qt -regtest -conf=$dpath/node0/dash.conf -datadir=$dpath/node0/ -debug=0 -debuglogfile=/dev/stdout ## Breaking Changes There's new type of transaction "platform-transfer" in rpc output of `gettransaction`. **This PR DOES NOT change any consensus rules.** Breaking changes that makes withdrawal transaction immature is moved to https://github.com/dashpay/dash/pull/6128 ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone Top commit has no ACKs. Tree-SHA512: ec2a54a910f121ad30ff8e94cf17080b5b3c651872e9bc3de9ec0924ca7f7a0e526b74b05cde26aaf860e3809e67f66142112319a69c216527e5bcb1b8a2b8f6
This commit is contained in:
parent
80ed27914e
commit
8e70262db4
@ -408,6 +408,7 @@ struct WalletTx
|
||||
int64_t time;
|
||||
std::map<std::string, std::string> value_map;
|
||||
bool is_coinbase;
|
||||
bool is_platform_transfer{false};
|
||||
bool is_denominate;
|
||||
};
|
||||
|
||||
|
@ -264,6 +264,11 @@ public:
|
||||
return nVersion >= SPECIAL_VERSION;
|
||||
}
|
||||
|
||||
bool IsPlatformTransfer() const noexcept
|
||||
{
|
||||
return IsSpecialTxVersion() && nType == TRANSACTION_ASSET_UNLOCK;
|
||||
}
|
||||
|
||||
bool HasExtraPayloadField() const noexcept
|
||||
{
|
||||
return IsSpecialTxVersion() && nType != TRANSACTION_NORMAL;
|
||||
|
@ -93,6 +93,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
{
|
||||
strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
|
||||
}
|
||||
else if (wtx.is_platform_transfer)
|
||||
{
|
||||
strHTML += "<b>" + tr("Source") + ":</b> " + tr("Platform Transfer") + "<br>";
|
||||
}
|
||||
else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty())
|
||||
{
|
||||
// Online transaction
|
||||
|
@ -39,7 +39,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(interfaces::Wal
|
||||
auto node = interfaces::MakeNode();
|
||||
auto& coinJoinOptions = node->coinJoinOptions();
|
||||
|
||||
if (nNet > 0 || wtx.is_coinbase)
|
||||
if (nNet > 0 || wtx.is_coinbase || wtx.is_platform_transfer)
|
||||
{
|
||||
//
|
||||
// Credit
|
||||
@ -74,6 +74,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(interfaces::Wal
|
||||
// Generated
|
||||
sub.type = TransactionRecord::Generated;
|
||||
}
|
||||
if (wtx.is_platform_transfer)
|
||||
{
|
||||
// Withdrawal from platform
|
||||
sub.type = TransactionRecord::PlatformTransfer;
|
||||
}
|
||||
|
||||
parts.append(sub);
|
||||
}
|
||||
|
@ -96,7 +96,8 @@ public:
|
||||
CoinJoinCollateralPayment,
|
||||
CoinJoinMakeCollaterals,
|
||||
CoinJoinCreateDenominations,
|
||||
CoinJoinSend
|
||||
CoinJoinSend,
|
||||
PlatformTransfer,
|
||||
};
|
||||
|
||||
/** Number of confirmation recommended for accepting a transaction */
|
||||
|
@ -431,6 +431,8 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
|
||||
return tr("Payment to yourself");
|
||||
case TransactionRecord::Generated:
|
||||
return tr("Mined");
|
||||
case TransactionRecord::PlatformTransfer:
|
||||
return tr("Platform Transfer");
|
||||
|
||||
case TransactionRecord::CoinJoinMixing:
|
||||
return tr("%1 Mixing").arg(QString::fromStdString(gCoinJoinName));
|
||||
@ -443,9 +445,10 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
|
||||
case TransactionRecord::CoinJoinSend:
|
||||
return tr("%1 Send").arg(QString::fromStdString(gCoinJoinName));
|
||||
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
case TransactionRecord::Other:
|
||||
break; // use fail-over here
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx) const
|
||||
@ -473,14 +476,20 @@ QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, b
|
||||
case TransactionRecord::SendToAddress:
|
||||
case TransactionRecord::Generated:
|
||||
case TransactionRecord::CoinJoinSend:
|
||||
case TransactionRecord::PlatformTransfer:
|
||||
return formatAddressLabel(wtx->strAddress, wtx->label, tooltip) + watchAddress;
|
||||
case TransactionRecord::SendToOther:
|
||||
return QString::fromStdString(wtx->strAddress) + watchAddress;
|
||||
case TransactionRecord::SendToSelf:
|
||||
return formatAddressLabel(wtx->strAddress, wtx->label, tooltip) + watchAddress;
|
||||
default:
|
||||
return tr("(n/a)") + watchAddress;
|
||||
}
|
||||
case TransactionRecord::CoinJoinMixing:
|
||||
case TransactionRecord::CoinJoinCollateralPayment:
|
||||
case TransactionRecord::CoinJoinMakeCollaterals:
|
||||
case TransactionRecord::CoinJoinCreateDenominations:
|
||||
case TransactionRecord::Other:
|
||||
break; // use fail-over here
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
return tr("(n/a)") + watchAddress;
|
||||
}
|
||||
|
||||
QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
|
||||
@ -491,6 +500,7 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
|
||||
case TransactionRecord::RecvWithAddress:
|
||||
case TransactionRecord::SendToAddress:
|
||||
case TransactionRecord::Generated:
|
||||
case TransactionRecord::PlatformTransfer:
|
||||
case TransactionRecord::CoinJoinSend:
|
||||
case TransactionRecord::RecvWithCoinJoin:
|
||||
{
|
||||
@ -504,9 +514,11 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
|
||||
case TransactionRecord::CoinJoinMakeCollaterals:
|
||||
case TransactionRecord::CoinJoinCollateralPayment:
|
||||
return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BAREADDRESS);
|
||||
default:
|
||||
case TransactionRecord::SendToOther:
|
||||
case TransactionRecord::RecvFromOther:
|
||||
case TransactionRecord::Other:
|
||||
break;
|
||||
}
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::DEFAULT);
|
||||
}
|
||||
|
||||
@ -530,6 +542,7 @@ QVariant TransactionTableModel::amountColor(const TransactionRecord *rec) const
|
||||
case TransactionRecord::RecvWithCoinJoin:
|
||||
case TransactionRecord::RecvWithAddress:
|
||||
case TransactionRecord::RecvFromOther:
|
||||
case TransactionRecord::PlatformTransfer:
|
||||
return GUIUtil::getThemedQColor(GUIUtil::ThemedColor::GREEN);
|
||||
case TransactionRecord::CoinJoinSend:
|
||||
case TransactionRecord::SendToAddress:
|
||||
|
@ -90,6 +90,7 @@ TransactionView::TransactionView(QWidget* parent) :
|
||||
typeWidget->addItem(tr("%1 Collateral Payment").arg(strCoinJoinName), TransactionFilterProxy::TYPE(TransactionRecord::CoinJoinCollateralPayment));
|
||||
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
|
||||
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
|
||||
typeWidget->addItem(tr("Platform Transfer"), TransactionFilterProxy::TYPE(TransactionRecord::PlatformTransfer));
|
||||
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
|
||||
typeWidget->setCurrentIndex(settings.value("transactionType").toInt());
|
||||
|
||||
|
@ -81,6 +81,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
|
||||
result.time = wtx.GetTxTime();
|
||||
result.value_map = wtx.mapValue;
|
||||
result.is_coinbase = wtx.IsCoinBase();
|
||||
result.is_platform_transfer = wtx.IsPlatformTransfer();
|
||||
// The determination of is_denominate is based on simplified checks here because in this part of the code
|
||||
// we only want to know about mixing transactions belonging to this specific wallet.
|
||||
result.is_denominate = wtx.tx->vin.size() == wtx.tx->vout.size() && // Number of inputs is same as number of outputs
|
||||
|
@ -167,6 +167,8 @@ static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniVa
|
||||
entry.pushKV("chainlock", chainlock);
|
||||
if (wtx.IsCoinBase())
|
||||
entry.pushKV("generated", true);
|
||||
if (wtx.IsPlatformTransfer())
|
||||
entry.pushKV("platform-transfer", true);
|
||||
if (confirms > 0)
|
||||
{
|
||||
entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
|
||||
@ -1419,6 +1421,10 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
|
||||
else
|
||||
entry.pushKV("category", "generate");
|
||||
}
|
||||
else if (wtx.IsPlatformTransfer())
|
||||
{
|
||||
entry.pushKV("category", "platform-transfer");
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.pushKV("category", "receive");
|
||||
@ -1483,7 +1489,8 @@ static RPCHelpMan listtransactions()
|
||||
"\"receive\" Non-coinbase transactions received.\n"
|
||||
"\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
|
||||
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
|
||||
"\"orphan\" Orphaned coinbase transactions received.\n"},
|
||||
"\"orphan\" Orphaned coinbase transactions received.\n"
|
||||
"\"platform-transfer\" Platform Transfer transactions received.\n"},
|
||||
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
|
||||
"for all other categories"},
|
||||
{RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
|
||||
@ -1599,7 +1606,8 @@ static RPCHelpMan listsinceblock()
|
||||
"\"receive\" Non-coinbase transactions received.\n"
|
||||
"\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
|
||||
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
|
||||
"\"orphan\" Orphaned coinbase transactions received.\n"},
|
||||
"\"orphan\" Orphaned coinbase transactions received.\n"
|
||||
"\"platform-transfer\" Platform Transfer transactions received.\n"},
|
||||
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
|
||||
"for all other categories"},
|
||||
{RPCResult::Type::NUM, "vout", "the vout value"},
|
||||
@ -1740,7 +1748,8 @@ static RPCHelpMan gettransaction()
|
||||
"\"receive\" Non-coinbase transactions received.\n"
|
||||
"\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
|
||||
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
|
||||
"\"orphan\" Orphaned coinbase transactions received.\n"},
|
||||
"\"orphan\" Orphaned coinbase transactions received.\n"
|
||||
"\"platform-transfer\" Platform Transfer transactions received.\n"},
|
||||
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
|
||||
{RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
|
||||
{RPCResult::Type::NUM, "vout", "the vout value"},
|
||||
|
@ -591,6 +591,7 @@ public:
|
||||
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
|
||||
const uint256& GetHash() const { return tx->GetHash(); }
|
||||
bool IsCoinBase() const { return tx->IsCoinBase(); }
|
||||
bool IsPlatformTransfer() const { return tx->IsPlatformTransfer(); }
|
||||
bool IsImmatureCoinBase() const;
|
||||
|
||||
// Disable copying of CWalletTx objects to prevent bugs where instances get
|
||||
|
@ -41,6 +41,7 @@ from test_framework.util import (
|
||||
get_bip9_details,
|
||||
hex_str_to_bytes,
|
||||
)
|
||||
from test_framework.wallet_util import bytes_to_wif
|
||||
|
||||
llmq_type_test = 106 # LLMQType::LLMQ_TEST_PLATFORM
|
||||
tiny_amount = int(Decimal("0.0007") * COIN)
|
||||
@ -257,6 +258,8 @@ class AssetLocksTest(DashTestFramework):
|
||||
|
||||
key = ECKey()
|
||||
key.generate()
|
||||
privkey = bytes_to_wif(key.get_bytes())
|
||||
node_wallet.importprivkey(privkey)
|
||||
pubkey = key.get_pubkey().get_bytes()
|
||||
|
||||
self.test_asset_locks(node_wallet, node, pubkey)
|
||||
@ -478,15 +481,31 @@ class AssetLocksTest(DashTestFramework):
|
||||
self.check_mempool_result(tx=asset_unlock_tx_full, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
|
||||
|
||||
txid_in_block = self.send_tx(asset_unlock_tx_full)
|
||||
expected_balance = (Decimal(self.get_credit_pool_balance()) - Decimal(tiny_amount))
|
||||
node.generate(1)
|
||||
self.sync_all()
|
||||
self.log.info("Check txid_in_block was mined...")
|
||||
self.log.info("Check txid_in_block was mined")
|
||||
block = node.getblock(node.getbestblockhash())
|
||||
assert txid_in_block in block['tx']
|
||||
self.validate_credit_pool_balance(0)
|
||||
|
||||
self.log.info(f"Check status of withdrawal and try to spend it")
|
||||
withdrawal_status = node_wallet.gettransaction(txid_in_block)
|
||||
assert_equal(withdrawal_status['amount'] * COIN, expected_balance)
|
||||
assert_equal(withdrawal_status['details'][0]['category'], 'platform-transfer')
|
||||
|
||||
spend_withdrawal_hex = node_wallet.createrawtransaction([{'txid': txid_in_block, 'vout' : 0}], { node_wallet.getnewaddress() : (expected_balance - Decimal(tiny_amount)) / COIN})
|
||||
spend_withdrawal_hex = node_wallet.signrawtransactionwithwallet(spend_withdrawal_hex)['hex']
|
||||
spend_withdrawal = tx_from_hex(spend_withdrawal_hex)
|
||||
self.check_mempool_result(tx=spend_withdrawal, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}})
|
||||
spend_txid_in_block = self.send_tx(spend_withdrawal)
|
||||
|
||||
node.generate(1)
|
||||
block = node.getblock(node.getbestblockhash())
|
||||
assert spend_txid_in_block in block['tx']
|
||||
|
||||
self.log.info("Fast forward to the next day to reset all current unlock limits...")
|
||||
self.slowly_generate_batch(blocks_in_one_day + 1)
|
||||
self.slowly_generate_batch(blocks_in_one_day)
|
||||
self.mine_quorum(llmq_type_name="llmq_test_platform", llmq_type=106)
|
||||
|
||||
total = self.get_credit_pool_balance()
|
||||
|
Loading…
Reference in New Issue
Block a user