Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6655c50
fix(assetlock): do not punish relays of asset unlocks outside the tip…
PastaPastaPasta Sep 24, 2026
be272c2
fix(mempool): admit expired version 2 asset unlocks whose signature w…
PastaPastaPasta Sep 24, 2026
68b7d95
doc: release notes for admitting expired version 2 asset unlocks
PastaPastaPasta Sep 24, 2026
f3b5eba
fix(coinjoin): reject demotion entries for the largest denomination
knst Sep 23, 2026
970fdf5
docs: add release notes for Asset Lock v2 and DIP-18 Platform addresses
knst Sep 22, 2026
6339f9c
docs: add release notes for multi-party masternode payouts
knst Sep 22, 2026
69d330b
docs: add release notes for the removed -deprecatedrpc=fees option
knst Sep 22, 2026
90d98b9
docs: cover numeric RPC arguments in the 7099 release notes
knst Sep 22, 2026
6ca3c76
docs: describe -dustprotectionthreshold as an extension of the v23.1.…
knst Sep 22, 2026
4fa7d8f
docs: list every RPC affected by the deprecated service field
knst Sep 22, 2026
6c500b5
docs: add release notes for the removed gettxoutsetinfo bip30 field
knst Sep 22, 2026
ea2be11
docs: add release notes for the stricter bind failure handling
knst Sep 22, 2026
db036aa
docs: add release notes for quorum dkgstatus upcoming_dkgs
knst Sep 22, 2026
d1957f8
docs: add release notes for use_cj on the remaining spending RPCs
knst Sep 22, 2026
22c41b6
fix(evo): stop deriving Platform entries when raising an EvoNode to E…
knst Sep 24, 2026
a2de4c5
fix(evo): take cs_main for CompleteRepair
knst Sep 24, 2026
4a7881b
refactor(rpc): explain the createwallet load_on_startup requirement
knst Sep 24, 2026
20f4af8
fix(index): record disabled legacy indexes so their stale rows are ne…
knst Sep 23, 2026
bd32275
doc(versionbits): explain the threshold anchor in GetStateStatisticsFor
knst Sep 24, 2026
d42d149
Merge #7745: fix: multiple issues for v24 release: cj demotions, cj b…
PastaPastaPasta Sep 24, 2026
829d85f
Merge #7742: docs: updated multiple release notes and added missing r…
PastaPastaPasta Sep 24, 2026
13e43cb
Merge #7744: fix: accept expired v2 asset unlocks and stop penalizing…
PastaPastaPasta Sep 24, 2026
7b70f57
Merge #7749: fix: take cs_main for CompleteRepair
PastaPastaPasta Sep 24, 2026
a65b840
Merge #7748: fix(evo): stop deriving Platform entries when raising an…
PastaPastaPasta Sep 24, 2026
29dad5d
fix: drop historical masternode list cache entries after getmnlistd/g…
PastaPastaPasta Sep 24, 2026
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: 9 additions & 0 deletions doc/release-notes-25204.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Updated RPCs
------------

