Skip to content

Commit

Permalink
feat!: add an implementation of DIP 0027 Credit Asset Locks
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 903f132
Author: pasta <pasta@dashboost.org>
Date:   Tue Jan 3 14:55:50 2023 -0600

    fix: resolve gitian compilation error

commit befd1fb
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Mon Dec 26 16:58:02 2022 +0700

    refactor: make a dependency of checking special transactions on credit pool visible and clear

commit 390e0d0
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Mon Dec 26 15:53:54 2022 +0700

    fix!: add a missing validation of Asset Unlock limits for externally mined blocks

    Also added a functional test to be sure that this bug won't be made again (regression)

commit 3d79d6c
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Fri Dec 23 15:43:18 2022 +0700

    refactoring: CCreditPoolManager doesn't call calculations recursively for big amount of blocks

commit 29a0ee5
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 23:49:15 2022 +0700

    adds a fixed limit for height of Asset Unlock transaction: 48 blocks

commit 786c577
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 16:16:50 2022 +0700

    Code style fixes for Credit Pool

     - added a comment about logic of sliding window
     - replaced in-out params to a struct
     - let miner to copy CreditPool rather than create a copy when possible

commit 01eb537
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 15:54:40 2022 +0700

    Fixed initialization of data in CAssetUnlockPayload + return const reference in getters

commit f47085a
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 15:47:40 2022 +0700

    Improved implementation of SkipSet accordingly review:

     - removed `state` dependency
     - renamed SkipSet -> CSkipSet
     - renamed `right` to `current_max`

commit b856cec
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 15:33:17 2022 +0700

    More fixes accodingly code review

     - renamed CreditPoolCb -> CCreditPool
     - renamed CreditPoolCbDiff -> CCreditPoolDiff
     - ordered private/public sections in classes
     - replaced std::set -> std::unordered_set
     - using util function IsV19 instead duplicated code

commit 24836ab
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 14:54:31 2022 +0700

    Fixed data type for asset-unlock transation: the fee is unsigned number 32 bit as specified in DIP

commit c67160f
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Dec 22 14:47:36 2022 +0700

    Code style fixes based on code review + unit test improvement

commit 9cfd130
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Dec 20 17:15:04 2022 +0700

    fixes accordingly review

     - call constructor CreditPoolCb with proper initialization list
     - move data to private section
     - code alignment

commit d082091
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Wed Dec 14 13:15:02 2022 +0700

    generate blocks by batch of 10, not 20

commit 2c49333
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Dec 13 18:12:30 2022 +0700

    Stabilize functional test 'feature_asset_locks.py' for CI

     - blocks are generated by smaller batches (not by 500 once)
     - reduced total amount of generated blocks when it is not necessary
     - added some extra logs and comments

commit a1b8c7f
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Mon Dec 12 21:35:24 2022 +0700

    refactoring: moved common "send tx" code in a new function for functional test feature_asset_locks.py

commit f718236
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Mon Dec 12 19:30:33 2022 +0700

    Improved functional test 'feature_asset_locks'.

     - now this test is more reliable when choosing unspent coins to create asset-lock tx
     - now this test is more strict about size of mempool for capacity of SkipSet test

commit e64b9ea
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Mon Dec 12 17:15:25 2022 +0700

    Fixes for index processing for assset-unlocks.

     - Changed logic of processing indexes to make it independent from order:
       tx in current block do not change status of SkipSet and related code won't fail occasionally
       depends on order of transactions
     - Passing CreditPoolCb externally for Check Asset Unlock (before it was retrieved for each transaction from cache)
     - Fix type int64_t -> uint64_t in many locations

commit 1e20644
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Mon Dec 12 01:43:11 2022 +0700

    feat: improvement and refactorings for asset-locks unit tests

commit c411570
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 29 16:24:39 2022 +0700

    Added an unit test for SkipSet

commit 4c18229
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 29 16:56:56 2022 +0700

    Added limit for SkipSet and updated functional test feature_asset_locks.py

