Rename NullifierType to ShieldedType.
This commit is contained in:
@@ -43,7 +43,7 @@ bool CCoins::Spend(uint32_t nPos)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; }
|
bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; }
|
||||||
bool CCoinsView::GetNullifier(const uint256 &nullifier, NullifierType type) const { return false; }
|
bool CCoinsView::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; }
|
||||||
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
|
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
|
||||||
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
|
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
|
||||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||||
@@ -60,7 +60,7 @@ bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
|
|||||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||||
|
|
||||||
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
|
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
|
||||||
bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, NullifierType type) const { return base->GetNullifier(nullifier, type); }
|
bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return base->GetNullifier(nullifier, type); }
|
||||||
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
|
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
|
||||||
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
|
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
|
||||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||||
@@ -133,13 +133,13 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, NullifierType type) const {
|
bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, ShieldedType type) const {
|
||||||
CNullifiersMap* cacheToUse;
|
CNullifiersMap* cacheToUse;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SPROUT_NULLIFIER:
|
case SPROUT:
|
||||||
cacheToUse = &cacheSproutNullifiers;
|
cacheToUse = &cacheSproutNullifiers;
|
||||||
break;
|
break;
|
||||||
case SAPLING_NULLIFIER:
|
case SAPLING:
|
||||||
cacheToUse = &cacheSaplingNullifiers;
|
cacheToUse = &cacheSaplingNullifiers;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -430,7 +430,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
|||||||
{
|
{
|
||||||
BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers)
|
BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers)
|
||||||
{
|
{
|
||||||
if (GetNullifier(nullifier, SPROUT_NULLIFIER)) {
|
if (GetNullifier(nullifier, SPROUT)) {
|
||||||
// If the nullifier is set, this transaction
|
// If the nullifier is set, this transaction
|
||||||
// double-spends!
|
// double-spends!
|
||||||
return false;
|
return false;
|
||||||
@@ -454,7 +454,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
if (GetNullifier(spendDescription.nullifier, SAPLING_NULLIFIER)) // Prevent double spends
|
if (GetNullifier(spendDescription.nullifier, SAPLING)) // Prevent double spends
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
src/coins.h
12
src/coins.h
@@ -298,10 +298,10 @@ struct CNullifiersCacheEntry
|
|||||||
CNullifiersCacheEntry() : entered(false), flags(0) {}
|
CNullifiersCacheEntry() : entered(false), flags(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum NullifierType
|
enum ShieldedType
|
||||||
{
|
{
|
||||||
SPROUT_NULLIFIER,
|
SPROUT,
|
||||||
SAPLING_NULLIFIER,
|
SAPLING,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap;
|
typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap;
|
||||||
@@ -330,7 +330,7 @@ public:
|
|||||||
virtual bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
virtual bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||||
|
|
||||||
//! Determine whether a nullifier is spent or not
|
//! Determine whether a nullifier is spent or not
|
||||||
virtual bool GetNullifier(const uint256 &nullifier, NullifierType type) const;
|
virtual bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||||
|
|
||||||
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
|
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
|
||||||
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||||
@@ -371,7 +371,7 @@ protected:
|
|||||||
public:
|
public:
|
||||||
CCoinsViewBacked(CCoinsView *viewIn);
|
CCoinsViewBacked(CCoinsView *viewIn);
|
||||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||||
bool GetNullifier(const uint256 &nullifier, NullifierType type) const;
|
bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||||
bool HaveCoins(const uint256 &txid) const;
|
bool HaveCoins(const uint256 &txid) const;
|
||||||
uint256 GetBestBlock() const;
|
uint256 GetBestBlock() const;
|
||||||
@@ -437,7 +437,7 @@ public:
|
|||||||
|
|
||||||
// Standard CCoinsView methods
|
// Standard CCoinsView methods
|
||||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||||
bool GetNullifier(const uint256 &nullifier, NullifierType type) const;
|
bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||||
bool HaveCoins(const uint256 &txid) const;
|
bool HaveCoins(const uint256 &txid) const;
|
||||||
uint256 GetBestBlock() const;
|
uint256 GetBestBlock() const;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNullifier(const uint256 &nf, NullifierType type) const {
|
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNullifier(const uint256 &nf, NullifierType type) const {
|
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1305,13 +1305,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
}
|
}
|
||||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||||
if (pool.nullifierExists(nf, SPROUT_NULLIFIER)) {
|
if (pool.nullifierExists(nf, SPROUT)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
if (pool.nullifierExists(spendDescription.nullifier, SAPLING_NULLIFIER)) {
|
if (pool.nullifierExists(spendDescription.nullifier, SAPLING)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,14 +52,14 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNullifier(const uint256 &nf, NullifierType type) const
|
bool GetNullifier(const uint256 &nf, ShieldedType type) const
|
||||||
{
|
{
|
||||||
const std::map<uint256, bool>* mapToUse;
|
const std::map<uint256, bool>* mapToUse;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SPROUT_NULLIFIER:
|
case SPROUT:
|
||||||
mapToUse = &mapSproutNullifiers_;
|
mapToUse = &mapSproutNullifiers_;
|
||||||
break;
|
break;
|
||||||
case SAPLING_NULLIFIER:
|
case SAPLING:
|
||||||
mapToUse = &mapSaplingNullifiers_;
|
mapToUse = &mapSaplingNullifiers_;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -215,11 +215,11 @@ BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
|
|||||||
|
|
||||||
void checkNullifierCache(const CCoinsViewCacheTest &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) {
|
void checkNullifierCache(const CCoinsViewCacheTest &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) {
|
||||||
// Make sure the nullifiers have not gotten mixed up
|
// Make sure the nullifiers have not gotten mixed up
|
||||||
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING_NULLIFIER));
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING));
|
||||||
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT_NULLIFIER));
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT));
|
||||||
// Check if the nullifiers either are or are not in the cache
|
// Check if the nullifiers either are or are not in the cache
|
||||||
bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT_NULLIFIER);
|
bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT);
|
||||||
bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING_NULLIFIER);
|
bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING);
|
||||||
BOOST_CHECK(containsSproutNullifier == shouldBeInCache);
|
BOOST_CHECK(containsSproutNullifier == shouldBeInCache);
|
||||||
BOOST_CHECK(containsSaplingNullifier == shouldBeInCache);
|
BOOST_CHECK(containsSaplingNullifier == shouldBeInCache);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,14 +52,14 @@ bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree)
|
|||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewDB::GetNullifier(const uint256 &nf, NullifierType type) const {
|
bool CCoinsViewDB::GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||||
bool spent = false;
|
bool spent = false;
|
||||||
char dbChar;
|
char dbChar;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SPROUT_NULLIFIER:
|
case SPROUT:
|
||||||
dbChar = DB_NULLIFIER;
|
dbChar = DB_NULLIFIER;
|
||||||
break;
|
break;
|
||||||
case SAPLING_NULLIFIER:
|
case SAPLING:
|
||||||
dbChar = DB_SAPLING_NULLIFIER;
|
dbChar = DB_SAPLING_NULLIFIER;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||||
|
|
||||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||||
bool GetNullifier(const uint256 &nf, NullifierType type) const;
|
bool GetNullifier(const uint256 &nf, ShieldedType type) const;
|
||||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||||
bool HaveCoins(const uint256 &txid) const;
|
bool HaveCoins(const uint256 &txid) const;
|
||||||
uint256 GetBestBlock() const;
|
uint256 GetBestBlock() const;
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||||||
|
|
||||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||||
assert(!pcoins->GetNullifier(nf, SPROUT_NULLIFIER));
|
assert(!pcoins->GetNullifier(nf, SPROUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
ZCIncrementalMerkleTree tree;
|
ZCIncrementalMerkleTree tree;
|
||||||
@@ -413,7 +413,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||||
}
|
}
|
||||||
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
for (const SpendDescription &spendDescription : tx.vShieldedSpend) {
|
||||||
assert(!pcoins->GetNullifier(spendDescription.nullifier, SAPLING_NULLIFIER));
|
assert(!pcoins->GetNullifier(spendDescription.nullifier, SAPLING));
|
||||||
}
|
}
|
||||||
if (fDependsWait)
|
if (fDependsWait)
|
||||||
waitingOnDependants.push_back(&(*it));
|
waitingOnDependants.push_back(&(*it));
|
||||||
@@ -452,21 +452,21 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||||||
assert(it->first == it->second.ptx->vin[it->second.n].prevout);
|
assert(it->first == it->second.ptx->vin[it->second.n].prevout);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNullifiers(SPROUT_NULLIFIER);
|
checkNullifiers(SPROUT);
|
||||||
checkNullifiers(SAPLING_NULLIFIER);
|
checkNullifiers(SAPLING);
|
||||||
|
|
||||||
assert(totalTxSize == checkTotal);
|
assert(totalTxSize == checkTotal);
|
||||||
assert(innerUsage == cachedInnerUsage);
|
assert(innerUsage == cachedInnerUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::checkNullifiers(NullifierType type) const
|
void CTxMemPool::checkNullifiers(ShieldedType type) const
|
||||||
{
|
{
|
||||||
const std::map<uint256, const CTransaction*>* mapToUse;
|
const std::map<uint256, const CTransaction*>* mapToUse;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SPROUT_NULLIFIER:
|
case SPROUT:
|
||||||
mapToUse = &mapSproutNullifiers;
|
mapToUse = &mapSproutNullifiers;
|
||||||
break;
|
break;
|
||||||
case SAPLING_NULLIFIER:
|
case SAPLING:
|
||||||
mapToUse = &mapSaplingNullifiers;
|
mapToUse = &mapSaplingNullifiers;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -582,12 +582,12 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CTxMemPool::nullifierExists(const uint256& nullifier, NullifierType type) const
|
bool CTxMemPool::nullifierExists(const uint256& nullifier, ShieldedType type) const
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SPROUT_NULLIFIER:
|
case SPROUT:
|
||||||
return mapSproutNullifiers.count(nullifier);
|
return mapSproutNullifiers.count(nullifier);
|
||||||
case SAPLING_NULLIFIER:
|
case SAPLING:
|
||||||
return mapSaplingNullifiers.count(nullifier);
|
return mapSaplingNullifiers.count(nullifier);
|
||||||
default:
|
default:
|
||||||
throw runtime_error("Unknown nullifier type");
|
throw runtime_error("Unknown nullifier type");
|
||||||
@@ -596,7 +596,7 @@ bool CTxMemPool::nullifierExists(const uint256& nullifier, NullifierType type) c
|
|||||||
|
|
||||||
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
||||||
|
|
||||||
bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, NullifierType type) const
|
bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, ShieldedType type) const
|
||||||
{
|
{
|
||||||
return mempool.nullifierExists(nf, type) || base->GetNullifier(nf, type);
|
return mempool.nullifierExists(nf, type) || base->GetNullifier(nf, type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ private:
|
|||||||
std::map<uint256, const CTransaction*> mapSproutNullifiers;
|
std::map<uint256, const CTransaction*> mapSproutNullifiers;
|
||||||
std::map<uint256, const CTransaction*> mapSaplingNullifiers;
|
std::map<uint256, const CTransaction*> mapSaplingNullifiers;
|
||||||
|
|
||||||
void checkNullifiers(NullifierType type) const;
|
void checkNullifiers(ShieldedType type) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef boost::multi_index_container<
|
typedef boost::multi_index_container<
|
||||||
@@ -192,7 +192,7 @@ public:
|
|||||||
void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta);
|
void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta);
|
||||||
void ClearPrioritisation(const uint256 hash);
|
void ClearPrioritisation(const uint256 hash);
|
||||||
|
|
||||||
bool nullifierExists(const uint256& nullifier, NullifierType type) const;
|
bool nullifierExists(const uint256& nullifier, ShieldedType type) const;
|
||||||
|
|
||||||
unsigned long size()
|
unsigned long size()
|
||||||
{
|
{
|
||||||
@@ -243,7 +243,7 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn);
|
CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn);
|
||||||
bool GetNullifier(const uint256 &txid, NullifierType type) const;
|
bool GetNullifier(const uint256 &txid, ShieldedType type) const;
|
||||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||||
bool HaveCoins(const uint256 &txid) const;
|
bool HaveCoins(const uint256 &txid) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNullifier(const uint256 &nf, NullifierType type) const {
|
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user