forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
feat!: add an implementation of DIP 0027 Credit Asset Locks #5026
Merged
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
967443a
feat: add implementation of DIP 0027 Credit Asset Locks
knst ae631b5
feat: add unittests for SkipSet structure
knst a16fe16
cleanup mninfo
knst c763d97
fixups for coin
knst 2db5c05
refactor: clean up duplicated code - using get_recovered_sigs from fr…
knst 3ffdcdd
fixup after assetlocks
knst d07abef
using check_mempool_size() instead direct call of RPC
knst 4fa8f1c
fixup codestyle
knst fc8e7d2
fixup feature_nulldummy
knst a1a0ce1
feat: instead comments in functional test use logs for more clear pro…
knst 51d095a
improved performance of functional test feature_asset_locks significa…
knst 157a282
resolve conflicts after refactoring util/validation.h follow-up bitco…
knst e5469cf
resolve conflicts
knst 6836e9d
fix: removed nType from AssetLock tx accordingly DIP
knst cd22d3a
fix: nVersion in Asset Lock/Unlock transaction's payload is 8bits not 16
knst 3416553
fixup missing explicit
knst 1b2944d
fixes accordingly to codereview
knst 9a7645c
codestyle: { at new line
knst 46c6c6d
fix for SkipList unit tests accordingly code review
knst 1e02bcf
more fixes accodringly to review
knst 4ebfcc4
fix inflation bug for credit outputs in assetlock tx
knst 4ff8e75
improved hash calculation
knst 4f60fb2
hidden implementation of asset locks from tx_verify
knst 4e6e963
cleanup `quorum_count`
knst f2c2df2
fix: revert hash calculation of AssetUnlock transaction
UdjinM6 55dff94
refactor: add more const for local variables that is not supposed to …
knst 3908739
fix: apply suggestions from code review
knst 128ef19
refactor: move trivial getters of CAsset{Lock,Unlock}Payload to header
knst 23fc477
refactor: moved CSkipSet from CreditPool.h to separate file
knst 95b8d19
fix: code-style adjusting for methods naming in credit pool and relat…
knst 5d29b06
fix: fixing typos and improving comments. Fixing error message for v2…
knst 2ea4251
fix: replaced an assert to an exception in CSkipSet inside Add
knst 54ae92d
fix: add missing #define guards for skip_set
knst 0e793c6
fix: removed duplicated `return false` from src/evo/specialtxman.cpp
knst 5534f64
feat: check credit pool amount against all nodes in functional test
knst dcd9262
feat: add an extra node with wallet for asset locks function test
knst 2c6aa49
feat: new tests with asset-unlock tx absurdly high fee and zero fee
knst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
// Copyright (c) 2023 The Dash Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <evo/assetlocktx.h> | ||
#include <evo/specialtx.h> | ||
#include <evo/creditpool.h> | ||
|
||
#include <consensus/params.h> | ||
|
||
#include <chainparams.h> | ||
#include <logging.h> | ||
#include <validation.h> | ||
|
||
#include <llmq/commitment.h> | ||
#include <llmq/signing.h> | ||
#include <llmq/utils.h> | ||
#include <llmq/quorums.h> | ||
|
||
#include <algorithm> | ||
|
||
/** | ||
* Common code for Asset Lock and Asset Unlock | ||
*/ | ||
bool CheckAssetLockUnlockTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCreditPool& creditPool, TxValidationState& state) | ||
{ | ||
switch (tx.nType) { | ||
case TRANSACTION_ASSET_LOCK: | ||
return CheckAssetLockTx(tx, state); | ||
case TRANSACTION_ASSET_UNLOCK: | ||
return CheckAssetUnlockTx(tx, pindexPrev, creditPool, state); | ||
default: | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-not-asset-locks-at-all"); | ||
} | ||
} | ||
|
||
/** | ||
* Asset Lock Transaction | ||
*/ | ||
bool CheckAssetLockTx(const CTransaction& tx, TxValidationState& state) | ||
{ | ||
if (tx.nType != TRANSACTION_ASSET_LOCK) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-type"); | ||
} | ||
|
||
CAmount returnAmount{0}; | ||
for (const CTxOut& txout : tx.vout) { | ||
const CScript& script = txout.scriptPubKey; | ||
if (script.empty() || script[0] != OP_RETURN) continue; | ||
|
||
if (script.size() != 2 || script[1] != 0) return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-non-empty-return"); | ||
|
||
if (txout.nValue == 0 || !MoneyRange(txout.nValue)) return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-opreturn-outofrange"); | ||
|
||
// Should be only one OP_RETURN | ||
if (returnAmount > 0) return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-multiple-return"); | ||
returnAmount = txout.nValue; | ||
} | ||
|
||
if (returnAmount == 0) return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-no-return"); | ||
|
||
CAssetLockPayload assetLockTx; | ||
if (!GetTxPayload(tx, assetLockTx)) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-payload"); | ||
} | ||
|
||
if (assetLockTx.getVersion() == 0 || assetLockTx.getVersion() > CAssetLockPayload::CURRENT_VERSION) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-version"); | ||
} | ||
|
||
if (assetLockTx.getCreditOutputs().empty()) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-emptycreditoutputs"); | ||
} | ||
|
||
CAmount creditOutputsAmount = 0; | ||
for (const CTxOut& out : assetLockTx.getCreditOutputs()) { | ||
if (out.nValue == 0 || !MoneyRange(out.nValue) || !MoneyRange(creditOutputsAmount + out.nValue)) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-credit-outofrange"); | ||
} | ||
|
||
creditOutputsAmount += out.nValue; | ||
UdjinM6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!out.scriptPubKey.IsPayToPublicKeyHash()) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-pubKeyHash"); | ||
} | ||
} | ||
if (creditOutputsAmount != returnAmount) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetlocktx-creditamount"); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
std::string CAssetLockPayload::ToString() const | ||
{ | ||
std::string outputs{"["}; | ||
for (const CTxOut& tx: creditOutputs) { | ||
outputs.append(tx.ToString()); | ||
outputs.append(","); | ||
} | ||
outputs.back() = ']'; | ||
return strprintf("CAssetLockPayload(nVersion=%d,creditOutputs=%s)", nVersion, outputs.c_str()); | ||
} | ||
|
||
/** | ||
* Asset Unlock Transaction (withdrawals) | ||
*/ | ||
|
||
const std::string ASSETUNLOCK_REQUESTID_PREFIX = "plwdtx"; | ||
|
||
bool CAssetUnlockPayload::VerifySig(const uint256& msgHash, const CBlockIndex* pindexTip, TxValidationState& state) const | ||
{ | ||
// That quourm hash must be active at `requestHeight`, | ||
// and at the quorumHash must be active in either the current or previous quorum cycle | ||
// and the sig must validate against that specific quorumHash. | ||
|
||
Consensus::LLMQType llmqType = Params().GetConsensus().llmqTypeAssetLocks; | ||
|
||
// We check at most 2 quorums | ||
const auto quorums = llmq::quorumManager->ScanQuorums(llmqType, pindexTip, 2); | ||
bool isActive = std::any_of(quorums.begin(), quorums.end(), [&](const auto &q) { return q->qc->quorumHash == quorumHash; }); | ||
|
||
if (!isActive) { | ||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-not-active-quorum"); | ||
} | ||
|
||
if (pindexTip->nHeight < requestedHeight || pindexTip->nHeight >= getHeightToExpiry()) { | ||
LogPrintf("Asset unlock tx %d with requested height %d could not be accepted on height: %d\n", | ||
index, requestedHeight, pindexTip->nHeight); | ||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-too-late"); | ||
} | ||
|
||
const auto quorum = llmq::quorumManager->GetQuorum(llmqType, quorumHash); | ||
assert(quorum); | ||
|
||
const uint256 requestId = ::SerializeHash(std::make_pair(ASSETUNLOCK_REQUESTID_PREFIX, index)); | ||
|
||
const uint256 signHash = llmq::utils::BuildSignHash(llmqType, quorum->qc->quorumHash, requestId, msgHash); | ||
if (quorumSig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash)) { | ||
return true; | ||
} | ||
|
||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-not-verified"); | ||
} | ||
|
||
bool CheckAssetUnlockTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCreditPool& creditPool, TxValidationState& state) | ||
{ | ||
if (tx.nType != TRANSACTION_ASSET_UNLOCK) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlocktx-type"); | ||
} | ||
|
||
if (!tx.vin.empty()) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlocktx-have-input"); | ||
} | ||
|
||
if (tx.vout.size() > CAssetUnlockPayload::MAXIMUM_WITHDRAWALS) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlocktx-too-many-outs"); | ||
} | ||
|
||
CAssetUnlockPayload assetUnlockTx; | ||
if (!GetTxPayload(tx, assetUnlockTx)) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlocktx-payload"); | ||
} | ||
|
||
if (assetUnlockTx.getVersion() == 0 || assetUnlockTx.getVersion() > CAssetUnlockPayload::CURRENT_VERSION) { | ||
knst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlocktx-version"); | ||
} | ||
|
||
if (creditPool.indexes.Contains(assetUnlockTx.getIndex())) { | ||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-duplicated-index"); | ||
} | ||
|
||
const CBlockIndex* pindexQuorum = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(assetUnlockTx.getQuorumHash())); | ||
if (!pindexQuorum) { | ||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-quorum-hash"); | ||
} | ||
|
||
// Copy transaction except `quorumSig` field to calculate hash | ||
CMutableTransaction tx_copy(tx); | ||
const CAssetUnlockPayload payload_copy{assetUnlockTx.getVersion(), assetUnlockTx.getIndex(), assetUnlockTx.getFee(), assetUnlockTx.getRequestedHeight(), assetUnlockTx.getQuorumHash(), CBLSSignature{}}; | ||
SetTxPayload(tx_copy, payload_copy); | ||
|
||
uint256 msgHash = tx_copy.GetHash(); | ||
|
||
return assetUnlockTx.VerifySig(msgHash, pindexPrev, state); | ||
} | ||
|
||
bool GetAssetUnlockFee(const CTransaction& tx, CAmount& txfee, TxValidationState& state) | ||
{ | ||
CAssetUnlockPayload assetUnlockTx; | ||
if (!GetTxPayload(tx, assetUnlockTx)) { | ||
return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-assetunlocktx-payload"); | ||
} | ||
const CAmount txfee_aux = assetUnlockTx.getFee(); | ||
if (txfee_aux == 0 || !MoneyRange(txfee_aux)) { | ||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-assetunlock-fee-outofrange"); | ||
} | ||
txfee = txfee_aux; | ||
return true; | ||
} | ||
|
||
std::string CAssetUnlockPayload::ToString() const | ||
{ | ||
return strprintf("CAssetUnlockPayload(nVersion=%d,index=%d,fee=%d.%08d,requestedHeight=%d,quorumHash=%d,quorumSig=%s", | ||
nVersion, index, fee / COIN, fee % COIN, requestedHeight, quorumHash.GetHex(), quorumSig.ToString().c_str()); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it's not needed for us to handle bloom for either of these, but @UdjinM6 please provide your thoughts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOCK: could probably use it for
p2pkh
insideop_return
but that's a question for platform/mobile guys if they need to track LOCK txes this way.UNLOCK: I can't think of any use case here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, we need to figure out if we want to remove this TODO or implement something here