- The `deprecatedrpc=fees` configuration option has been removed. The top-level
fee fields `fee`, `modifiedfee`, `ancestorfees` and `descendantfees` are no
longer returned by RPCs `getmempoolentry`, `getrawmempool(verbose=true)`,
`getmempoolancestors(verbose=true)` and `getmempooldescendants(verbose=true)`.
The same fee fields can be accessed through the `fees` object in the result.
The top-level fee fields were previously deprecated in 23.0. (#25204)
9 changes: 9 additions & 0 deletions doc/release-notes-7099.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ Updated RPCs

* This restriction can be relaxed by setting `-deprecatedrpc=permissive_bool` at runtime
but is liable to be removed in future versions of Dash Core.

* Numeric arguments of the Dash-specific RPCs (`quorum`, `protx`, `gobject`,
`masternode` and their sub-commands) must now be passed as JSON numbers.
Strings containing a number, which earlier versions accepted, are rejected
with a type error (code -3). The only exception is the `baseBlock` and
`block` arguments of `protx diff` and `protx listdiff`, which take a block
hash or a height and therefore still accept a height as a string. There is
no runtime option to restore the old behaviour; `-deprecatedrpc=permissive_bool`
only applies to booleans.
4 changes: 4 additions & 0 deletions doc/release-notes-7181.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ Updated RPCs
`protx listdiff`.
* This runtime gate also extends to the functionally identical key, `address` in `masternode list`
(and its alias, `masternodelist`)
* The same gate applies to `getcoinjoininfo`, `protx info`, `protx list detailed`
and `quorum info`.
* `dash-tx -json` output omits these keys as well and offers no way to restore
them, since `dash-tx` does not accept `-deprecatedrpc`.
8 changes: 8 additions & 0 deletions doc/release-notes-7261.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Wallet
------

* `fundrawtransaction`, `send`, `sendall` and `walletcreatefundedpsbt` accept a
`use_cj` option, previously available only on `sendtoaddress` and `sendmany`,
that restricts coin selection to fully mixed CoinJoin outputs. `send` and
`sendall` record the resulting transaction as a CoinJoin spend (`DS` in
`gettransaction`), as `sendtoaddress` and `sendmany` already do.
11 changes: 11 additions & 0 deletions doc/release-notes-7265.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
New settings
------------

* The dust attack protection introduced in v23.1.0 as a GUI option is now
available to `dashd` as well through `-dustprotectionthreshold=<n>`: UTXOs
received from external transactions whose value is at or below `<n>` duffs are
locked automatically by the wallet, so they are not spent together with other
coins. It is disabled by default (`0`). The GUI option and the command-line
option control the same setting. Locks persist across restarts and are not
released when the threshold is lowered or the option is removed; use
`lockunspent` to unlock such outputs manually.
44 changes: 44 additions & 0 deletions doc/release-notes-7294.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Consensus changes
-----------------

* Asset Lock special transactions gain a payload version 2, enabled by the `v24`
hard fork. Before activation only version 1 is valid; after activation both
version 1 and version 2 are. A version 2 payload signals that Platform assigns
each credit output to the matching Identity automatically, and it extends the
allowed `creditOutputs[].scriptPubKey` forms from P2PKH only to P2PKH and P2SH.
All other Asset Lock rules are unchanged. See DIP-27. (dash#7294)

Updated RPCs
------------

* `validateaddress` and `getaddressinfo` now accept DIP-18 Dash Platform
addresses (`dash1k…`/`dash1s…` on mainnet, `tdash1k…`/`tdash1s…` on the test
chains) and report them with a new `isplatform` field. `getaddressinfo`
describes such an address against the credit output script an Asset Lock would
carry for it, so `ismine` and `solvable` answer whether this wallet holds the
key or script behind the address. (dash#7294)

* `sendtoaddress` and `sendmany` accept Platform addresses as recipients. Doing
so turns the transaction into an Asset Lock with a version 2 payload whose
credit outputs are the Platform recipients, plus a single empty `OP_RETURN`
output carrying their total. `subtractfeefromamount` is not supported for
Platform recipients, because the locked amount has to match the `OP_RETURN`
value exactly. (dash#7294)

* `getrawtransaction`, `decoderawtransaction` and the other transaction-decoding
RPCs report `assetLockTx.creditOutputs[].address` for version 2 payloads, the
DIP-18 Platform address the credit output pays. Version 1 payloads carry no
such address and the field is omitted. (dash#7294)

Notes
-----

* This release carries the consensus side of version 2 Asset Locks: after the
`v24` hard fork they are valid in a block. They remain non-standard for now,
so under default policy they are not accepted into the mempool or relayed,
and sending to a Platform address fails with
`Transaction is non-standard (assetlocktx-version-2) …`. A future release
will make them standard once Dash Platform can process them; that is a
policy change only and needs no further consensus change. On testnet,
devnets and regtest the node can be started with `-acceptnonstdtxn=1` to
relay them already; mainnet does not accept that option. (dash#7294)
8 changes: 8 additions & 0 deletions doc/release-notes-7300.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
P2P and network changes
-----------------------

* Startup now fails if any configured `-bind`, `-whitebind` or the implicit Tor
onion-service bind (`127.0.0.1:9996` by default) cannot be set up. Dash Core
v23 and earlier started as long as at least one bind succeeded. Use
`-bind=<addr>:<port>=onion` to move the onion bind, or `-listenonion=0` to
drop it, if the default address is not available on your system.
6 changes: 6 additions & 0 deletions doc/release-notes-7335.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Updated RPCs
------------

* `gettxoutsetinfo` no longer returns `block_info.unspendables.bip30`. Dash
never had transactions overridden by duplicates, so the value was always
zero.
17 changes: 17 additions & 0 deletions doc/release-notes-7340.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Updated RPCs
------------

* The `payoutAddress` argument of `protx register`, `protx register_legacy`,
`protx register_evo`, `protx register_fund`, `protx register_fund_legacy`,
`protx register_fund_evo` and `protx update_registrar` accepts, in addition to
a single address, an array of `{"address": ..., "reward": ...}` objects that
splits the owner reward between several payees, with `reward` given in basis
points. Multi-party payouts are only valid once the v24 hard fork activates.

* For ExtAddr (version 3) masternode states and ProTx payloads the payout list
is reported as a `payouts` array in place of the single `payoutAddress`
field, which legacy versions keep. Shared masternodes report `shares`
instead. The fields appear in `protx info`, `protx list detailed`,
`protx listdiff`, `masternode status` (`dmnState`) and in decoded
`ProRegTx`/`ProUpRegTx` payloads of the transaction-decoding RPCs;
`protx diff` includes them only with `extended=true`.
7 changes: 7 additions & 0 deletions doc/release-notes-7428.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Updated RPCs
------------

* `quorum dkginfo` accepts an optional `proTxHash` argument and reports, in a
new `upcoming_dkgs` array, the DKG sessions that masternode will take part in
whose work block is already mined. Without the argument the local active
masternode is used, if any.
7 changes: 7 additions & 0 deletions doc/release-notes-7485.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
P2P and network changes
-----------------------

- Masternode list caches no longer grow between blocks when peers request
masternode list diffs or quorum rotation info (`getmnlistd`, `getqrinfo`) for
many historical blocks. Lists and diffs older than the recent cache window are
now dropped after each such request instead of after the next block. (#7485)
7 changes: 6 additions & 1 deletion doc/release-notes-7639.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ outputs as trusted, so Platform-to-Core transfers become rapidly respendable.
Version 2 unlocks are relayed by instance hash (new inventory type
`MSG_ASSET_UNLOCK`, protocol version 70242), kept in the mempool while expired
awaiting a re-signed replacement, and committed to by the coinbase transaction
(CbTx version 4, `merkleRootAssetUnlocks`).
(CbTx version 4, `merkleRootAssetUnlocks`). An expired instance is also
accepted into the mempool (from peers and when reloading `mempool.dat`) if its
signature was valid at a height within its window, so its spends propagate; it is
neither mined nor InstantSend-locked until Platform re-signs it. Relaying an
Asset Unlock outside the receiving node's height window or quorum range is no
longer penalized.

Updated RPCs
------------
Expand Down
8 changes: 8 additions & 0 deletions src/coinjoin/coinjoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,14 @@ bool CoinJoin::ValidateDemotionEntry(const std::vector<CTxIn>& vecTxIn, const st
return false;
}

// The input has to come from the larger adjacent denomination
if (GetLargerAdjacentDenom(nSessionDenom) == 0) {
LogPrint(BCLog::COINJOIN, "CoinJoin::ValidateDemotionEntry -- ERROR: no larger adjacent denom for %s\n",
DenominationToString(nSessionDenom));
nMessageIDRet = ERR_DENOM;
return false;
}

// Validate all outputs are at session denomination and P2PKH
for (const auto& txout : vecTxOut) {
const int nDenom = AmountToDenomination(txout.nValue);
Expand Down
7 changes: 5 additions & 2 deletions src/evo/assetlocktx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,17 @@ static bool VerifyAssetUnlockSig(const CAssetUnlockPayload& payload, ScanQuorums
const int quorums_to_scan = llmq_params_opt->signingActiveQuorumCount + 1;
const auto quorums = scan_quorums(llmqType, pindexTip, quorums_to_scan);

// The quorum recency and height window checks depend on the validating node's tip: the same
// transaction is valid a block earlier or later, so an honest peer at another height relays
// it. They fail as TX_BAD_SPECIAL so that relaying such a transaction is not punished.
if (bool isActive = std::any_of(quorums.begin(), quorums.end(), [&](const auto &q) { return q->qc->quorumHash == payload.getQuorumHash(); }); !isActive) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-too-old-quorum");
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlock-too-old-quorum");
}

if (static_cast<uint32_t>(pindexTip->nHeight) < payload.getRequestedHeight() || pindexTip->nHeight >= payload.getHeightToExpiry()) {
LogPrint(BCLog::CREDITPOOL, "Asset unlock tx %d with requested height %d could not be accepted on height: %d\n",
payload.getIndex(), payload.getRequestedHeight(), pindexTip->nHeight);
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-too-late");
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlock-too-late");
}

const auto quorum = get_quorum(llmqType, payload.getQuorumHash());
Expand Down
12 changes: 12 additions & 0 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,17 @@ void CDeterministicMNManager::CleanupCache(int nHeight)

}

void CDeterministicMNManager::CleanupHistoricalCache()
{
LOCK(cs);
if (tipIndex == nullptr) return;
const int tip_height{tipIndex->nHeight};
std::erase_if(mnListsCache,
[tip_height](const auto& p) { return p.second.GetHeight() + LIST_DIFFS_CACHE_SIZE < tip_height; });
std::erase_if(mnListDiffsCache,
[tip_height](const auto& p) { return p.second.nHeight + LIST_DIFFS_CACHE_SIZE < tip_height; });
}

//end

void CDeterministicMNManager::DoMaintenance() {
Expand Down Expand Up @@ -1308,6 +1319,7 @@ bool CDeterministicMNManager::IsRepaired() const { return m_evoDb.Exists(DB_LIST

void CDeterministicMNManager::CompleteRepair()
{
AssertLockHeld(::cs_main);
auto dbTx = m_evoDb.BeginTransaction();
m_evoDb.Write(DB_LIST_REPAIRED, 1);
dbTx->Commit();
Expand Down
12 changes: 11 additions & 1 deletion src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ class BlockDataUnavailableError : public std::runtime_error

class CDeterministicMNManager
{
public:
static constexpr int DISK_SNAPSHOT_PERIOD = 576; // once per day
// keep cache for enough disk snapshots to have all active quourms covered
static constexpr int DISK_SNAPSHOTS = llmq_max_blocks() / DISK_SNAPSHOT_PERIOD + 1;
Expand Down Expand Up @@ -810,6 +811,15 @@ class CDeterministicMNManager
static bool IsProTxWithCollateral(const CTransactionRef& tx, uint32_t n);

void DoMaintenance() EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cleanup);
// Drops cached lists and diffs older than the window CleanupCache() keeps. Called after
// serving a peer request, which may have rebuilt lists for arbitrarily old blocks.
void CleanupHistoricalCache() EXCLUSIVE_LOCKS_REQUIRED(!cs);

std::pair<size_t, size_t> GetCacheSizesForTesting() EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
return {mnListsCache.size(), mnListDiffsCache.size()};
}

// Recalculate and optionally repair diffs between snapshots
struct RecalcDiffsResult {
Expand All @@ -836,7 +846,7 @@ class CDeterministicMNManager
BuildListFromBlockFunc build_list_func, bool repair)
EXCLUSIVE_LOCKS_REQUIRED(!cs);
[[nodiscard]] bool IsRepaired() const;
void CompleteRepair();
void CompleteRepair() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

// Migration support for nVersion-first CDeterministicMNStateDiff format
[[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
Expand Down
32 changes: 12 additions & 20 deletions src/evo/specialtxman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,29 +89,15 @@ static bool SetStateVersion(CDeterministicMNState& state_mn, uint16_t nVersion,
return false;
}
} else if (nType == MnType::Evo && !state_mn.netInfo->IsEmpty()) {
const CNetAddr addr{state_mn.netInfo->GetPrimary()};
if ((state_mn.platformP2PPort != 0 &&
converted_netinfo->AddEntry(NetInfoPurpose::PLATFORM_P2P,
CService(addr, state_mn.platformP2PPort).ToStringAddrPort()) != NetInfoStatus::Success) ||
(state_mn.platformHTTPPort != 0 &&
converted_netinfo->AddEntry(NetInfoPurpose::PLATFORM_HTTPS,
CService(addr, state_mn.platformHTTPPort).ToStringAddrPort()) != NetInfoStatus::Success)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-version");
}
// The scalar Platform ports are not carried over: an EvoNode is raised to ExtAddr by a
// ProUpServTx that spells out its Platform entries
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-version");
}
state_mn.platformP2PPort = 0;
state_mn.platformHTTPPort = 0;
} else {
if (!AddNetInfoEntries(converted_netinfo, NetInfoPurpose::CORE_P2P,
state_mn.netInfo->GetEntries(NetInfoPurpose::CORE_P2P), state)) {
return false;
}
if (nType == MnType::Evo && state_mn.netInfo->CanStorePlatform() && !state_mn.netInfo->IsEmpty()) {
const auto p2p_entries{state_mn.netInfo->GetEntries(NetInfoPurpose::PLATFORM_P2P)};
const auto http_entries{state_mn.netInfo->GetEntries(NetInfoPurpose::PLATFORM_HTTPS)};
state_mn.platformP2PPort = p2p_entries.empty() ? 0 : p2p_entries.front().GetPort();
state_mn.platformHTTPPort = http_entries.empty() ? 0 : http_entries.front().GetPort();
}
} else if (!AddNetInfoEntries(converted_netinfo, NetInfoPurpose::CORE_P2P,
state_mn.netInfo->GetEntries(NetInfoPurpose::CORE_P2P), state)) {
return false;
}

state_mn.nVersion = nVersion;
Expand Down Expand Up @@ -1361,6 +1347,12 @@ bool CheckProUpServTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*>
return false;
}

// From ExtAddr on the Platform ports live in netInfo and are never derived from the scalar fields,
// so an EvoNode service update has to spell them out
if (is_v24_active && opt_ptx->nType == MnType::Evo && opt_ptx->nVersion < ProTxVersion::ExtAddr) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-disallowed");
}

if (is_v24_active) {
CDeterministicMNState new_state{*dmn->pdmnState};
BlockValidationState migration_state;
Expand Down
4 changes: 2 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2460,7 +2460,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)

if (skip_evodb_repair_on_reindex) {
LogPrintf("Skipping evodb repair during reindex\n");
node.dmnman->CompleteRepair(); // Mark as repaired since we're rebuilding fresh
WITH_LOCK(::cs_main, node.dmnman->CompleteRepair()); // Mark as repaired since we're rebuilding fresh
} else if (node.dmnman->IsRepaired() && !args.GetBoolArg("-forceevodbrepair", false)) {
LogPrintf("Masternode list diffs are already repaired\n");
} else {
Expand Down Expand Up @@ -2500,7 +2500,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
StartShutdown();
return;
}
node.dmnman->CompleteRepair();
WITH_LOCK(::cs_main, node.dmnman->CompleteRepair());
LogPrintf("Successfully repaired %d masternode list diffs, verified %d snapshots in %ds\n",
result.diffs_recalculated, result.snapshots_verified,
Ticks<std::chrono::seconds>(SteadyClock::now() - start));
Expand Down
14 changes: 10 additions & 4 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5614,9 +5614,11 @@ void PeerManagerImpl::ProcessMessage(

CSimplifiedMNListDiff mnListDiff;
std::string strError;
if (BuildSimplifiedMNListDiff(m_dmnman, m_chainman, *m_llmq_ctx.quorum_block_processor, *m_llmq_ctx.qman,
cmd.baseBlockHash, cmd.blockHash, mnListDiff, strError))
{
const auto& qblockman{*m_llmq_ctx.quorum_block_processor};
const bool built{BuildSimplifiedMNListDiff(m_dmnman, m_chainman, qblockman, *m_llmq_ctx.qman, cmd.baseBlockHash,
cmd.blockHash, mnListDiff, strError)};
m_dmnman.CleanupHistoricalCache();
if (built) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MNLISTDIFF, mnListDiff));
} else {
strError = strprintf("getmnlistdiff failed for baseBlockHash=%s, blockHash=%s. error=%s", cmd.baseBlockHash.ToString(), cmd.blockHash.ToString(), strError);
Expand Down Expand Up @@ -5671,7 +5673,11 @@ void PeerManagerImpl::ProcessMessage(
llmq::CQuorumRotationInfo quorumRotationInfoRet;
std::string strError;
bool use_legacy_construction = pfrom.GetCommonVersion() < EFFICIENT_QRINFO_VERSION;;
if (BuildQuorumRotationInfo(m_dmnman, *m_llmq_ctx.qsnapman, m_chainman, *m_llmq_ctx.qman, *m_llmq_ctx.quorum_block_processor, cmd, use_legacy_construction, quorumRotationInfoRet, strError)) {
const bool built{BuildQuorumRotationInfo(m_dmnman, *m_llmq_ctx.qsnapman, m_chainman, *m_llmq_ctx.qman,
*m_llmq_ctx.quorum_block_processor, cmd, use_legacy_construction,
quorumRotationInfoRet, strError)};
m_dmnman.CleanupHistoricalCache();
if (built) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QUORUMROTATIONINFO, quorumRotationInfoRet));
} else {
strError = strprintf("getquorumrotationinfo failed for size(baseBlockHashes)=%d, blockRequestHash=%s. error=%s", cmd.baseBlockHashes.size(), cmd.blockRequestHash.ToString(), strError);
Expand Down
Loading
Loading