commit 98090e9
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Fri Dec 9 03:29:33 2022 +0700

    Refactoring of functional test feature_asset_locks: better function to check mempool usage

commit ca9154e
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 22 23:48:28 2022 +0700

    Adds functional test with duplicated indexes in Asset Unlock txes

commit 46b6ad1
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 22 21:06:20 2022 +0700

    fix: indexes also validated before beeing added to txmempool

commit 286cd74
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Nov 3 20:23:20 2022 +0700

    feat: implemented an index validation for asset-unlock + refactoring of CCreditPoolManager + saving CreditPoolCb to evo db

commit 1dedab6
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Wed Nov 2 00:39:47 2022 +0700

    feat: improved functional tests for asset locks feature

     - reconsider/invalide blocks
     - manually created block with invalid transaction

commit dbf6c51
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 1 18:22:28 2022 +0700

    feat: improved functional tests for feature asset locks

commit 87c7d0d
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 1 18:05:36 2022 +0700

    fix!: for validation asset unlock transaction is used now the exactly quorum that specified in a message and this quorum should be active

    It is breaking changes with previous implementation, because it changes consensus rules

commit 04375aa
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 1 17:31:06 2022 +0700

    feat: removing expired asset-unlock transaction from mempool

    It will reduce memory usage, improve performance.
    Also, that's just good that transaction that won't be ever mined (because expired) are kept on all nodes

commit 5bab728
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Nov 1 18:19:02 2022 +0700

    feat!: update rules for asset unlock limits: min(max(100, min(.10 * assetlockpool, 1000)), assetlockpool)

    Before it was: min(max(1000, 0.10 * assetlockpool), assetlockpool)

    It's breaking changes, because changes rules of consensus

commit 82491d9
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Oct 20 03:30:16 2022 +0700

    feat: add new functional test to check Asset Lock limits

commit e9df864
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Oct 20 03:29:14 2022 +0700

    feat!: implement limits for asset unlock (withdrawal) txes

    This changes are breaking because it changes consensus rules

commit 5353ff9
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Tue Oct 4 01:41:53 2022 +0700

    feat: improve improved funtional test feature_asset_locks.py to test more

     - rollback of block with tx Asset Lock
     - rollback of block with tx Asset Unlock
     - duplicate 'asset unlock' tx for new block

    asset unlock rollback and reconsider

commit 7160305
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Fri Sep 30 01:29:34 2022 +0700

    fix: adds missing creditpool data to coinbase tx python's binding (message.py)

    It also fixes functional tests 'feature_nulldummy.py', 'feature_llmq_is_cl_conflicts.py'

commit 8ce03a7
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Sep 29 19:25:05 2022 +0700

    Adds implementation of DIP 0027 Credit Asset Locks

    This commit includes:
      - Asset Lock transaction
      - Asset Unlock transaction (withdrawal)
      - Credit Pool in coinbase
      - Unit tests for Asset Lock/Unlock tx
      - New functional test `feature_asset_locks.py`
      - RPC for Credit Pool (currently locked amount)

    Co-authored-by: pasta <pasta@dashboost.org>

commit 13dba82
Author: Konstantin Akimov <knstqq@gmail.com>
Date:   Thu Sep 29 19:25:05 2022 +0700

    Refactors special tx manager: tidy up header dependencies and move general code
  • Loading branch information
