Skip to content

Commit

Permalink
Move Lelantus joinsplit data into DASH v3 payload (#1063)
Browse files Browse the repository at this point in the history
* Move lelantus data to CTransaction::vExtraPayload

* Fixed failing test

* Added unit test

* Adding Lelantus data to the RPC output

* Lelantus protocol fixes

* Raised maximum dash v3 payload size to 150KB

* Set lelantus payload HF block number

Co-authored-by: Andrey <andrey@firo.org>
  • Loading branch information
psolstice and a-bezrukov authored Aug 17, 2021
1 parent 04b6c72 commit 2cfa8e9
Show file tree
Hide file tree
Showing 30 changed files with 233 additions and 71 deletions.
4 changes: 2 additions & 2 deletions src/bip47/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,9 @@ bool CAccountReceiver::acceptMaskedPayload(std::vector<unsigned char> const & ma
return true;
}

bool CAccountReceiver::acceptMaskedPayload(std::vector<unsigned char> const & maskedPayload, CTxIn const & in)
bool CAccountReceiver::acceptMaskedPayload(std::vector<unsigned char> const & maskedPayload, CTransaction const & tx)
{
std::unique_ptr<lelantus::JoinSplit> jsplit = lelantus::ParseLelantusJoinSplit(in);
std::unique_ptr<lelantus::JoinSplit> jsplit = lelantus::ParseLelantusJoinSplit(tx);
if (!jsplit)
return false;
std::unique_ptr<CPaymentCode> pcode;
Expand Down
2 changes: 1 addition & 1 deletion src/bip47/account.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class CAccountReceiver : public CAccountBase

void acceptPcode(CPaymentCode const & theirPcode);
bool acceptMaskedPayload(std::vector<unsigned char> const & maskedPayload, COutPoint const & outpoint, CPubKey const & outpoinPubkey);
bool acceptMaskedPayload(std::vector<unsigned char> const & maskedPayload, CTxIn const & in);
bool acceptMaskedPayload(std::vector<unsigned char> const & maskedPayload, CTransaction const & tx);
CPaymentCode const & lastPcode() const;
bool findTheirPcode(CPaymentCode const & pcode) const;

Expand Down
1 change: 1 addition & 0 deletions src/bloom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ bool CBloomFilter::CheckSpecialTransactionMatchesAndUpdate(const CTransaction &t
case(TRANSACTION_COINBASE):
case(TRANSACTION_QUORUM_COMMITMENT):
case (TRANSACTION_SPORK):
case (TRANSACTION_LELANTUS):
// No aditional checks for this transaction types
return false;
}
Expand Down
9 changes: 9 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ class CMainParams : public CChainParams {

// Bip39
consensus.nMnemonicBlock = 222400;

// moving lelantus data to v3 payload
consensus.nLelantusV3PayloadStartBlock = 401580;
}
virtual bool SkipUndoForBlock(int nHeight) const
{
Expand Down Expand Up @@ -681,6 +684,9 @@ class CTestNetParams : public CChainParams {

// Bip39
consensus.nMnemonicBlock = 1;

// moving lelantus data to v3 payload
consensus.nLelantusV3PayloadStartBlock = 35000;
}
};

Expand Down Expand Up @@ -885,6 +891,9 @@ class CRegTestParams : public CChainParams {

// Bip39
consensus.nMnemonicBlock = 0;

// moving lelantus data to v3 payload
consensus.nLelantusV3PayloadStartBlock = 1000;
}

void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
Expand Down
2 changes: 1 addition & 1 deletion src/consensus/consensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static const unsigned int MAX_BLOCK_BASE_SIZE = 2000000;
/** The maximum allowed number of signature check operations in a block (network rule) */
static const int64_t MAX_BLOCK_SIGOPS_COST = 400000;
/** The maximum allowed size of version 3 extra payload */
static const unsigned int MAX_TX_EXTRA_PAYLOAD = 10000;
static const unsigned int MAX_TX_EXTRA_PAYLOAD = 150000;
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
static const int COINBASE_MATURITY = 100;

Expand Down
7 changes: 7 additions & 0 deletions src/evo/specialtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
return llmq::CheckLLMQCommitment(tx, pindexPrev, state);
case TRANSACTION_SPORK:
return CheckSporkTx(tx, pindexPrev, state);
case TRANSACTION_LELANTUS:
// lelantus transaction checks are done in other places
return true;
}

return state.DoS(10, false, REJECT_INVALID, "bad-tx-type-check");
Expand All @@ -65,6 +68,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
return true; // handled per block
case TRANSACTION_SPORK:
return true;
case TRANSACTION_LELANTUS:
return true;
}

return state.DoS(100, false, REJECT_INVALID, "bad-tx-type-proc");
Expand All @@ -88,6 +93,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
return true; // handled per block
case TRANSACTION_SPORK:
return true;
case TRANSACTION_LELANTUS:
return true;
}

return false;
Expand Down
3 changes: 3 additions & 0 deletions src/firo_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ static const int64_t DUST_HARD_LIMIT = 1000; // 0.00001 FIRO mininput
#define SIGMA_TO_LELANTUS_JOINSPLIT 41
#define LELANTUS_TX_VERSION_4_5 45
#define SIGMA_TO_LELANTUS_JOINSPLIT_FIXED 46
#define LELANTUS_TX_TPAYLOAD 47
#define SIGMA_TO_LELANTUS_TX_TPAYLOAD 48

#define ZEROCOIN_PUBLICKEY_TO_SERIALNUMBER "PUBLICKEY_TO_SERIALNUMBER"

#endif
2 changes: 1 addition & 1 deletion src/hdmint/tracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ bool CHDMintTracker::IsMempoolSpendOurs(const std::set<uint256>& setMempool, con
if (txin.IsLelantusJoinSplit()) {
std::unique_ptr<lelantus::JoinSplit> joinsplit;
try {
joinsplit = lelantus::ParseLelantusJoinSplit(txin);
joinsplit = lelantus::ParseLelantusJoinSplit(tx);
} catch (CBadTxIn &) {
return false;
} catch (std::ios_base::failure &) {
Expand Down
49 changes: 35 additions & 14 deletions src/lelantus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,21 +209,24 @@ void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElemen
}
}

std::unique_ptr<JoinSplit> ParseLelantusJoinSplit(const CTxIn& in)
std::unique_ptr<JoinSplit> ParseLelantusJoinSplit(const CTransaction &tx)
{
if (in.scriptSig.size() < 1) {
if (tx.vin.size() != 1 || tx.vin[0].scriptSig.size() < 1) {
throw CBadTxIn();
}

CDataStream serialized(
std::vector<unsigned char>(in.scriptSig.begin() + 1, in.scriptSig.end()),
SER_NETWORK,
PROTOCOL_VERSION
);
CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION);

std::unique_ptr<lelantus::JoinSplit> joinsplit(new lelantus::JoinSplit(lelantus::Params::get_default(), serialized));
if (tx.vin[0].scriptSig[0] == OP_LELANTUSJOINSPLIT) {
serialized.write((const char *)tx.vin[0].scriptSig.data()+1, tx.vin[0].scriptSig.size()-1);
}
else if (tx.vin[0].scriptSig[0] == OP_LELANTUSJOINSPLITPAYLOAD && tx.nVersion >= 3 && tx.nType == TRANSACTION_LELANTUS) {
serialized.write((const char *)tx.vExtraPayload.data(), tx.vExtraPayload.size());
}
else
throw CBadTxIn();

return joinsplit;
return std::make_unique<lelantus::JoinSplit>(lelantus::Params::get_default(), serialized);
}

bool CheckLelantusBlock(CValidationState &state, const CBlock& block) {
Expand Down Expand Up @@ -354,11 +357,26 @@ bool CheckLelantusJoinSplitTransaction(
"CheckLelantusJoinSplitTransaction: can't mix lelantus spend input with other tx types or have more than one spend");
}

int height = nHeight == INT_MAX ? chainActive.Height()+1 : nHeight;
if (!isVerifyDB) {
if (height >= params.nLelantusV3PayloadStartBlock) {
// data should be moved to v3 payload
if (tx.nVersion < 3 || tx.nType != TRANSACTION_LELANTUS)
return state.DoS(100, false, NSEQUENCE_INCORRECT,
"CheckLelantusJoinSplitTransaction: lelantus data should reside in transaction payload");
}
else {
if (tx.nVersion >= 3 && tx.nType != TRANSACTION_NORMAL)
return state.DoS(100, false, NSEQUENCE_INCORRECT,
"CheckLelantusJoinSplitTransaction: network hasn't yet switched over to lelantus payload data");
}
}

const CTxIn &txin = tx.vin[0];
std::unique_ptr<lelantus::JoinSplit> joinsplit;

try {
joinsplit = ParseLelantusJoinSplit(txin);
joinsplit = ParseLelantusJoinSplit(tx);
}
catch (CBadTxIn&) {
return state.DoS(100,
Expand All @@ -370,7 +388,9 @@ bool CheckLelantusJoinSplitTransaction(
int jSplitVersion = joinsplit->getVersion();

if (jSplitVersion < LELANTUS_TX_VERSION_4 ||
(!isVerifyDB && nHeight >= params.nLelantusFixesStartBlock && jSplitVersion != LELANTUS_TX_VERSION_4_5 && jSplitVersion != SIGMA_TO_LELANTUS_JOINSPLIT_FIXED)) {
(!isVerifyDB &&
((height >= params.nLelantusFixesStartBlock && height < params.nLelantusV3PayloadStartBlock && jSplitVersion != LELANTUS_TX_VERSION_4_5 && jSplitVersion != SIGMA_TO_LELANTUS_JOINSPLIT_FIXED) ||
(height >= params.nLelantusV3PayloadStartBlock && jSplitVersion != LELANTUS_TX_TPAYLOAD && jSplitVersion != SIGMA_TO_LELANTUS_TX_TPAYLOAD)))) {
return state.DoS(100,
false,
NSEQUENCE_INCORRECT,
Expand All @@ -382,6 +402,7 @@ bool CheckLelantusJoinSplitTransaction(
// Obtain the hash of the transaction sans the zerocoin part
CMutableTransaction txTemp = tx;
txTemp.vin[0].scriptSig.clear();
txTemp.vExtraPayload.clear();

txHashForMetadata = txTemp.GetHash();

Expand Down Expand Up @@ -666,7 +687,7 @@ bool CheckLelantusTransaction(
if(tx.IsLelantusJoinSplit()) {
CAmount nFees;
try {
nFees = lelantus::ParseLelantusJoinSplit(tx.vin[0])->getFee();
nFees = lelantus::ParseLelantusJoinSplit(tx)->getFee();
}
catch (CBadTxIn&) {
return state.DoS(0, false, REJECT_INVALID, "unable to parse joinsplit");
Expand Down Expand Up @@ -741,7 +762,7 @@ void RemoveLelantusJoinSplitReferencingBlock(CTxMemPool& pool, CBlockIndex* bloc
std::unique_ptr<lelantus::JoinSplit> joinsplit;

try {
joinsplit = ParseLelantusJoinSplit(txin);
joinsplit = ParseLelantusJoinSplit(tx);
}
catch (const std::ios_base::failure &) {
txn_to_remove.push_back(tx);
Expand Down Expand Up @@ -780,7 +801,7 @@ std::vector<Scalar> GetLelantusJoinSplitSerialNumbers(const CTransaction &tx, co
return std::vector<Scalar>();

try {
return ParseLelantusJoinSplit(txin)->getCoinSerialNumbers();
return ParseLelantusJoinSplit(tx)->getCoinSerialNumbers();
}
catch (const std::ios_base::failure &) {
return std::vector<Scalar>();
Expand Down
2 changes: 1 addition & 1 deletion src/lelantus.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElemen
void ParseLelantusJMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, std::vector<unsigned char>& encryptedValue);
void ParseLelantusJMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, std::vector<unsigned char>& encryptedValue, uint256& mintTag);
void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin);
std::unique_ptr<JoinSplit> ParseLelantusJoinSplit(const CTxIn& in);
std::unique_ptr<JoinSplit> ParseLelantusJoinSplit(const CTransaction& tx);

size_t GetSpendInputs(const CTransaction &tx, const CTxIn& in);
size_t GetSpendInputs(const CTransaction &tx);
Expand Down
2 changes: 2 additions & 0 deletions src/liblelantus/innerproduct_proof_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ void InnerProductProofGenerator::generate_proof(
compute_P(a, b, P_initial);
u_ *= x;
proof_out.c_ = c;
if (version_ >=3)
challengeGenerator->add(c);
P_ = (P_initial + u_ * c);
generate_proof_util(a, b, challengeGenerator, proof_out);
}
Expand Down
2 changes: 2 additions & 0 deletions src/liblelantus/innerproduct_proof_verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ bool InnerProductProofVerifier::verify(
auto itr_r = proof.R_.begin();
u_ *= x;
P_ += u_ * proof.c_;
if (version_ >= 3)
challengeGenerator->add(proof.c_);
return verify_util(proof, itr_l, itr_r, challengeGenerator);
}

Expand Down
2 changes: 1 addition & 1 deletion src/liblelantus/joinsplit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ bool JoinSplit::HasValidSerials() const {
}

bool JoinSplit::isSigmaToLelantus() const {
return version == SIGMA_TO_LELANTUS_JOINSPLIT || version == SIGMA_TO_LELANTUS_JOINSPLIT_FIXED;
return version == SIGMA_TO_LELANTUS_JOINSPLIT || version == SIGMA_TO_LELANTUS_JOINSPLIT_FIXED || version == SIGMA_TO_LELANTUS_TX_TPAYLOAD;
}

} //namespace lelantus
3 changes: 3 additions & 0 deletions src/liblelantus/range_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ void RangeProver::batch_proof(
}

int inner_product_version = version >= LELANTUS_TX_VERSION_4_5 ? 2 : 1;
if (version >= LELANTUS_TX_TPAYLOAD)
inner_product_version = 3;

InnerProductProofGenerator InnerProductProofGenerator(g_, h_prime, g, inner_product_version);
//t^ is calculated inside inner product proof generation with name c
Scalar x_u;
Expand Down
3 changes: 3 additions & 0 deletions src/liblelantus/range_verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ bool RangeVerifier::verify_batch(const std::vector<GroupElement>& V, const std::
challengeGenerator->add(pre);
}

if (version >= LELANTUS_TX_TPAYLOAD)
challengeGenerator->add(innerProductProof.c_);

for (int i = 0; i < log_n; ++i)
{
std::vector<GroupElement> group_elements_i = {innerProductProof.L_[i], innerProductProof.R_[i]};
Expand Down
5 changes: 4 additions & 1 deletion src/primitives/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ bool CTxIn::IsSigmaSpend() const

bool CTxIn::IsLelantusJoinSplit() const
{
return (prevout.IsNull() && scriptSig.size() > 0 && (scriptSig[0] == OP_LELANTUSJOINSPLIT) );
return (prevout.IsNull() && scriptSig.size() > 0 && (scriptSig[0] == OP_LELANTUSJOINSPLIT || scriptSig[0] == OP_LELANTUSJOINSPLITPAYLOAD) );
}

bool CTxIn::IsZerocoinRemint() const
Expand Down Expand Up @@ -170,6 +170,9 @@ bool CTransaction::IsSigmaSpend() const

bool CTransaction::IsLelantusJoinSplit() const
{
if (nVersion >= 3 && nType == TRANSACTION_LELANTUS)
return true;

for (const CTxIn &txin: vin) {
if (txin.IsLelantusJoinSplit())
return true;
Expand Down
4 changes: 3 additions & 1 deletion src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum {
TRANSACTION_COINBASE = 5,
TRANSACTION_QUORUM_COMMITMENT = 6,
TRANSACTION_SPORK = 7,
TRANSACTION_LELANTUS = 8
};

/** An outpoint - a combination of a transaction hash and an index n into its vout */
Expand Down Expand Up @@ -466,7 +467,8 @@ class CTransaction
return (vin.size() == 1 && vin[0].prevout.IsNull() && (vin[0].scriptSig.size() == 0
|| (vin[0].scriptSig[0] != OP_ZEROCOINSPEND
&& vin[0].scriptSig[0] != OP_ZEROCOINTOSIGMAREMINT
&& vin[0].scriptSig[0] != OP_LELANTUSJOINSPLIT)));
&& vin[0].scriptSig[0] != OP_LELANTUSJOINSPLIT
&& vin[0].scriptSig[0] != OP_LELANTUSJOINSPLITPAYLOAD)));
}

friend bool operator==(const CTransaction& a, const CTransaction& b)
Expand Down
10 changes: 5 additions & 5 deletions src/qt/transactiondesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
isminetype mine = wallet->IsMine(txin);
isminetype mine = wallet->IsMine(txin, *wtx.tx);
if(fAllFromMe > mine) fAllFromMe = mine;
}

Expand Down Expand Up @@ -233,8 +233,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Mixed debit transaction
//
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
if (wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
if (wallet->IsMine(txin, *wtx.tx))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, *wtx.tx, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if (wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
Expand Down Expand Up @@ -309,8 +309,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
{
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
if(wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
if(wallet->IsMine(txin, *wtx.tx))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, *wtx.tx, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
if(wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
Expand Down
8 changes: 4 additions & 4 deletions src/qt/transactionrecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,22 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *

bool isAllSigmaSpendFromMe = false;
for (const auto& vin : wtx.tx->vin) {
isAllSigmaSpendFromMe = (wallet->IsMine(vin) & ISMINE_SPENDABLE) && vin.IsSigmaSpend();
isAllSigmaSpendFromMe = (wallet->IsMine(vin, *wtx.tx) & ISMINE_SPENDABLE) && vin.IsSigmaSpend();
if (!isAllSigmaSpendFromMe)
break;
}

bool isAllJoinSplitFromMe = false;
for (const auto& vin : wtx.tx->vin) {
isAllJoinSplitFromMe = (wallet->IsMine(vin) & ISMINE_SPENDABLE) && vin.IsLelantusJoinSplit();
isAllJoinSplitFromMe = (wallet->IsMine(vin, *wtx.tx) & ISMINE_SPENDABLE) && vin.IsLelantusJoinSplit();
if (!isAllJoinSplitFromMe)
break;
}

if (wtx.tx->IsZerocoinSpend() || isAllSigmaSpendFromMe || isAllJoinSplitFromMe) {
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
if (isAllJoinSplitFromMe && wtx.tx->vin.size() > 0) {
nTxFee = lelantus::ParseLelantusJoinSplit(wtx.tx->vin[0])->getFee();
nTxFee = lelantus::ParseLelantusJoinSplit(*wtx.tx)->getFee();
}

bool first = true;
Expand Down Expand Up @@ -201,7 +201,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
isminetype mine = wallet->IsMine(txin);
isminetype mine = wallet->IsMine(txin, *wtx.tx);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
}
Expand Down
2 changes: 1 addition & 1 deletion src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins,
{
COutput cout = out;

while (wallet->IsChange(cout.tx->GetHash(), cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0]))
while (wallet->IsChange(cout.tx->GetHash(), cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0], *cout.tx->tx))
{
if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break;
cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0, true, true);
Expand Down
Loading

0 comments on commit 2cfa8e9

Please sign in to comment.