PastaPastaPasta committed Jan 3, 2023
1 parent 1c0ab95 commit 8b64c0f
Show file tree
Hide file tree
Showing 37 changed files with 2,050 additions and 38 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@ BITCOIN_CORE_H = \
cuckoocache.h \
ctpl_stl.h \
cxxtimer.hpp \
evo/assetlocktx.h \
evo/cbtx.h \
evo/creditpool.h \
evo/deterministicmns.h \
evo/dmnstate.h \
evo/evodb.h \
Expand Down Expand Up @@ -376,7 +378,9 @@ libbitcoin_server_a_SOURCES = \
consensus/tx_verify.cpp \
dbwrapper.cpp \
dsnotificationinterface.cpp \
evo/assetlocktx.cpp \
evo/cbtx.cpp \
evo/creditpool.cpp \
evo/deterministicmns.cpp \
evo/dmnstate.cpp \
evo/evodb.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ BITCOIN_TESTS =\
test/dip0020opcodes_tests.cpp \
test/descriptor_tests.cpp \
test/dynamic_activation_thresholds_tests.cpp \
test/evo_assetlocks_tests.cpp \
test/evo_deterministicmns_tests.cpp \
test/evo_instantsend_tests.cpp \
test/evo_simplifiedmns_tests.cpp \
Expand Down
4 changes: 4 additions & 0 deletions src/bloom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ bool CBloomFilter::CheckSpecialTransactionMatchesAndUpdate(const CTransaction &t
case(TRANSACTION_MNHF_SIGNAL):
// No additional checks for this transaction types
return false;
case(TRANSACTION_ASSET_LOCK):
case(TRANSACTION_ASSET_UNLOCK):
// TODO asset lock/unlock bloom?
return false;
}

LogPrintf("Unknown special transaction type in Bloom filter check.\n");
Expand Down
4 changes: 4 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ class CMainParams : public CChainParams {
consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_60_75;
consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67;
consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_400_85;
consensus.llmqTypeAssetLocks = Consensus::LLMQType::LLMQ_400_85;

fDefaultConsistencyChecks = false;
fRequireStandard = true;
Expand Down Expand Up @@ -546,6 +547,7 @@ class CTestNetParams : public CChainParams {
consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_60_75;
consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67;
consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_50_60;
consensus.llmqTypeAssetLocks = Consensus::LLMQType::LLMQ_50_60;

fDefaultConsistencyChecks = false;
fRequireStandard = false;
Expand Down Expand Up @@ -757,6 +759,7 @@ class CDevNetParams : public CChainParams {
consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_60_75;
consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67;
consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_50_60;
consensus.llmqTypeAssetLocks = Consensus::LLMQType::LLMQ_50_60;

UpdateDevnetLLMQChainLocksFromArgs(args);
UpdateDevnetLLMQInstantSendFromArgs(args);
Expand Down Expand Up @@ -1035,6 +1038,7 @@ class CRegTestParams : public CChainParams {
consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_TEST_DIP0024;
consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_TEST;
consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_TEST;
consensus.llmqTypeAssetLocks = Consensus::LLMQType::LLMQ_TEST;

UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST);
UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST_INSTANTSEND);
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ struct Params {
LLMQType llmqTypeDIP0024InstantSend{LLMQType::LLMQ_NONE};
LLMQType llmqTypePlatform{LLMQType::LLMQ_NONE};
LLMQType llmqTypeMnhf{LLMQType::LLMQ_NONE};
LLMQType llmqTypeAssetLocks{LLMQType::LLMQ_NONE};
};
} // namespace Consensus

Expand Down
13 changes: 9 additions & 4 deletions src/consensus/tx_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@

bool CheckTransaction(const CTransaction& tx, CValidationState& state)
{
bool allowEmptyTxInOut = false;
bool allowEmptyTxIn = false;
bool allowEmptyTxOut = false;
if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
allowEmptyTxInOut = true;
allowEmptyTxIn = true;
allowEmptyTxOut = true;
}
if (tx.nType == TRANSACTION_ASSET_UNLOCK) {
allowEmptyTxIn = true;
}

// Basic checks that don't depend on any context
if (!allowEmptyTxInOut && tx.vin.empty())
if (!allowEmptyTxIn && tx.vin.empty())
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vin-empty");
if (!allowEmptyTxInOut && tx.vout.empty())
if (!allowEmptyTxOut && tx.vout.empty())
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-empty");
// Size limits
if (::GetSerializeSize(tx, PROTOCOL_VERSION) > MAX_LEGACY_BLOCK_SIZE)
Expand Down
15 changes: 15 additions & 0 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <consensus/validation.h>
#include <evo/assetlocktx.h>

// TODO remove the following dependencies
#include <chain.h>
Expand Down Expand Up @@ -160,6 +161,20 @@ unsigned int GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCa

bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{

if (bool isAssetUnlockTx = (tx.nVersion == 3 && tx.nType == TRANSACTION_ASSET_UNLOCK); isAssetUnlockTx) {
CAssetUnlockPayload assetUnlockTx;
if (!GetTxPayload(tx, assetUnlockTx)) {
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-assetunlocktx-payload");
}
CAmount txfee_aux = assetUnlockTx.getFee();
if (!MoneyRange(txfee_aux)) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-assetunlock-fee-outofrange");
}
txfee = txfee_aux;
return true;
}

// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", strprintf("%s: inputs missing/spent", __func__));
Expand Down
15 changes: 15 additions & 0 deletions src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <spentindex.h>

#include <evo/assetlocktx.h>
#include <evo/cbtx.h>
#include <evo/mnhftx.h>
#include <evo/providertx.h>
Expand Down Expand Up @@ -310,6 +311,20 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
mnhfTx.ToJson(obj);
entry.pushKV("mnhfTx", obj);
}
} else if (tx.nType == TRANSACTION_ASSET_LOCK) {
CAssetLockPayload assetLockTx;
if (!GetTxPayload(tx, assetLockTx)) {
UniValue obj;
assetLockTx.ToJson(obj);
entry.pushKV("assetLockTx", obj);
}
} else if (tx.nType == TRANSACTION_ASSET_UNLOCK) {
CAssetUnlockPayload assetUnlockTx;
if (!GetTxPayload(tx, assetUnlockTx)) {
UniValue obj;
assetUnlockTx.ToJson(obj);
entry.pushKV("assetUnlockTx", obj);
}
}

if (!hashBlock.IsNull())
Expand Down
237 changes: 237 additions & 0 deletions src/evo/assetlocktx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Copyright (c) 2022 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 <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
*/
maybe_error CheckAssetLockUnlockTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCreditPool& creditPool)
{
switch (tx.nType) {
case TRANSACTION_ASSET_LOCK:
return CheckAssetLockTx(tx);
case TRANSACTION_ASSET_UNLOCK:
return CheckAssetUnlockTx(tx, pindexPrev, creditPool);
default:
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-not-asset-locks-at-all"};
}
}

/*
Asset Lock Transaction
*/
maybe_error CheckAssetLockTx(const CTransaction& tx)
{
if (tx.nType != TRANSACTION_ASSET_LOCK) {
return {ValidationInvalidReason::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 {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-non-empty-return"};

if (txout.nValue <= 0) return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-zeroout-return"};

// Should be only one OP_RETURN
if (returnAmount) return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-multiple-return"};
returnAmount = txout.nValue;
}

if (returnAmount == 0) return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-no-return"};

CAssetLockPayload assetLockTx;
if (!GetTxPayload(tx, assetLockTx)) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-payload"};
}

if (assetLockTx.getVersion() == 0 || assetLockTx.getVersion() > CAssetLockPayload::CURRENT_VERSION) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-version"};
}

if (assetLockTx.getType() != 0) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-locktype"};
}

if (assetLockTx.getCreditOutputs().empty()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-emptycreditoutputs"};
}

CAmount creditOutputsAmount = 0;
for (const CTxOut& out : assetLockTx.getCreditOutputs()) {
creditOutputsAmount += out.nValue;
if (!out.scriptPubKey.IsPayToPublicKeyHash()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-pubKeyHash"};
}
}
if (creditOutputsAmount != returnAmount) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetlocktx-creditamount"};
}

return {};
}
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,nType=%d,creditOutputs=%s)", nVersion, nType, outputs.c_str());
}

uint16_t CAssetLockPayload::getVersion() const {
return nVersion;
}

uint16_t CAssetLockPayload::getType() const {
return nType;
}

const std::vector<CTxOut>& CAssetLockPayload::getCreditOutputs() const {
return creditOutputs;
}

/*
Asset Unlock Transaction (withdrawals)
*/
maybe_error CAssetUnlockPayload::VerifySig(const uint256& msgHash, const CBlockIndex* pindexTip) 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;

if (!Params().HasLLMQ(llmqType)) {
return {ValidationInvalidReason::CONSENSUS, "bad-assetunlock-llmq-type"};
}

const auto& llmq_params = llmq::GetLLMQParams(llmqType);

// We check at most 2 quorums, so, count is equal to 2
const int count = 2;
auto quorums = llmq::quorumManager->ScanQuorums(llmqType, pindexTip, count > -1 ? count : llmq_params.signingActiveQuorumCount);
bool isActive = std::any_of(quorums.begin(), quorums.end(), [&](const auto &q) { return q->qc->quorumHash == quorumHash; });

if (!isActive) {
return {ValidationInvalidReason::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 {ValidationInvalidReason::CONSENSUS, "bad-assetunlock-too-late"};
}

const auto quorum = llmq::quorumManager->GetQuorum(llmqType, quorumHash);
assert(quorum);

const std::string id(strprintf("plwdtx%lld", index));

std::vector<uint8_t> vchHash(32);
CSHA256().Write(reinterpret_cast<const uint8_t*>(id.data()), id.size()).Finalize(vchHash.data());
uint256 requestId(vchHash);

uint256 signHash = llmq::utils::BuildSignHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
if (quorumSig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash)) {
return {};
}

return {ValidationInvalidReason::CONSENSUS, "bad-assetunlock-not-verified"};
}

maybe_error CheckAssetUnlockTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCreditPool& creditPool)
{
if (tx.nType != TRANSACTION_ASSET_UNLOCK) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetunlocktx-type"};
}

if (!tx.vin.empty()) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetunlocktx-have-input"};
}

if (tx.vout.size() > CAssetUnlockPayload::MAXIMUM_WITHDRAWALS) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetunlocktx-too-many-outs"};
}

CAssetUnlockPayload assetUnlockTx;
if (!GetTxPayload(tx, assetUnlockTx)) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetunlocktx-payload"};
}

if (assetUnlockTx.getVersion() == 0 || assetUnlockTx.getVersion() > CAssetUnlockPayload::CURRENT_VERSION) {
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-assetunlocktx-version"};
}

if (creditPool.indexes.contains(assetUnlockTx.getIndex())) {
return {ValidationInvalidReason::CONSENSUS, "bad-assetunlock-duplicated-index"};
}

const CBlockIndex* pindexQuorum = WITH_LOCK(cs_main, return LookupBlockIndex(assetUnlockTx.getQuorumHash()));
if (!pindexQuorum) {
return {ValidationInvalidReason::CONSENSUS, "bad-assetunlock-quorum-hash"};
}

// Copy transaction except `quorumSig` field to calculate hash
CMutableTransaction tx_copy(tx);
auto payload_copy = CAssetUnlockPayload(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);
}

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());
}

uint16_t CAssetUnlockPayload::getVersion() const {
return nVersion;
}

uint64_t CAssetUnlockPayload::getIndex() const {
return index;
}

uint32_t CAssetUnlockPayload::getFee() const {
return fee;
}

uint32_t CAssetUnlockPayload::getRequestedHeight() const {
return requestedHeight;
}

const uint256& CAssetUnlockPayload::getQuorumHash() const {
return quorumHash;
}

const CBLSSignature& CAssetUnlockPayload::getQuorumSig() const {
return quorumSig;
}

int CAssetUnlockPayload::getHeightToExpiry() const {
int expiryHeight = 48;
return requestedHeight + expiryHeight;
}

0 comments on commit 8b64c0f

Please sign in to comment.